This commit is contained in:
Kadir Sekha 2017-10-16 18:05:09 +09:00
commit a0261a6c9f
146 changed files with 16800 additions and 5578 deletions

View file

@ -1,13 +1,3 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# http://editorconfig.org
## Plugins
# Atom (https://github.com/sindresorhus/atom-editorconfig)
# Vim (https://github.com/editorconfig/editorconfig-vim)
# Sublime (https://github.com/sindresorhus/editorconfig-sublime)
;
root = true
[*]
@ -27,4 +17,3 @@ indent_style = tab
[**.html]
max_char = 78
brace_style = expand

3
.gitignore vendored
View file

@ -3,6 +3,9 @@ i18n/po/*.mo
i18n/crowdin_api_key.txt
src/js/translations.js
package-lock.json
# cordova
plugins
platforms

View file

@ -267,7 +267,7 @@ module.exports = function(grunt) {
grunt.registerTask('desktop', ['prod', 'nwjs', 'copy:linux', 'compress:linux']);
grunt.registerTask('osx', ['prod', 'nwjs', 'exec:macos', 'exec:osxsign']);
grunt.registerTask('osx-debug', ['default', 'nwjs']);
grunt.registerTask('chrome', ['exec:chrome']);
grunt.registerTask('chrome', ['default','exec:chrome']);
grunt.registerTask('wp', ['prod', 'exec:wp']);
grunt.registerTask('wp-copy', ['default', 'exec:wpcopy']);
grunt.registerTask('wp-init', ['default', 'exec:wpinit']);

View file

@ -13,6 +13,10 @@ bwcModule.provider("bwcService", function() {
return Client.Bitcore;
};
service.getBitcoreCash = function() {
return Client.BitcoreCash;
};
service.getErrors = function() {
return Client.errors;
};

View file

@ -23,8 +23,8 @@
"windowsAppId": "2d1002d7-ee34-4f60-bd29-0c871ba0c195",
"pushSenderId": "1036948132229",
"description": "Secure Bitcoin Wallet",
"version": "3.7.1",
"androidVersion": "371000",
"version": "3.8.2",
"androidVersion": "382001",
"_extraCSS": null,
"_enabledExtensions": {
"coinbase": true,

View file

@ -66,6 +66,7 @@
<plugin name="cordova-plugin-customurlscheme" spec="https://github.com/cmgustavo/Custom-URL-scheme.git">
<variable name="URL_SCHEME" value="bitcoin" />
<variable name="SECOND_URL_SCHEME" value="*APPURI*" />
<variable name="THIRD_URL_SCHEME" value="bitcoincash" />
</plugin>
<plugin name="cordova-custom-config" spec="~3.0.5" />
<plugin name="cordova-plugin-queries-schemes" spec="~0.1.5" />

View file

@ -23,8 +23,8 @@
"windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c",
"pushSenderId": "1036948132229",
"description": "A Secure Bitcoin Wallet",
"version": "3.7.1",
"androidVersion": "371000",
"version": "3.8.2",
"androidVersion": "382001",
"_extraCSS": null,
"_enabledExtensions": {
"coinbase": true,

View file

@ -1,7 +1,7 @@
{
"name": "*PACKAGENAME*",
"description": "*DESCRIPTION*",
"author": "Bitcoin.com",
"author": "BitPay",
"version": "*VERSION*",
"keywords": [
"bitcoin",
@ -54,9 +54,9 @@
"angular": "1.4.6",
"angular-mocks": "1.4.10",
"bezier-easing": "^2.0.3",
"bhttp": "^1.2.1",
"bhttp": "1.2.1",
"bitauth": "^0.2.1",
"bitcore-wallet-client": "5.3.0",
"bitcore-wallet-client": "6.2.1",
"bower": "^1.7.9",
"cordova-android": "5.1.1",
"cordova-custom-config": "^3.0.5",
@ -107,10 +107,9 @@
"run:android": "cordova run android --device",
"run:android-release": "cordova run android --device --release",
"log:android": "adb logcat | grep chromium",
"sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../copay.keystore -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && ../android-sdk-macosx/build-tools/25.0.3/zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk",
"sign:android": "rm -f platforms/android/build/outputs/apk/android-release-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../copay.keystore -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk copay_play && $ANDROID_HOME/build-tools/26.0.1/zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-signed-aligned.apk",
"apply:copay": "npm i fs-extra && cd app-template && node apply.js copay && npm i && cordova prepare",
"apply:bitpay": "npm i fs-extra && cd app-template && node apply.js bitpay && npm i && cordova prepare",
"apply:bitcoincom": "npm i fs-extra && cd app-template && node apply.js bitcoincom && npm i && cordova prepare",
"test": "echo \"no package tests configured\"",
"clean": "trash platforms && trash plugins && cordova prepare",
"unstage-package": "git reset package.json",
@ -119,9 +118,9 @@
"devDependencies": {
"cordova": "^6.3.1",
"grunt": "^1.0.1",
"ionic": "^2.1.0",
"ionic": "^3.6.0",
"trash-cli": "^1.4.0",
"lodash": "^4.3.0",
"lodash": "^4.17.4",
"pre-commit": "^1.1.3"
},
"pre-commit": "unstage-package"

View file

@ -15,7 +15,7 @@
"moment": "2.10.3",
"ng-lodash": "0.2.3",
"qrcode-decoder-js": "*",
"trezor-connect": "*",
"trezor-connect": "https://github.com/trezor/connect.git#c1a00f9f0f4a13239ba13ddd589c6ca61f2e7876",
"ng-csv": "~0.3.6",
"ionic-toast": "^0.4.1",
"angular-clipboard": "^1.4.2",

23
i18n/docs/appstore_nl.txt Normal file
View file

@ -0,0 +1,23 @@
Beveilig bitcoin op je eigen voorwaarden met een open source, meervoudige ondertekening-portemonnee van BitPay.
Copay gebruikers kunnen individueel saldo beheren of veilig financiën delen met andere gebruikers door middel van portemonnees met meervoudige ondertekening, dit voorkomt ongeautoriseerde betalingen doordat meerdere goedkeuringen nodig zijn. Hier zijn een aantal manieren waarop Copay gebruikt kan worden met anderen:
Om te sparen voor vakanties of gezamenlijke aankopen met vrienden
Voor het bijhouden van de familieuitgaven en zakgeld
Voor het beheer van het saldo en de uitgaven van een bedrijf, club of organisatie
We hebben de volgende functies ingebouwd in deze versie van Copay voor een bitcoin portemonnee die niet inlevert op beveiliging of toegankelijkheid:
Het aanmaken en beheren van meerdere portemonnees binnen de app
Intuïtieve meervoudige-ondertekening beveiliging voor persoonlijke of gedeelde portemonnees
Eenvoudige bestedingsvoorstellen voor gedeelde portemonnees en groepsbetalingen
Hiërarchisch deterministische (HD) adres generatie en portemonnee backups
Apparaat gebaseerde beveiliging: alle privé sleutels worden lokaal opgeslagen, niet in de cloud
Ondersteuning voor Bitcoin testnet portemonnees
Gesynchroniseerde toegang vanaf alle grote mobiele en desktop platformen
Ondersteuning voor betalings protocol (BIP70-BIP73): gemakkelijk te herkennen betalingsverzoeken en verifieerbaar veilige bitcoin betalingen
Ondersteuning voor prijsweergave in 150+ valuta's en eenheid denominatie in BTC of bits
Email meldingen voor betalingen en overdrachten
Aanpasbare portemonnee namen en achtergrond kleuren
10 ondersteunde talen (EN, CZ, FR, DE, IT, ES, JA, PL, RU, NL)
Copay is gratis open source software draaiend op niet-merkgebonden servers, dus het is niet nodig te vertrouwen op enig bedrijf voor blijvende ondersteuning. Iedereen kan de broncode van Copay inzien of er aan bijdragen via GitHub (https://github.com/bitpay/copay).

View file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

3630
i18n/po/nl.po Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@ angular.module('copayApp.controllers').controller('addressbookAddController', fu
$timeout(function() {
var form = addressbookForm;
if (data && form) {
data = data.replace('bitcoin:', '');
data = data.replace(/^bitcoin(cash)?:/, '');
form.address.$setViewValue(data);
form.address.$isValid = true;
form.address.$render();

View file

@ -1,11 +1,23 @@
'use strict';
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, $stateParams, lodash, addressbookService, popupService, $ionicHistory, platformInfo, gettextCatalog) {
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, lodash, addressbookService, popupService, $ionicHistory, platformInfo, gettextCatalog, bitcoreCash) {
$scope.isChromeApp = platformInfo.isChromeApp;
$scope.addressbookEntry = {};
$scope.addressbookEntry.name = $stateParams.name;
$scope.addressbookEntry.email = $stateParams.email;
$scope.addressbookEntry.address = $stateParams.address;
var coin;
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.addressbookEntry = {};
$scope.addressbookEntry.name = data.stateParams.name;
$scope.addressbookEntry.email = data.stateParams.email;
$scope.addressbookEntry.address = data.stateParams.address;
var cashAddress = bitcoreCash.Address.isValid($scope.addressbookEntry.address, 'livenet');
if (cashAddress) {
coin = 'bch';
} else {
coin = 'btc';
}
});
$scope.sendTo = function() {
$ionicHistory.removeBackView();
@ -14,7 +26,8 @@ angular.module('copayApp.controllers').controller('addressbookViewController', f
$state.transitionTo('tabs.send.amount', {
toAddress: $scope.addressbookEntry.address,
toName: $scope.addressbookEntry.name,
toEmail: $scope.addressbookEntry.email
toEmail: $scope.addressbookEntry.email,
coin: coin
});
}, 100);
};

View file

@ -1,13 +1,8 @@
'use strict';
angular.module('copayApp.controllers').controller('addressesController', function($scope, $log, $stateParams, $state, $timeout, $ionicHistory, $ionicScrollDelegate, configService, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, bwcError, platformInfo, appConfigService, txFormatService, feeService) {
angular.module('copayApp.controllers').controller('addressesController', function($scope, $log, $stateParams, $state, $timeout, $ionicHistory, $ionicScrollDelegate, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, bwcError, platformInfo, appConfigService, txFormatService, feeService) {
var UNUSED_ADDRESS_LIMIT = 5;
var BALANCE_ADDRESS_LIMIT = 5;
var config = configService.getSync().wallet.settings;
var unitName = config.unitName;
var unitToSatoshi = config.unitToSatoshi;
var satToUnit = 1 / unitToSatoshi;
var unitDecimals = config.unitDecimals;
var withBalance, cachedWallet;
$scope.isCordova = platformInfo.isCordova;
@ -55,7 +50,7 @@ angular.module('copayApp.controllers').controller('addressesController', functio
$scope.latestWithBalance = lodash.slice(withBalance, 0, BALANCE_ADDRESS_LIMIT);
lodash.each(withBalance, function(a) {
a.balanceStr = txFormatService.formatAmount(a.amount);
a.balanceStr = txFormatService.formatAmountStr($scope.wallet.coin, a.amount);
});
$scope.viewAll = {
@ -75,11 +70,11 @@ angular.module('copayApp.controllers').controller('addressesController', functio
feeService.getFeeLevels(function(err, levels){
feeService.getFeeLevels($scope.wallet.coin, function(err, levels){
walletService.getLowUtxos($scope.wallet, levels, function(err, resp) {
if (err) return;
if (resp.allUtxos && resp.allUtxos.length) {
if (resp && resp.allUtxos && resp.allUtxos.length) {
var allSum = lodash.sum(resp.allUtxos || 0, 'satoshis');
@ -88,9 +83,9 @@ angular.module('copayApp.controllers').controller('addressesController', functio
$scope.lowWarning = resp.warning;
$scope.lowUtxosNb = resp.lowUtxos.length;
$scope.allUtxosNb = resp.allUtxos.length;
$scope.lowUtxosSum = txFormatService.formatAmountStr(lodash.sum(resp.lowUtxos || 0, 'satoshis'));
$scope.allUtxosSum = txFormatService.formatAmountStr(allSum);
$scope.minFee = txFormatService.formatAmountStr(resp.minFee || 0);
$scope.lowUtxosSum = txFormatService.formatAmountStr($scope.wallet.coin, lodash.sum(resp.lowUtxos || 0, 'satoshis'));
$scope.allUtxosSum = txFormatService.formatAmountStr($scope.wallet.coin, allSum);
$scope.minFee = txFormatService.formatAmountStr($scope.wallet.coin, resp.minFee || 0);
$scope.minFeePer = per.toFixed(2) + '%';

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $log, configService, platformInfo) {
angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $log, configService, platformInfo, externalLinkService, gettextCatalog) {
var updateConfig = function() {
var config = configService.getSync();
@ -14,6 +14,7 @@ angular.module('copayApp.controllers').controller('advancedSettingsController',
$scope.hideNextSteps = {
value: config.hideNextSteps.enabled
};
};
$scope.spendUnconfirmedChange = function() {

View file

@ -9,6 +9,14 @@ angular.module('copayApp.controllers').controller('amountController', function($
var SMALL_FONT_SIZE_LIMIT = 10;
var LENGTH_EXPRESSION_LIMIT = 19;
var isNW = platformInfo.isNW;
var unitIndex = 0;
var altUnitIndex = 0;
var availableUnits = [];
var fiatCode;
var fixedUnit;
$scope.isChromeApp = platformInfo.isChromeApp;
$scope.$on('$ionicView.leave', function() {
@ -16,19 +24,100 @@ angular.module('copayApp.controllers').controller('amountController', function($
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var config = configService.getSync().wallet.settings;
function setAvailableUnits() {
availableUnits = [];
var hasBTCWallets = profileService.getWallets({
coin: 'btc'
}).length;
if (hasBTCWallets) {
availableUnits.push({
name: 'Bitcoin',
id: 'btc',
shortName: 'BTC',
});
}
var hasBCHWallets = profileService.getWallets({
coin: 'bch'
}).length;
if (hasBCHWallets) {
availableUnits.push({
name: 'Bitcoin Cash',
id: 'bch',
shortName: 'BCH',
});
};
unitIndex = 0;
if (data.stateParams.coin) {
var coins = data.stateParams.coin.split(',');
var newAvailableUnits = [];
lodash.each(coins, function(c) {
var coin = lodash.find(availableUnits, {
id: c
});
if (!coin) {
$log.warn('Could not find desired coin:' + data.stateParams.coin)
} else {
newAvailableUnits.push(coin);
}
});
if (newAvailableUnits.length > 0) {
availableUnits = newAvailableUnits;
}
}
// currency have preference
var fiatName;
if (data.stateParams.currency) {
fiatCode = data.stateParams.currency;
altUnitIndex = unitIndex
unitIndex = availableUnits.length;
} else {
fiatCode = config.alternativeIsoCode || 'USD';
fiatName = config.alternanativeName || fiatCode;
altUnitIndex = availableUnits.length;
}
availableUnits.push({
name: fiatName || fiatCode,
// TODO
id: fiatCode,
shortName: fiatCode,
isFiat: true,
});
if (data.stateParams.fixedUnit) {
fixedUnit = true;
}
};
// Go to...
_id = data.stateParams.id; // Optional (BitPay Card ID or Wallet ID)
$scope.nextStep = data.stateParams.nextStep;
$scope.currency = data.stateParams.currency;
$scope.forceCurrency = data.stateParams.forceCurrency;
$scope.showMenu = $ionicHistory.backView() && ($ionicHistory.backView().stateName == 'tabs.send' ||
$ionicHistory.backView().stateName == 'tabs.bitpayCard');
setAvailableUnits();
updateUnitUI();
$scope.showMenu = $ionicHistory.backView() && ($ionicHistory.backView().stateName == 'tabs.send' || $ionicHistory.backView().stateName == 'tabs.bitpayCard');
$scope.recipientType = data.stateParams.recipientType || null;
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
$scope.toEmail = data.stateParams.toEmail;
$scope.showAlternativeAmount = !!$scope.nextStep;
$scope.toColor = data.stateParams.toColor;
$scope.showSendMax = false;
@ -52,23 +141,13 @@ angular.module('copayApp.controllers').controller('amountController', function($
} else if (e.key.match(reOp)) {
$scope.pushOperator(e.key);
} else if (e.keyCode === 86) {
if (e.ctrlKey || e.metaKey)
processClipboard();
} else if (e.keyCode === 13)
$scope.finish();
if (e.ctrlKey || e.metaKey) processClipboard();
} else if (e.keyCode === 13) $scope.finish();
$timeout(function() {
$scope.$apply();
});
});
var config = configService.getSync().wallet.settings;
$scope.unitName = config.unitName;
if (data.stateParams.currency) {
$scope.alternativeIsoCode = data.stateParams.currency;
} else {
$scope.alternativeIsoCode = config.alternativeIsoCode || 'USD';
}
$scope.specificAmount = $scope.specificAlternativeAmount = '';
$scope.isCordova = platformInfo.isCordova;
unitToSatoshi = config.unitToSatoshi;
@ -114,16 +193,59 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.finish();
};
$scope.toggleAlternative = function() {
if ($scope.forceCurrency) return;
$scope.showAlternativeAmount = !$scope.showAlternativeAmount;
$scope.toggleAlternative = function() {
if ($scope.amount && isExpression($scope.amount)) {
var amount = evaluate(format($scope.amount));
$scope.globalResult = '= ' + processResult(amount);
}
};
function updateUnitUI() {
$scope.unit = availableUnits[unitIndex].shortName;
$scope.alternativeUnit = availableUnits[altUnitIndex].shortName;
processAmount();
$log.debug('Update unit coin @amount unit:' + $scope.unit + " alternativeUnit:" + $scope.alternativeUnit);
};
$scope.changeUnit = function() {
if (fixedUnit) return;
unitIndex++;
if (unitIndex >= availableUnits.length) unitIndex = 0;
if (availableUnits[unitIndex].isFiat) {
// Always return to BTC... TODO?
altUnitIndex = 0;
} else {
altUnitIndex = lodash.findIndex(availableUnits, {
isFiat: true
});
}
updateUnitUI();
};
$scope.changeAlternativeUnit = function() {
// Do nothing is fiat is not main unit
if (!availableUnits[unitIndex].isFiat) return;
var nextCoin = lodash.findIndex(availableUnits, function(x) {
if (x.isFiat) return false;
if (x.id == availableUnits[altUnitIndex].id) return false;
return true;
});
if (nextCoin >= 0) {
altUnitIndex = nextCoin;
updateUnitUI();
}
};
function checkFontSize() {
if ($scope.amount && $scope.amount.length >= SMALL_FONT_SIZE_LIMIT) $scope.smallFont = true;
else $scope.smallFont = false;
@ -132,7 +254,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.pushDigit = function(digit) {
if ($scope.amount && $scope.amount.length >= LENGTH_EXPRESSION_LIMIT) return;
if ($scope.amount.indexOf('.') > -1 && digit == '.') return;
if ($scope.showAlternativeAmount && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return;
if (availableUnits[unitIndex].isFiat && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return;
$scope.amount = ($scope.amount + digit).replace('..', '.');
checkFontSize();
@ -169,7 +291,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
};
$scope.resetAmount = function() {
$scope.amount = $scope.alternativeResult = $scope.amountResult = $scope.globalResult = '';
$scope.amount = $scope.alternativeAmount = $scope.globalResult = '';
$scope.allowSend = false;
checkFontSize();
};
@ -180,24 +302,39 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.allowSend = lodash.isNumber(result) && +result > 0;
if (lodash.isNumber(result)) {
$scope.globalResult = isExpression($scope.amount) ? '= ' + processResult(result) : '';
$scope.amountResult = $filter('formatFiatAmount')(toFiat(result));
$scope.alternativeResult = txFormatService.formatAmount(fromFiat(result) * unitToSatoshi, true);
if (availableUnits[unitIndex].isFiat) {
var a = fromFiat(result);
if (a) {
$scope.alternativeAmount = txFormatService.formatAmount(a * unitToSatoshi, true);
} else {
if (result) {
$scope.alternativeAmount = 'N/A';
} else {
$scope.alternativeAmount = null;
}
$scope.allowSend = false;
}
} else {
$scope.alternativeAmount = $filter('formatFiatAmount')(toFiat(result));
}
}
};
function processResult(val) {
if ($scope.showAlternativeAmount)
return $filter('formatFiatAmount')(val);
else
return txFormatService.formatAmount(val.toFixed(unitDecimals) * unitToSatoshi, true);
if (availableUnits[unitIndex].isFiat) return $filter('formatFiatAmount')(val);
else return txFormatService.formatAmount(val.toFixed(unitDecimals) * unitToSatoshi, true);
};
function fromFiat(val) {
return parseFloat((rateService.fromFiat(val, $scope.alternativeIsoCode) * satToUnit).toFixed(unitDecimals));
return parseFloat((rateService.fromFiat(val, fiatCode, availableUnits[altUnitIndex].id) * satToUnit).toFixed(unitDecimals));
};
function toFiat(val) {
return parseFloat((rateService.toFiat(val * unitToSatoshi, $scope.alternativeIsoCode)).toFixed(2));
if (!rateService.getRate(fiatCode)) return;
return parseFloat((rateService.toFiat(val * unitToSatoshi, fiatCode, availableUnits[unitIndex].id)).toFixed(2));
};
function evaluate(val) {
@ -212,33 +349,50 @@ angular.module('copayApp.controllers').controller('amountController', function($
};
function format(val) {
if (!val) return;
var result = val.toString();
if (isOperator(lodash.last(val)))
result = result.slice(0, -1);
if (isOperator(lodash.last(val))) result = result.slice(0, -1);
return result.replace('x', '*');
};
$scope.finish = function() {
var unit = availableUnits[unitIndex];
var _amount = evaluate(format($scope.amount));
var coin = unit.id;
if (unit.isFiat) {
coin = availableUnits[altUnitIndex].id;
}
if ($scope.nextStep) {
$state.transitionTo($scope.nextStep, {
id: _id,
amount: $scope.useSendMax ? null : _amount,
currency: $scope.showAlternativeAmount ? $scope.alternativeIsoCode : $scope.unitName,
currency: unit.id.toUpperCase(),
coin: coin,
useSendMax: $scope.useSendMax
});
} else {
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
var amount = _amount;
if (unit.isFiat) {
amount = (fromFiat(amount) * unitToSatoshi).toFixed(0);
} else {
amount = (amount * unitToSatoshi).toFixed(0);
}
$state.transitionTo('tabs.send.confirm', {
recipientType: $scope.recipientType,
toAmount: $scope.useSendMax ? null : (amount * unitToSatoshi).toFixed(0),
toAmount: amount,
toAddress: $scope.toAddress,
toName: $scope.toName,
toEmail: $scope.toEmail,
toColor: $scope.toColor,
coin: coin,
useSendMax: $scope.useSendMax
});
}

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('buyAmazonController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, amazonService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, gettextCatalog, txFormatService) {
var coin = 'btc';
var amount;
var currency;
var createdTx;
@ -64,7 +65,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
};
var satToFiat = function(sat, cb) {
txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) {
txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) {
return cb(value);
});
};
@ -216,8 +217,8 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
});
var initialize = function(wallet) {
var parsedAmount = txFormatService.parseAmount(amount, currency);
$scope.currencyIsoCode = parsedAmount.alternativeIsoCode;
var parsedAmount = txFormatService.parseAmount(coin, amount, currency);
$scope.currencyIsoCode = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
var dataSrc = {
amount: parsedAmount.amount,
@ -260,7 +261,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
invoiceUrl: invoice.url,
invoiceTime: invoice.invoiceTime
};
$scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount);
$scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount);
setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee);
});
});
@ -292,7 +293,8 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
hasFunds: true
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack(null, gettextCatalog.getString('No wallets available'));

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, $ionicConfig, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -33,46 +34,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
// Buy always in BTC
amount = (parsedAmount.amountSat / 100000000).toFixed(8);
currency = 'BTC';
$scope.amountUnitStr = parsedAmount.amountUnitStr;
ongoingProcess.set('calculatingFee', true);
coinbaseService.checkEnoughFundsForFee(amount, function(err) {
ongoingProcess.set('calculatingFee', false);
if (err) {
showErrorAndBack(err);
return;
}
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
var processPaymentInfo = function() {
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
@ -116,7 +78,33 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
$scope.buyRequest();
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
$scope.buyRequest = function() {
@ -209,12 +197,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
});
};
$timeout(function() {
var tx = b.data ? b.data.transaction : null;
if (tx) {
processBuyTx(tx);
}
else {
var _processBuyOrder = function() {
coinbaseService.getBuyOrder(accessToken, accountId, b.data.id, function (err, buyResp) {
if (err) {
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
@ -222,9 +205,24 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
return;
}
var tx = buyResp.data ? buyResp.data.transaction : null;
if (tx && tx.id) {
processBuyTx(tx);
} else {
$timeout(function() {
_processBuyOrder();
}, 5000);
}
});
}
$timeout(function() {
var tx = b.data ? b.data.transaction : null;
if (tx && tx.id) {
processBuyTx(tx);
}
else {
_processBuyOrder();
}
}, 8000);
});
});
@ -238,6 +236,25 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
// Buy always in BTC
amount = (parsedAmount.amountSat / 100000000).toFixed(8);
currency = 'BTC';
$scope.amountUnitStr = parsedAmount.amountUnitStr;
ongoingProcess.set('calculatingFee', true);
coinbaseService.checkEnoughFundsForFee(amount, function(err) {
ongoingProcess.set('calculatingFee', false);
if (err) {
showErrorAndBack(err);
return;
}
processPaymentInfo();
});
};
$scope.goBackHome = function() {

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('buyGlideraController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, glideraService, popupService, profileService, ongoingProcess, walletService, platformInfo, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -35,36 +36,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
var processPaymentInfo = function() {
ongoingProcess.set('connectingGlidera', true);
glideraService.init(function(err, data) {
if (err) {
@ -88,6 +60,33 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
$scope.buyInfo = buy;
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
var ask2FaCode = function(mode, cb) {
@ -162,6 +161,15 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
processPaymentInfo();
};
$scope.goBackHome = function() {

View file

@ -0,0 +1,354 @@
'use strict';
angular.module('copayApp.controllers').controller('buyMercadoLibreController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, mercadoLibreService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, txFormatService, gettextCatalog) {
var coin = 'btc';
var amount;
var currency;
var createdTx;
var message;
var invoiceId;
var configWallet = configService.getSync().wallet;
$scope.isCordova = platformInfo.isCordova;
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var _resetValues = function() {
$scope.totalAmountStr = $scope.amount = $scope.invoiceFee = $scope.networkFee = $scope.totalAmount = $scope.wallet = null;
createdTx = message = invoiceId = null;
};
var showErrorAndBack = function(title, msg) {
title = title || gettextCatalog.getString('Error');
$scope.sendStatus = '';
$log.error(msg);
msg = (msg && msg.errors) ? msg.errors[0].message : msg;
popupService.showAlert(title, msg, function() {
$ionicHistory.goBack();
});
};
var showError = function(title, msg, cb) {
cb = cb || function() {};
title = title || gettextCatalog.getString('Error');
$scope.sendStatus = '';
$log.error(msg);
msg = (msg && msg.errors) ? msg.errors[0].message : msg;
popupService.showAlert(title, msg, cb);
};
var publishAndSign = function(wallet, txp, onSendStatusChange, cb) {
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
var err = 'No signing proposal: No private key';
$log.info(err);
return cb(err);
}
walletService.publishAndSign(wallet, txp, function(err, txp) {
if (err) return cb(err);
return cb(null, txp);
}, onSendStatusChange);
};
var statusChangeHandler = function(processName, showName, isOn) {
$log.debug('statusChangeHandler: ', processName, showName, isOn);
if (processName == 'Comprando Vale-Presente' && !isOn) {
$scope.sendStatus = 'success';
$timeout(function() {
$scope.$digest();
}, 100);
} else if (showName) {
$scope.sendStatus = showName;
}
};
var satToFiat = function(sat, cb) {
txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) {
return cb(value);
});
};
var setTotalAmount = function(amountSat, invoiceFeeSat, networkFeeSat) {
satToFiat(amountSat, function(a) {
$scope.amount = Number(a);
satToFiat(invoiceFeeSat, function(i) {
$scope.invoiceFee = Number(i);
satToFiat(networkFeeSat, function(n) {
$scope.networkFee = Number(n);
$scope.totalAmount = $scope.amount + $scope.invoiceFee + $scope.networkFee;
$timeout(function() {
$scope.$digest();
});
});
});
});
};
var createInvoice = function(data, cb) {
mercadoLibreService.createBitPayInvoice(data, function(err, dataInvoice) {
if (err) {
var err_title = gettextCatalog.getString('Error creating the invoice');
var err_msg;
if (err && err.message && err.message.match(/suspended/i)) {
err_title = gettextCatalog.getString('Service not available');
err_msg = gettextCatalog.getString('Mercadolibre Gift Card Service is not available at this moment. Please try back later.');
} else if (err && err.message) {
err_msg = err.message;
} else {
err_msg = gettextCatalog.getString('Could not access Gift Card Service');
};
return cb({
title: err_title,
message: err_msg
});
}
var accessKey = dataInvoice ? dataInvoice.accessKey : null;
if (!accessKey) {
return cb({
message: gettextCatalog.getString('No access key defined')
});
}
mercadoLibreService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) {
if (err) {
return cb({
message: gettextCatalog.getString('Could not get the invoice')
});
}
return cb(null, invoice, accessKey);
});
});
};
var createTx = function(wallet, invoice, message, cb) {
var payProUrl = (invoice && invoice.paymentUrls) ? invoice.paymentUrls.BIP73 : null;
if (!payProUrl) {
return cb({
title: gettextCatalog.getString('Error in Payment Protocol'),
message: gettextCatalog.getString('Invalid URL')
});
}
var outputs = [];
var toAddress = invoice.bitcoinAddress;
var amountSat = parseInt((invoice.btcDue * 100000000).toFixed(0)); // BTC to Satoshi
outputs.push({
'toAddress': toAddress,
'amount': amountSat,
'message': message
});
var txp = {
toAddress: toAddress,
amount: amountSat,
outputs: outputs,
message: message,
payProUrl: payProUrl,
excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
feeLevel: configWallet.settings.feeLevel || 'normal'
};
walletService.createTx(wallet, txp, function(err, ctxp) {
if (err) {
return cb({
title: gettextCatalog.getString('Could not create transaction'),
message: bwcError.msg(err)
});
}
return cb(null, ctxp);
});
};
var checkTransaction = lodash.throttle(function(count, dataSrc) {
mercadoLibreService.createGiftCard(dataSrc, function(err, giftCard) {
$log.debug("creating gift card " + count);
if (err) {
$scope.sendStatus = '';
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
giftCard = {};
giftCard.status = 'FAILURE';
}
if (giftCard && giftCard.cardStatus && (giftCard.cardStatus != 'active' && giftCard.cardStatus != 'inactive' && giftCard.cardStatus != 'expired')) {
$scope.sendStatus = '';
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
giftCard = {};
giftCard.status = 'FAILURE';
}
if (giftCard.status == 'PENDING' && count < 3) {
$log.debug("Waiting for payment confirmation");
checkTransaction(count + 1, dataSrc);
return;
}
var now = moment().unix() * 1000;
var newData = giftCard;
newData['invoiceId'] = dataSrc.invoiceId;
newData['accessKey'] = dataSrc.accessKey;
newData['invoiceUrl'] = dataSrc.invoiceUrl;
newData['amount'] = dataSrc.amount;
newData['currency'] = dataSrc.currency;
newData['date'] = dataSrc.invoiceTime || now;
newData['uuid'] = dataSrc.uuid;
mercadoLibreService.savePendingGiftCard(newData, null, function(err) {
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
$log.debug("Saving new gift card with status: " + newData.status);
$scope.mlGiftCard = newData;
});
});
}, 8000, {
'leading': true
});
var initialize = function(wallet) {
var parsedAmount = txFormatService.parseAmount(coin, amount, currency);
$scope.currencyIsoCode = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
var dataSrc = {
amount: parsedAmount.amount,
currency: parsedAmount.currency,
uuid: wallet.id
};
ongoingProcess.set('loadingTxInfo', true);
createInvoice(dataSrc, function(err, invoice, accessKey) {
if (err) {
ongoingProcess.set('loadingTxInfo', false);
showErrorAndBack(err.title, err.message);
return;
}
// Sometimes API does not return this element;
invoice['buyerPaidBtcMinerFee'] = invoice.buyerPaidBtcMinerFee || 0;
var invoiceFeeSat = (invoice.buyerPaidBtcMinerFee * 100000000).toFixed();
message = gettextCatalog.getString("{{amountStr}} for Mercado Livre Brazil Gift Card", {
amountStr: $scope.amountUnitStr
});
createTx(wallet, invoice, message, function(err, ctxp) {
ongoingProcess.set('loadingTxInfo', false);
if (err) {
_resetValues();
showError(err.title, err.message);
return;
}
// Save in memory
createdTx = ctxp;
invoiceId = invoice.id;
createdTx['giftData'] = {
currency: dataSrc.currency,
amount: dataSrc.amount,
uuid: dataSrc.uuid,
accessKey: accessKey,
invoiceId: invoice.id,
invoiceUrl: invoice.url,
invoiceTime: invoice.invoiceTime
};
$scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount);
setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee);
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
amount = data.stateParams.amount;
currency = data.stateParams.currency;
if (amount > 2000 || amount < 50) {
showErrorAndBack(null, gettextCatalog.getString('Purchase amount must be a value between 50 and 2000'));
return;
}
$scope.network = mercadoLibreService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack(null, gettextCatalog.getString('No wallets available'));
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
$scope.buyConfirm = function() {
if (!createdTx) {
showError(null, gettextCatalog.getString('Transaction has not been created'));
return;
}
var title = gettextCatalog.getString('Confirm');
var okText = gettextCatalog.getString('Ok');
var cancelText = gettextCatalog.getString('Cancel');
popupService.showConfirm(title, message, okText, cancelText, function(ok) {
if (!ok) {
$scope.sendStatus = '';
return;
}
ongoingProcess.set('Comprando Vale-Presente', true, statusChangeHandler);
publishAndSign($scope.wallet, createdTx, function() {}, function(err, txSent) {
if (err) {
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
showError(gettextCatalog.getString('Could not send transaction'), err);
return;
}
checkTransaction(1, createdTx.giftData);
});
});
};
$scope.showWalletSelector = function() {
$scope.walletSelectorTitle = 'Buy from';
$scope.showWallets = true;
};
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
initialize(wallet);
};
$scope.goBackHome = function() {
$scope.sendStatus = '';
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.home').then(function() {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.transitionTo('tabs.giftcards.mercadoLibre').then(function() {
$state.transitionTo('tabs.giftcards.mercadoLibre.cards', {
invoiceId: invoiceId
});
});
});
};
});

View file

@ -0,0 +1,192 @@
'use strict';
angular.module('copayApp.controllers').controller('cashScanController',
function($rootScope, $timeout, $scope, $state, $ionicHistory, gettextCatalog, lodash, ongoingProcess, profileService, walletService, $log, txFormatService, bwcError, pushNotificationsService, bwcService, externalLinkService) {
var wallet;
var errors = bwcService.getErrors();
$scope.error = null;
$scope.walletDisabled = '#667';
$scope.$on("$ionicView.beforeEnter", function(event, data) {
updateAllWallets();
});
$scope.openRecoveryToolLink = function() {
var url = 'https://bitpay.github.io/copay-recovery/';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Open the recovery tool.');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
var goHome = function() {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.settings').then(function() {
$state.transitionTo('tabs.home');
});
}
var updateAllWallets = function() {
var walletsBTC = profileService.getWallets({
coin: 'btc',
onlyComplete: true,
network: 'livenet'
});
// Filter out already duplicated wallets
var walletsBCH = profileService.getWallets({
coin: 'bch',
network: 'livenet'
});
var xPubKeyIndex = lodash.indexBy(walletsBCH, "credentials.xPubKey");
walletsBTC = lodash.filter(walletsBTC, function(w) {
return !xPubKeyIndex[w.credentials.xPubKey];
});
var availableWallets = [];
var nonEligibleWallets = [];
lodash.each(walletsBTC, function(w) {
if (w.credentials.derivationStrategy != 'BIP44') {
w.excludeReason = gettextCatalog.getString('Non BIP44 wallet');
nonEligibleWallets.push(w);
} else if (!w.canSign()) {
w.excludeReason = gettextCatalog.getString('Read only wallet');
nonEligibleWallets.push(w);
} else if (w.needsBackup) {
w.excludeReason = gettextCatalog.getString('Backup needed');
nonEligibleWallets.push(w);
} else {
availableWallets.push(w);
}
});
$scope.availableWallets = availableWallets;
$scope.nonEligibleWallets = nonEligibleWallets;
var i = availableWallets.length;
var j = 0;
lodash.each(availableWallets, function(wallet) {
walletService.getBalance(wallet, {
coin: 'bch'
}, function(err, balance) {
if (err) {
wallet.error = (err === 'WALLET_NOT_REGISTERED') ? gettextCatalog.getString('Wallet not registered') : bwcError.msg(err);
$log.error(err);
return;
}
wallet.error = null;
wallet.bchBalance = txFormatService.formatAmountStr('bch', balance.availableAmount);
if (++j == i) {
//Done
$timeout(function() {
$rootScope.$apply();
}, 10);
}
});
});
};
$scope.duplicate = function(wallet) {
$scope.error = null;
$log.debug('Duplicating wallet for BCH:' + wallet.id + ':' + wallet.name);
var opts = {};
opts.name = wallet.name + '[BCH]';
opts.m = wallet.m;
opts.n = wallet.n;
opts.myName = wallet.credentials.copayerName;
opts.networkName = wallet.network;
opts.coin = 'bch';
opts.walletPrivKey = wallet.credentials.walletPrivKey;
opts.compliantDerivation = wallet.credentials.compliantDerivation;
function setErr(err, cb) {
if (!cb) cb = function() {};
$scope.error = bwcError.cb(err, gettextCatalog.getString('Could not duplicate'), function() {
return cb(err);
});
$timeout(function() {
$rootScope.$apply();
}, 10);
}
function importOrCreate(cb) {
walletService.getStatus(wallet, {}, function(err, status) {
if (err) return cb(err);
opts.singleAddress = status.wallet.singleAddress;
// first try to import
profileService.importExtendedPrivateKey(opts.extendedPrivateKey, opts, function(err, newWallet) {
if (err && !(err instanceof errors.NOT_AUTHORIZED)) {
return setErr(err, cb);
}
if (err) {
// create and store a wallet
return profileService.createWallet(opts, function(err, newWallet) {
if (err) return setErr(err, cb);
return cb(null, newWallet, true);
});
}
return cb(null, newWallet);
});
});
};
// Multisig wallets? add Copayers
function addCopayers(newWallet, isNew, cb) {
if (!isNew) return cb();
if (wallet.n == 1) return cb();
$log.info('Adding copayers for BCH wallet config:' + wallet.m + '-' + wallet.n);
walletService.copyCopayers(wallet, newWallet, function(err) {
if (err) return setErr(err, cb);
return cb();
});
};
walletService.getKeys(wallet, function(err, keys) {
if (err) {
$scope.error = err;
return $timeout(function() {
$rootScope.$apply();
}, 10);
}
opts.extendedPrivateKey = keys.xPrivKey;
ongoingProcess.set('duplicatingWallet', true);
importOrCreate(function(err, newWallet, isNew) {
if (err) {
ongoingProcess.set('duplicatingWallet', false);
return;
}
walletService.updateRemotePreferences(newWallet);
pushNotificationsService.updateSubscription(newWallet);
addCopayers(newWallet, isNew, function(err) {
ongoingProcess.set('duplicatingWallet', false);
if (err)
return setErr(err);
if (isNew)
walletService.startScan(newWallet, function() {});
goHome();
});
});
});
}
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification) {
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, $stateParams, $window, $state, $log, profileService, bitcore, bitcoreCash, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification, externalLinkService) {
var countDown = null;
var CONFIRM_LIMIT_USD = 20;
@ -28,7 +28,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
function refresh() {
$timeout(function() {
$scope.$apply();
}, 1);
}, 10);
}
@ -58,9 +58,10 @@ angular.module('copayApp.controllers').controller('confirmController', function(
});
};
function setNoWallet(msg) {
function setNoWallet(msg, criticalError) {
$scope.wallet = null;
$scope.noWalletMessage = msg;
$scope.criticalError = criticalError;
$log.warn('Not ready to make the payment:' + msg);
$timeout(function() {
$scope.$apply();
@ -69,18 +70,19 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.$on("$ionicView.beforeEnter", function(event, data) {
function setWalletSelector(network, minAmount, cb) {
function setWalletSelector(coin, network, minAmount, cb) {
// no min amount? (sendMax) => look for no empty wallets
minAmount = minAmount || 1;
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: network
network: network,
coin: coin
});
if (!$scope.wallets || !$scope.wallets.length) {
setNoWallet(gettextCatalog.getString('No wallets available'));
setNoWallet(gettextCatalog.getString('No wallets available'), true);
return cb();
}
@ -109,7 +111,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
return cb('Could not update any wallet');
if (lodash.isEmpty(filteredWallets)) {
setNoWallet(gettextCatalog.getString('Insufficient funds'));
setNoWallet(gettextCatalog.getString('Insufficient funds'), true);
}
$scope.wallets = lodash.clone(filteredWallets);
return cb();
@ -120,6 +122,30 @@ angular.module('copayApp.controllers').controller('confirmController', function(
// Setup $scope
var B = data.stateParams.coin == 'bch' ? bitcoreCash : bitcore;
var networkName;
try {
networkName = (new B.Address(data.stateParams.toAddress)).network.name;
} catch(e) {
var message = gettextCatalog.getString('Copay only supports Bitcoin Cash using new version numbers addresses');
var backText = gettextCatalog.getString('Go back');
var learnText = gettextCatalog.getString('Learn more');
popupService.showConfirm(null, message, backText, learnText, function(back) {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$state.go('tabs.send').then(function() {
$ionicHistory.clearHistory();
if (!back) {
var url = 'https://support.bitpay.com/hc/en-us/articles/115004671663';
externalLinkService.open(url);
}
});
});
return;
}
// Grab stateParams
tx = {
toAmount: parseInt(data.stateParams.toAmount),
@ -136,21 +162,21 @@ angular.module('copayApp.controllers').controller('confirmController', function(
toName: data.stateParams.toName,
toEmail: data.stateParams.toEmail,
toColor: data.stateParams.toColor,
network: (new bitcore.Address(data.stateParams.toAddress)).network.name,
network: networkName,
coin: data.stateParams.coin,
txp: {},
};
if (tx.coin && tx.coin == 'bch') tx.feeLevel = 'normal';
// Other Scope vars
$scope.isCordova = isCordova;
$scope.isWindowsPhoneApp = isWindowsPhoneApp;
$scope.showAddress = false;
updateTx(tx, null, {}, function() {
$scope.walletSelectorTitle = gettextCatalog.getString('Send from');
setWalletSelector(tx.network, tx.toAmount, function(err) {
setWalletSelector(tx.coin, tx.network, tx.toAmount, function(err) {
if (err) {
return exitWithError('Could not update wallets');
}
@ -163,7 +189,6 @@ angular.module('copayApp.controllers').controller('confirmController', function(
});
});
});
function getSendMaxInfo(tx, wallet, cb) {
@ -227,6 +252,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
};
function updateTx(tx, wallet, opts, cb) {
ongoingProcess.set('calculatingFee', true);
if (opts.clearCache) {
tx.txp = {};
@ -238,10 +264,10 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (!tx.toAmount) return;
// Amount
tx.amountStr = txFormatService.formatAmountStr(tx.toAmount);
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.toAmount);
tx.amountValueStr = tx.amountStr.split(' ')[0];
tx.amountUnitStr = tx.amountStr.split(' ')[1];
txFormatService.formatAlternativeStr(tx.toAmount, function(v) {
txFormatService.formatAlternativeStr(wallet.coin, tx.toAmount, function(v) {
tx.alternativeAmountStr = v;
});
}
@ -250,19 +276,23 @@ angular.module('copayApp.controllers').controller('confirmController', function(
refresh();
// End of quick refresh, before wallet is selected.
if (!wallet) return cb();
if (!wallet) {
ongoingProcess.set('calculatingFee', false);
return cb();
}
feeService.getFeeRate(tx.network, tx.feeLevel, function(err, feeRate) {
if (err) return cb(err);
feeService.getFeeRate(wallet.coin, tx.network, tx.feeLevel, function(err, feeRate) {
if (err) {
ongoingProcess.set('calculatingFee', false);
return cb(err);
}
if (!usingCustomFee) tx.feeRate = feeRate;
tx.feeLevelName = feeService.feeOpts[tx.feeLevel];
if (!wallet)
return cb();
getSendMaxInfo(lodash.clone(tx), wallet, function(err, sendMaxInfo) {
if (err) {
ongoingProcess.set('calculatingFee', false);
var msg = gettextCatalog.getString('Error getting SendMax information');
return setSendError(msg);
}
@ -272,6 +302,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$log.debug('Send max info', sendMaxInfo);
if (tx.sendMax && sendMaxInfo.amount == 0) {
ongoingProcess.set('calculatingFee', false);
setNoWallet(gettextCatalog.getString('Insufficient funds'));
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'));
return cb('no_funds');
@ -280,20 +311,27 @@ angular.module('copayApp.controllers').controller('confirmController', function(
tx.sendMaxInfo = sendMaxInfo;
tx.toAmount = tx.sendMaxInfo.amount;
updateAmount();
showSendMaxWarning(sendMaxInfo);
ongoingProcess.set('calculatingFee', false);
$timeout(function() {
showSendMaxWarning(wallet, sendMaxInfo);
}, 200);
}
// txp already generated for this wallet?
if (tx.txp[wallet.id]) {
ongoingProcess.set('calculatingFee', false);
refresh();
return cb();
}
getTxp(lodash.clone(tx), wallet, opts.dryRun, function(err, txp) {
if (err) return cb(err);
ongoingProcess.set('calculatingFee', false);
if (err) {
return cb(err);
}
txp.feeStr = txFormatService.formatAmountStr(txp.fee);
txFormatService.formatAlternativeStr(txp.fee, function(v) {
txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee);
txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function(v) {
txp.alternativeFeeStr = v;
});
@ -321,14 +359,26 @@ angular.module('copayApp.controllers').controller('confirmController', function(
}
function setButtonText(isMultisig, isPayPro) {
$scope.buttonText = gettextCatalog.getString(isCordova && !isWindowsPhoneApp ? 'Slide' : 'Click') + ' ';
if (isPayPro) {
$scope.buttonText += gettextCatalog.getString('to pay');
if (isCordova && !isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to pay');
} else {
$scope.buttonText = gettextCatalog.getString('Click to pay');
}
} else if (isMultisig) {
$scope.buttonText += gettextCatalog.getString('to accept');
} else
$scope.buttonText += gettextCatalog.getString('to send');
if (isCordova && !isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to accept');
} else {
$scope.buttonText = gettextCatalog.getString('Click to accept');
}
} else {
if (isCordova && !isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to send');
} else {
$scope.buttonText = gettextCatalog.getString('Click to send');
}
}
};
@ -337,26 +387,26 @@ angular.module('copayApp.controllers').controller('confirmController', function(
};
function showSendMaxWarning(sendMaxInfo) {
function showSendMaxWarning(wallet, sendMaxInfo) {
function verifyExcludedUtxos() {
var warningMsg = [];
if (sendMaxInfo.utxosBelowFee > 0) {
warningMsg.push(gettextCatalog.getString("A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided.", {
amountBelowFeeStr: txFormatService.formatAmountStr(sendMaxInfo.amountBelowFee)
amountBelowFeeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountBelowFee)
}));
}
if (sendMaxInfo.utxosAboveMaxSize > 0) {
warningMsg.push(gettextCatalog.getString("A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded.", {
amountAboveMaxSizeStr: txFormatService.formatAmountStr(sendMaxInfo.amountAboveMaxSize)
amountAboveMaxSizeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountAboveMaxSize)
}));
}
return warningMsg.join('\n');
};
var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees.", {
fee: txFormatService.formatAmountStr(sendMaxInfo.fee)
fee: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.fee)
});
var warningMsg = verifyExcludedUtxos();
@ -422,6 +472,11 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.wallet = wallet;
// If select another wallet
tx.coin = wallet.coin;
tx.feeLevel = wallet.coin == 'bch' ? 'normal' : configFeeLevel;
usingCustomFee = null;
setButtonText(wallet.credentials.m > 1, !!tx.paypro);
if (tx.paypro)
@ -483,7 +538,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (walletService.isEncrypted(wallet))
return cb();
var amountUsd = parseFloat(txFormatService.formatToUSD(txp.amount));
var amountUsd = parseFloat(txFormatService.formatToUSD(wallet.coin, txp.amount));
if (amountUsd <= CONFIRM_LIMIT_USD)
return cb();
@ -563,10 +618,13 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.chooseFeeLevel = function(tx, wallet) {
if (wallet.coin == 'bch') return;
var scope = $rootScope.$new(true);
scope.network = tx.network;
scope.feeLevel = tx.feeLevel;
scope.noSave = true;
scope.coin = wallet.coin;
if (usingCustomFee) {
scope.customFeePerKB = tx.feeRate;

View file

@ -22,11 +22,16 @@ angular.module('copayApp.controllers').controller('createController',
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.formData = {};
var defaults = configService.getDefaults();
var config = configService.getSync();
var tc = $state.current.name == 'tabs.add.create-personal' ? 1 : defaults.wallet.totalCopayers;
$scope.formData.account = 1;
$scope.formData.bwsurl = defaults.bws.url;
$scope.TCValues = lodash.range(2, defaults.limits.totalCopayers + 1);
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.coin = data.stateParams.coin;
if (config.cashSupport) $scope.enableCash = true;
$scope.setTotalCopayers(tc);
updateRCSelect(tc);
resetPasswordFields();
@ -133,10 +138,11 @@ angular.module('copayApp.controllers').controller('createController',
m: $scope.formData.requiredCopayers,
n: $scope.formData.totalCopayers,
myName: $scope.formData.totalCopayers > 1 ? $scope.formData.myName : null,
networkName: $scope.formData.testnetEnabled ? 'testnet' : 'livenet',
networkName: $scope.formData.testnetEnabled && $scope.formData.coin != 'bch' ? 'testnet' : 'livenet',
bwsurl: $scope.formData.bwsurl,
singleAddress: $scope.formData.singleAddressEnabled,
walletPrivKey: $scope.formData._walletPrivKey, // Only for testing
coin: $scope.formData.coin
};
var setSeed = $scope.formData.seedSource.id == 'set';
@ -170,6 +176,11 @@ angular.module('copayApp.controllers').controller('createController',
}
if ($scope.formData.seedSource.id == walletService.externalSource.ledger.id || $scope.formData.seedSource.id == walletService.externalSource.trezor.id || $scope.formData.seedSource.id == walletService.externalSource.intelTEE.id) {
if ($scope.formData.coin == 'bch') {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Hardware wallets are not yet supported with Bitcoin Cash'));
return;
}
var account = $scope.formData.account;
if (!account || account < 1) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));

View file

@ -8,6 +8,10 @@ angular.module('copayApp.controllers').controller('customAmountController', func
});
};
var setProtocolHandler = function() {
$scope.protocolHandler = walletService.getProtocolHandler($scope.wallet);
}
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var walletId = data.stateParams.id;
@ -20,6 +24,8 @@ angular.module('copayApp.controllers').controller('customAmountController', func
$scope.wallet = profileService.getWallet(walletId);
setProtocolHandler();
walletService.getAddress($scope.wallet, false, function(err, addr) {
if (!addr) {
showErrorAndBack('Error', 'Could not get the address');
@ -28,7 +34,9 @@ angular.module('copayApp.controllers').controller('customAmountController', func
$scope.address = addr;
$scope.coin = data.stateParams.coin;
var parsedAmount = txFormatService.parseAmount(
$scope.wallet.coin,
data.stateParams.amount,
data.stateParams.currency);
@ -37,17 +45,17 @@ angular.module('copayApp.controllers').controller('customAmountController', func
var currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
if (currency != 'BTC') {
// Convert to BTC
if (currency != 'BTC' && currency != 'BCH') {
// Convert to BTC or BCH
var config = configService.getSync().wallet.settings;
var amountUnit = txFormatService.satToUnit(parsedAmount.amountSat);
var btcParsedAmount = txFormatService.parseAmount(amountUnit, config.unitName);
var btcParsedAmount = txFormatService.parseAmount($scope.wallet.coin, amountUnit, $scope.wallet.coin);
$scope.amountBtc = btcParsedAmount.amount;
$scope.altAmountStr = btcParsedAmount.amountUnitStr;
} else {
$scope.amountBtc = amount; // BTC
$scope.altAmountStr = txFormatService.formatAlternativeStr(parsedAmount.amountSat);
$scope.amountBtc = amount; // BTC or BCH
$scope.altAmountStr = txFormatService.formatAlternativeStr($scope.wallet.coin, parsedAmount.amountSat);
}
});
});
@ -61,12 +69,16 @@ angular.module('copayApp.controllers').controller('customAmountController', func
$scope.shareAddress = function() {
if (!platformInfo.isCordova) return;
var data = 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc;
var protocol = 'bitcoin';
if ($scope.wallet.coin == 'bch') protocol += 'cash';
var data = protocol + ':' + $scope.address + '?amount=' + $scope.amountBtc;
window.plugins.socialsharing.share(data, null, null, null);
}
$scope.copyToClipboard = function() {
return 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc;
var protocol = 'bitcoin';
if ($scope.wallet.coin == 'bch') protocol += 'cash';
return protocol + ':' + $scope.address + '?amount=' + $scope.amountBtc;
};
});

View file

@ -5,6 +5,7 @@ angular.module('copayApp.controllers').controller('importController',
var reader = new FileReader();
var defaults = configService.getDefaults();
var config = configService.getSync();
var errors = bwcService.getErrors();
$scope.init = function() {
@ -15,9 +16,14 @@ angular.module('copayApp.controllers').controller('importController',
$scope.formData.bwsurl = defaults.bws.url;
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.account = 1;
$scope.formData.coin = $stateParams.coin;
$scope.importErr = false;
$scope.isCopay = appConfigService.name == 'copay';
$scope.fromHardwareWallet = { value: false };
$scope.fromHardwareWallet = {
value: false
};
if (config.cashSupport) $scope.enableCash = true;
if ($stateParams.code)
$scope.processWalletInfo($stateParams.code);
@ -59,6 +65,15 @@ angular.module('copayApp.controllers').controller('importController',
});
};
$scope.switchTestnetOff = function() {
$scope.formData.testnetEnabled = false;
$scope.setDerivationPath();
$scope.resizeView();
$timeout(function() {
$scope.$apply();
});
};
$scope.processWalletInfo = function(code) {
if (!code) return;
@ -203,6 +218,7 @@ angular.module('copayApp.controllers').controller('importController',
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
var opts = {};
opts.bwsurl = $scope.formData.bwsurl;
opts.coin = $scope.formData.coin;
_importBlob(evt.target.result, opts);
}
}
@ -228,6 +244,7 @@ angular.module('copayApp.controllers').controller('importController',
} else {
var opts = {};
opts.bwsurl = $scope.formData.bwsurl;
opts.coin = $scope.formData.coin;
_importBlob(backupText, opts);
}
};
@ -253,6 +270,7 @@ angular.module('copayApp.controllers').controller('importController',
opts.account = pathData.account;
opts.networkName = pathData.networkName;
opts.derivationStrategy = pathData.derivationStrategy;
opts.coin = $scope.formData.coin;
var words = $scope.formData.words || null;
@ -279,7 +297,7 @@ angular.module('copayApp.controllers').controller('importController',
$log.warn('This wont work for Intel TEE wallets');
var id = $scope.formData.seedSourceAll.id;
var isMultisig = opts.derivationStrategy =='BIP48';
var isMultisig = opts.derivationStrategy == 'BIP48';
var account = opts.account;
opts.entropySourcePath = 'm/' + hwWallet.getEntropyPath(id, isMultisig, account);
}

View file

@ -5,11 +5,14 @@ angular.module('copayApp.controllers').controller('joinController',
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var defaults = configService.getDefaults();
var config = configService.getSync();
$scope.formData = {};
$scope.formData.bwsurl = defaults.bws.url;
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.account = 1;
$scope.formData.secret = null;
$scope.formData.coin = data.stateParams.coin;
if (config.cashSupport) $scope.enableCash = true;
resetPasswordFields();
updateSeedSourceSelect();
});
@ -103,7 +106,8 @@ angular.module('copayApp.controllers').controller('joinController',
var opts = {
secret: $scope.formData.secret,
myName: $scope.formData.myName,
bwsurl: $scope.formData.bwsurl
bwsurl: $scope.formData.bwsurl,
coin: $scope.formData.coin
}
var setSeed = $scope.formData.seedSource.id == 'set';
@ -137,6 +141,11 @@ angular.module('copayApp.controllers').controller('joinController',
}
if ($scope.formData.seedSource.id == walletService.externalSource.ledger.id || $scope.formData.seedSource.id == walletService.externalSource.trezor.id || $scope.formData.seedSource.id == walletService.externalSource.intelTEE.id) {
if ($scope.formData.coin == 'bch') {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Hardware wallets are not yet supported with Bitcoin Cash'));
return;
}
var account = $scope.formData.account;
if (!account || account < 1) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));

View file

@ -0,0 +1,24 @@
'use strict';
angular.module('copayApp.controllers').controller('mercadoLibreController',
function($scope, $timeout, $log, mercadoLibreService, externalLinkService, popupService) {
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var init = function() {
mercadoLibreService.getPendingGiftCards(function(err, gcds) {
if (err) $log.error(err);
$scope.giftCards = gcds;
$timeout(function() {
$scope.$digest();
});
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.network = mercadoLibreService.getNetwork();
init();
});
});

View file

@ -0,0 +1,100 @@
'use strict';
angular.module('copayApp.controllers').controller('mercadoLibreCardsController',
function($scope, $timeout, $ionicModal, $log, $ionicScrollDelegate, lodash, mercadoLibreService, platformInfo, externalLinkService, popupService, ongoingProcess) {
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var updateGiftCards = function(cb) {
mercadoLibreService.getPendingGiftCards(function(err, gcds) {
if (err) {
popupService.showAlert('Could not get gift cards', err);
if (cb) return cb();
else return;
}
$scope.giftCards = gcds;
$timeout(function() {
$scope.$digest();
$ionicScrollDelegate.resize();
if (cb) return cb();
}, 100);
});
};
$scope.updatePendingGiftCards = lodash.debounce(function() {
updateGiftCards(function() {
var index = 0;
var gcds = $scope.giftCards;
lodash.forEach(gcds, function(dataFromStorage) {
if (dataFromStorage.status == 'PENDING') {
$log.debug("Creating / Updating gift card");
mercadoLibreService.createGiftCard(dataFromStorage, function(err, giftCard) {
if (err) {
popupService.showAlert('Error creating gift card', err);
return;
}
if (giftCard.status != 'PENDING') {
var newData = {};
if (!giftCard.status) dataFromStorage.status = null; // Fix error from server
var cardStatus = giftCard.cardStatus;
if (cardStatus && (cardStatus != 'active' && cardStatus != 'inactive' && cardStatus != 'expired'))
giftCard.status = 'FAILURE';
lodash.merge(newData, dataFromStorage, giftCard);
mercadoLibreService.savePendingGiftCard(newData, null, function(err) {
$log.debug("Saving new gift card");
updateGiftCards();
});
}
});
}
});
});
}, 1000, {
'leading': true
});
$scope.openCardModal = function(card) {
$scope.card = card;
$ionicModal.fromTemplateUrl('views/modals/mercadolibre-card-details.html', {
scope: $scope
}).then(function(modal) {
$scope.mercadoLibreCardDetailsModal = modal;
$scope.mercadoLibreCardDetailsModal.show();
});
$scope.$on('modal.hidden', function() {
$scope.updatePendingGiftCards();
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.invoiceId = data.stateParams.invoiceId;
updateGiftCards(function() {
if ($scope.invoiceId) {
var card = lodash.find($scope.giftCards, {
invoiceId: $scope.invoiceId
});
if (lodash.isEmpty(card)) {
popupService.showAlert(null, 'Card not found');
return;
}
$scope.openCardModal(card);
}
});
});
$scope.$on("$ionicView.afterEnter", function(event, data) {
$scope.updatePendingGiftCards();
});
});

View file

@ -18,14 +18,14 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio
var value = lodash.find($scope.feeLevels[$scope.network], {
level: 'superEconomy'
});
return parseInt((value.feePerKB / 1000).toFixed());
return parseInt((value.feePerKb / 1000).toFixed());
};
var getMaxRecommended = function() {
var value = lodash.find($scope.feeLevels[$scope.network], {
level: 'urgent'
});
return parseInt((value.feePerKB / 1000).toFixed());
return parseInt((value.feePerKb / 1000).toFixed());
};
$scope.ok = function() {
@ -61,7 +61,7 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio
// If no custom fee
if (value) {
$scope.customFeePerKB = null;
$scope.feePerSatByte = (value.feePerKB / 1000).toFixed();
$scope.feePerSatByte = (value.feePerKb / 1000).toFixed();
$scope.avgConfirmationTime = value.nbBlocks * 10;
} else {
$scope.avgConfirmationTime = null;
@ -102,7 +102,7 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio
$scope.feeOpts = feeService.feeOpts;
$scope.loadingFee = true;
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.coin, function(err, levels) {
$scope.loadingFee = false;
if (err || lodash.isEmpty(levels)) {
showErrorAndClose(null, err);

View file

@ -0,0 +1,21 @@
'use strict';
angular.module('copayApp.controllers').controller('mercadoLibreCardDetailsController', function($scope, mercadoLibreService, externalLinkService) {
$scope.remove = function() {
mercadoLibreService.savePendingGiftCard($scope.card, {
remove: true
}, function(err) {
$scope.close();
});
};
$scope.close = function() {
$scope.mercadoLibreCardDetailsModal.hide();
};
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
});

View file

@ -23,7 +23,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
};
function displayFeeValues() {
txFormatService.formatAlternativeStr($scope.tx.fee, function(v) {
txFormatService.formatAlternativeStr($scope.wallet.coin, $scope.tx.fee, function(v) {
$scope.tx.feeFiatStr = v;
});
$scope.tx.feeRateStr = ($scope.tx.fee / ($scope.tx.amount + $scope.tx.fee) * 100).toFixed(2) + '%';
@ -31,17 +31,23 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
};
function applyButtonText() {
$scope.buttonText = $scope.isCordova && !$scope.isWindowsPhoneApp ? gettextCatalog.getString('Slide') + ' ' : gettextCatalog.getString('Click') + ' ';
var lastSigner = lodash.filter($scope.tx.actions, {
type: 'accept'
}).length == $scope.tx.requiredSignatures - 1;
if (lastSigner) {
$scope.buttonText += gettextCatalog.getString('to send');
if ($scope.isCordova && !$scope.isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to send');
} else {
$scope.buttonText = gettextCatalog.getString('Click to send');
}
$scope.successText = gettextCatalog.getString('Payment Sent');
} else {
$scope.buttonText += gettextCatalog.getString('to accept');
if ($scope.isCordova && !$scope.isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to accept');
} else {
$scope.buttonText = gettextCatalog.getString('Click to accept');
}
$scope.successText = gettextCatalog.getString('Payment Accepted');
}
};
@ -219,7 +225,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
copayerId: $scope.wallet.credentials.copayerId
});
$scope.tx = txFormatService.processTx(tx);
$scope.tx = txFormatService.processTx($scope.wallet.coin, tx);
if (!action && tx.status == 'pending')
$scope.tx.pendingForUs = true;

View file

@ -27,7 +27,7 @@ angular.module('copayApp.controllers').controller('tourController',
rateService.whenAvailable(function() {
var localCurrency = 'USD';
var btcAmount = 1;
var rate = rateService.toFiat(btcAmount * 1e8, localCurrency);
var rate = rateService.toFiat(btcAmount * 1e8, localCurrency, 'btc');
$scope.localCurrencySymbol = '$';
$scope.localCurrencyPerBtc = $filter('formatFiatAmount')(parseFloat(rate.toFixed(2), 10));
$timeout(function() {

View file

@ -45,8 +45,7 @@ angular.module('copayApp.controllers').controller('paperWalletController',
$scope.balanceSat = balance;
if ($scope.balanceSat <= 0)
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not funds found'));
var config = configService.getSync().wallet.settings;
$scope.balance = txFormatService.formatAmount(balance) + ' ' + config.unitName;
$scope.balance = txFormatService.formatAmountStr($scope.wallet.coin, balance);
}
$scope.$apply();
});
@ -60,9 +59,9 @@ angular.module('copayApp.controllers').controller('paperWalletController',
$scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, null, function(err, testTx) {
if (err) return cb(err);
var rawTxLength = testTx.serialize().length;
feeService.getCurrentFeeRate('livenet', function(err, feePerKB) {
feeService.getCurrentFeeRate('btc', 'livenet', function(err, feePerKb) {
var opts = {};
opts.fee = Math.round((feePerKB * rawTxLength) / 2000);
opts.fee = Math.round((feePerKb * rawTxLength) / 2000);
$scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, opts, function(err, tx) {
if (err) return cb(err);
$scope.wallet.broadcastRawTx({

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesController',
function($scope, $rootScope, $timeout, $log, $ionicHistory, configService, profileService, fingerprintService, walletService, platformInfo) {
function($scope, $rootScope, $timeout, $log, $ionicHistory, configService, profileService, fingerprintService, walletService, platformInfo, externalLinkService, gettextCatalog) {
var wallet;
var walletId;
@ -58,6 +58,16 @@ angular.module('copayApp.controllers').controller('preferencesController',
}
};
$scope.openWikiSpendingPassword = function() {
var url = 'https://github.com/bitpay/copay/wiki/COPAY---FAQ#what-the-spending-password-does';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Read more in our Wiki');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.touchIdChange = function() {
var newStatus = $scope.touchIdEnabled.value;
walletService.setTouchId(wallet, !!newStatus, function(err) {

View file

@ -43,8 +43,9 @@ angular.module('copayApp.controllers').controller('preferencesAltCurrencyControl
$scope.findCurrency = function(search) {
if (!search) init();
$scope.altCurrencyList = lodash.filter(completeAlternativeList, function(item) {
var val = item.name;
return lodash.includes(val.toLowerCase(), search.toLowerCase());
var val = item.name
var val2 = item.isoCode;
return lodash.includes(val.toLowerCase(), search.toLowerCase()) || lodash.includes(val2.toLowerCase(), search.toLowerCase());
});
$timeout(function() {
$scope.$apply();

View file

@ -0,0 +1,42 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesCashController', function($scope, $log, $timeout, appConfigService, configService, gettextCatalog, externalLinkService) {
var updateConfig = function() {
var config = configService.getSync();
$scope.appName = appConfigService.nameCase;
$scope.cashSupport = {
value: config.cashSupport
};
$timeout(function() {
$scope.$apply();
});
};
$scope.cashSupportChange = function() {
var opts = {
cashSupport: $scope.cashSupport.value
};
configService.set(opts, function(err) {
if (err) $log.debug(err);
});
};
$scope.openBitcoinCashWeb = function() {
var url = 'https://www.bitcoincash.org/';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Open bitcoincash.org?');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
updateConfig();
});
});

View file

@ -17,8 +17,7 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro
});
return;
}
$scope.alias = lodash.isEqual($scope.wallet.name, $scope.wallet.credentials.walletName) ? null : $scope.wallet.name + ' ';
$scope.walletName = $scope.wallet.credentials.walletName;
$scope.walletName = $scope.wallet.name;
});
$scope.showDeletePopup = function() {

View file

@ -32,11 +32,12 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu
});
$scope.init = function() {
var coin = 'btc'; // TODO: only BTC in preferences
$scope.network = $scope.network || 'livenet';
$scope.feeOpts = feeService.feeOpts;
$scope.currentFeeLevel = $scope.feeLevel || feeService.getCurrentFeeLevel();
$scope.loadingFee = true;
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels(coin, function(err, levels) {
$scope.loadingFee = false;
if (err) {
//Error is already formatted
@ -66,7 +67,7 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu
return;
}
$scope.feePerSatByte = (value.feePerKB / 1000).toFixed();
$scope.feePerSatByte = (value.feePerKb / 1000).toFixed();
$scope.avgConfirmationTime = value.nbBlocks * 10;
$scope.invalidCustomFeeEntered = false;
setMinWarning();
@ -97,7 +98,7 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu
var value = lodash.find($scope.feeLevels[$scope.network], {
level: 'superEconomy'
});
return parseInt((value.feePerKB / 1000).toFixed());
return parseInt((value.feePerKb / 1000).toFixed());
};
var setMinWarning = function() {

View file

@ -1,43 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesUnitController', function($scope, $log, configService, $ionicHistory, gettextCatalog, walletService, profileService) {
var config = configService.getSync();
$scope.unitList = [{
name: 'bits (1,000,000 bits = 1BTC)',
shortName: 'bits',
value: 100,
decimals: 2,
code: 'bit',
}, {
name: 'BTC',
shortName: 'BTC',
value: 100000000,
decimals: 8,
code: 'btc',
}];
$scope.save = function(newUnit) {
var opts = {
wallet: {
settings: {
unitName: newUnit.shortName,
unitToSatoshi: newUnit.value,
unitDecimals: newUnit.decimals,
unitCode: newUnit.code,
}
}
};
configService.set(opts, function(err) {
if (err) $log.warn(err);
$ionicHistory.goBack();
walletService.updateRemotePreferences(profileService.getWallets())
});
};
$scope.$on("$ionicView.enter", function(event, data){
$scope.currentUnit = config.wallet.settings.unitCode;
});
});

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('sellCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, $ionicConfig, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, appConfigService, configService, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -34,6 +35,51 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
}, onSendStatusChange);
};
var processPaymentInfo = function() {
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var accessToken = res.accessToken;
coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, s) {
$scope.sellPrice = s.data || null;
});
$scope.paymentMethods = [];
$scope.selectedPaymentMethodId = { value : null };
coinbaseService.getPaymentMethods(accessToken, function(err, p) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var hasPrimary;
var pm;
for(var i = 0; i < p.data.length; i++) {
pm = p.data[i];
if (pm.allow_sell) {
$scope.paymentMethods.push(pm);
}
if (pm.allow_sell && pm.primary_sell) {
hasPrimary = true;
$scope.selectedPaymentMethodId.value = pm.id;
}
}
if (lodash.isEmpty($scope.paymentMethods)) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack('No payment method available to sell');
return;
}
if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id;
$scope.sellRequest();
});
});
};
var checkTransaction = lodash.throttle(function(count, txp) {
$log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5');
// TX amount in BTC
@ -125,14 +171,9 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.priceSensitivity = coinbaseService.priceSensitivity;
$scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity };
@ -143,57 +184,14 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
onlyComplete: true,
network: $scope.network,
hasFunds: true,
minAmount: parsedAmount.amountSat
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var accessToken = res.accessToken;
coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, s) {
$scope.sellPrice = s.data || null;
});
$scope.paymentMethods = [];
$scope.selectedPaymentMethodId = { value : null };
coinbaseService.getPaymentMethods(accessToken, function(err, p) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var hasPrimary;
var pm;
for(var i = 0; i < p.data.length; i++) {
pm = p.data[i];
if (pm.allow_sell) {
$scope.paymentMethods.push(pm);
}
if (pm.allow_sell && pm.primary_sell) {
hasPrimary = true;
$scope.selectedPaymentMethodId.value = pm.id;
}
}
if (lodash.isEmpty($scope.paymentMethods)) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack('No payment method available to sell');
return;
}
if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id;
$scope.sellRequest();
});
});
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
$scope.sellRequest = function() {
@ -306,6 +304,15 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
processPaymentInfo();
};
$scope.goBackHome = function() {

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('sellGlideraController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, glideraService, popupService, profileService, ongoingProcess, walletService, configService, platformInfo, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -35,39 +36,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
m: 1, // Only 1-signature wallet
onlyComplete: true,
network: $scope.network,
hasFunds: true,
minAmount: parsedAmount.amountSat
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
var processPaymentInfo = function() {
ongoingProcess.set('connectingGlidera', true);
glideraService.init(function(err, data) {
if (err) {
@ -91,6 +60,35 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
$scope.sellInfo = sell;
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
m: 1, // Only 1-signature wallet
onlyComplete: true,
network: $scope.network,
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
var ask2FaCode = function(mode, cb) {
@ -231,6 +229,15 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
processPaymentInfo();
};
$scope.goBackHome = function() {

View file

@ -206,14 +206,24 @@ angular.module('copayApp.controllers').controller('tabHomeController',
};
var updateAllWallets = function() {
$scope.wallets = profileService.getWallets();
if (lodash.isEmpty($scope.wallets)) return;
var wallets = [];
$scope.walletsBtc = profileService.getWallets({coin: 'btc'});
$scope.walletsBch = profileService.getWallets({coin: 'bch'});
var i = $scope.wallets.length;
lodash.each($scope.walletsBtc, function(wBtc) {
wallets.push(wBtc);
});
lodash.each($scope.walletsBch, function(wBch) {
wallets.push(wBch);
});
if (lodash.isEmpty(wallets)) return;
var i = wallets.length;
var j = 0;
var timeSpan = 60 * 60 * 24 * 7;
lodash.each($scope.wallets, function(wallet) {
lodash.each(wallets, function(wallet) {
walletService.getStatus(wallet, {}, function(err, status) {
if (err) {

View file

@ -8,7 +8,8 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.requestSpecificAmount = function() {
$state.go('tabs.paymentRequest.amount', {
id: $scope.wallet.credentials.walletId
id: $scope.wallet.credentials.walletId,
coin: $scope.wallet.coin
});
};
@ -123,8 +124,13 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
return wallet;
}
var setProtocolHandler = function() {
$scope.protocolHandler = walletService.getProtocolHandler($scope.wallet);
}
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
setProtocolHandler();
$scope.setAddress();
};
@ -136,6 +142,8 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.shareAddress = function() {
if (!$scope.isCordova) return;
window.plugins.socialsharing.share('bitcoin:' + $scope.addr, null, null, null);
var protocol = 'bitcoin';
if ($scope.wallet.coin == 'bch') protocol += 'cash';
window.plugins.socialsharing.share(protocol + ':' + $scope.addr, null, null, null);
}
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService) {
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService, bitcoreCash) {
var originalList;
var CONTACTS_SHOW_LIMIT;
@ -76,6 +76,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
color: v.color,
name: v.name,
recipientType: 'wallet',
coin: v.coin,
network: v.network,
getAddress: function(cb) {
walletService.getAddress(v, false, cb);
},
@ -85,6 +87,14 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
}
}
var getCoin = function(address) {
var cashAddress = bitcoreCash.Address.isValid(address, 'livenet');
if (cashAddress) {
return 'bch';
}
return 'btc';
};
var updateContactsList = function(cb) {
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
@ -99,6 +109,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
address: k,
email: lodash.isObject(v) ? v.email : null,
recipientType: 'contact',
coin: getCoin(k),
getAddress: function(cb) {
return cb(null, k);
},
@ -186,7 +197,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
toAddress: addr,
toName: item.name,
toEmail: item.email,
toColor: item.color
toColor: item.color,
coin: item.coin
})
});
});

View file

@ -6,11 +6,11 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
$scope.currentLanguageName = uxLanguage.getCurrentLanguageName();
$scope.feeOpts = feeService.feeOpts;
$scope.currentFeeLevel = feeService.getCurrentFeeLevel();
$scope.wallets = profileService.getWallets();
$scope.walletsBtc = profileService.getWallets({ coin: 'btc' });
$scope.walletsBch = profileService.getWallets({ coin: 'bch' });
$scope.buyAndSellServices = buyAndSellService.getLinked();
configService.whenAvailable(function(config) {
$scope.unitName = config.wallet.settings.unitName;
$scope.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
@ -26,6 +26,11 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
}, 10);
});
$scope.cashSupport = {
value: config.cashSupport
};
// TODO move this to a generic service
bitpayCardService.getCards(function(err, cards) {
if (err) $log.error(err);
@ -63,6 +68,8 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
});
});
$scope.$on("$ionicView.enter", function(event, data) {
updateConfig();
});

View file

@ -3,6 +3,7 @@
angular.module('copayApp.controllers').controller('topUpController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, popupService, profileService, ongoingProcess, walletService, configService, platformInfo, bitpayService, bitpayCardService, payproService, bwcError, txFormatService, sendMaxService, gettextCatalog) {
$scope.isCordova = platformInfo.isCordova;
var coin = 'btc';
var cardId;
var useSendMax;
var amount;
@ -36,7 +37,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
};
var satToFiat = function(sat, cb) {
txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) {
txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) {
return cb(value);
});
};
@ -218,7 +219,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
// Save TX in memory
createdTx = ctxp;
$scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount);
$scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount);
setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee);
@ -256,7 +257,8 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: bitpayService.getEnvironment().network,
hasFunds: true
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
@ -319,7 +321,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
});
return;
}
var parsedAmount = txFormatService.parseAmount(a, c);
var parsedAmount = txFormatService.parseAmount(coin, a, c);
initializeTopUp(wallet, parsedAmount);
});
};

View file

@ -5,6 +5,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
var txId;
var listeners = [];
var config = configService.getSync();
var blockexplorerUrl;
$scope.$on("$ionicView.beforeEnter", function(event, data) {
txId = data.stateParams.txid;
@ -15,6 +16,12 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.isShared = $scope.wallet.credentials.n > 1;
$scope.txsUnsubscribedForNotifications = config.confirmedTxsNotifications ? !config.confirmedTxsNotifications.enabled : true;
if ($scope.wallet.coin == 'bch') {
blockexplorerUrl = 'bch-insight.bitpay.com';
} else {
blockexplorerUrl = 'insight.bitpay.com';
}
txConfirmNotification.checkIfEnabled(txId, function(res) {
$scope.txNotification = {
value: res
@ -40,6 +47,16 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
});
});
$scope.readMore = function() {
var url = 'https://github.com/bitpay/copay/wiki/COPAY---FAQ#amount-too-low-to-spend';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Read more in our Wiki');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
function updateMemo() {
walletService.getTxNote($scope.wallet, $scope.btx.txid, function(err, note) {
if (err) {
@ -102,8 +119,8 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not available at this time'));
}
$scope.btx = txFormatService.processTx(tx);
txFormatService.formatAlternativeStr(tx.fees, function(v) {
$scope.btx = txFormatService.processTx($scope.wallet.coin, tx);
txFormatService.formatAlternativeStr($scope.wallet.coin, tx.fees, function(v) {
$scope.btx.feeFiatStr = v;
$scope.btx.feeRateStr = ($scope.btx.fees / ($scope.btx.amount + $scope.btx.fees) * 100).toFixed(2) + '%';
});
@ -121,7 +138,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.$digest();
});
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
if (err) return;
walletService.getLowAmount($scope.wallet, levels, function(err, amount) {
if (err) return;
@ -168,10 +185,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.viewOnBlockchain = function() {
var btx = $scope.btx;
var url = 'https://blockchain.info/tx/' + btx.txid;
if ($scope.getShortNetworkName() == 'test') {
url = "https://test-insight.bitpay.com/tx/" + btx.txid;
}
var url = 'https://' + ($scope.getShortNetworkName() == 'test' ? 'test-' : '') + blockexplorerUrl + '/tx/' + btx.txid;
var optIn = true;
var title = null;
var message = gettextCatalog.getString('View Transaction on Insight');

View file

@ -52,7 +52,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
var analyzeUtxos = function() {
if (analyzeUtxosDone) return;
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
if (err) return;
walletService.getLowUtxos($scope.wallet, levels, function(err, resp) {
if (err || !resp) return;
@ -156,9 +156,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
var updateTxHistory = function(cb) {
if (!cb) cb = function() {};
if ($scope.updatingTxHistory) return;
$scope.updatingTxHistory = true;
$scope.updateTxHistoryError = false;
$scope.updatingTxHistoryProgress = 0;
@ -171,7 +169,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
});
};
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
walletService.getTxHistory($scope.wallet, {
progressFn: progressFn,
feeLevels: levels,
@ -184,7 +182,9 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
}
$scope.completeTxHistory = txHistory;
$scope.showHistory();
$timeout(function() {
$scope.$apply();
});
return cb();
});
});
@ -356,6 +356,8 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (!$scope.wallet) return;
$scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;
$scope.updatingTxHistory = true;
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
$scope.addressbook = ab || {};

View file

@ -1,12 +1,18 @@
'use strict';
angular.module('copayApp.directives')
.directive('validAddress', ['$rootScope', 'bitcore',
function($rootScope, bitcore) {
.directive('validAddress', ['$rootScope', 'bitcore', 'bitcoreCash',
function($rootScope, bitcore, bitcoreCash) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
// Bitcoin address
var URI = bitcore.URI;
var Address = bitcore.Address
// Bitcoin Cash address
var URICash = bitcoreCash.URI;
var AddressCash = bitcoreCash.Address
var validator = function(value) {
// Regular url
@ -16,8 +22,8 @@ angular.module('copayApp.directives')
}
// Bip21 uri
if (/^bitcoin:/.test(value)) {
var uri, isAddressValidLivenet, isAddressValidTestnet;
if (/^bitcoin:/.test(value)) {
var isUriValid = URI.isValid(value);
if (isUriValid) {
uri = new URI(value);
@ -26,6 +32,14 @@ angular.module('copayApp.directives')
}
ctrl.$setValidity('validAddress', isUriValid && (isAddressValidLivenet || isAddressValidTestnet));
return value;
} else if (/^bitcoincash:/.test(value)) {
var isUriValid = URICash.isValid(value);
if (isUriValid) {
uri = new URICash(value);
isAddressValidLivenet = AddressCash.isValid(uri.address.toString(), 'livenet')
}
ctrl.$setValidity('validAddress', isUriValid && (isAddressValidLivenet));
return value;
}
if (typeof value == 'undefined') {
@ -33,10 +47,11 @@ angular.module('copayApp.directives')
return;
}
// Regular Address
// Regular Address: try Bitcoin and Bitcoin Cash
var regularAddressLivenet = Address.isValid(value, 'livenet');
var regularAddressTestnet = Address.isValid(value, 'testnet');
ctrl.$setValidity('validAddress', (regularAddressLivenet || regularAddressTestnet));
var regularAddressCashLivenet = AddressCash.isValid(value, 'livenet');
ctrl.$setValidity('validAddress', (regularAddressLivenet || regularAddressTestnet || regularAddressCashLivenet));
return value;
};

View file

@ -27,12 +27,10 @@ angular.module('copayApp.filters', [])
}
})
.filter('formatFiatAmount', ['$filter', '$locale', 'configService',
function(filter, locale, configService) {
function(filter, locale) {
var numberFilter = filter('number');
var formats = locale.NUMBER_FORMATS;
var config = configService.getSync().wallet.settings;
return function(amount) {
if (!config) return amount;
var fractionSize = 2;
var value = numberFilter(amount, fractionSize);

View file

@ -287,7 +287,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
*/
.state('tabs.send.amount', {
url: '/amount/:recipientType/:toAddress/:toName/:toEmail/:toColor',
url: '/amount/:recipientType/:toAddress/:toName/:toEmail/:toColor/:coin/:fixedUnit',
views: {
'tab-send@tabs': {
controller: 'amountController',
@ -296,7 +296,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.send.confirm', {
url: '/confirm/:recipientType/:toAddress/:toName/:toAmount/:toEmail/:toColor/:description/:useSendMax',
url: '/confirm/:recipientType/:toAddress/:toName/:toAmount/:toEmail/:toColor/:description/:coin/:useSendMax',
views: {
'tab-send@tabs': {
controller: 'confirmController',
@ -329,6 +329,9 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
'tab-home@tabs': {
templateUrl: 'views/add.html'
}
},
params: {
coin: 'btc'
}
})
.state('tabs.add.join', {
@ -374,6 +377,16 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
*
*/
.state('tabs.preferencesCash', {
url: '/preferencesCash',
views: {
'tab-settings@tabs': {
controller: 'preferencesCashController',
templateUrl: 'views/preferencesCash.html'
}
}
})
.state('tabs.notifications', {
url: '/notifications',
views: {
@ -392,15 +405,6 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
}
})
.state('tabs.unit', {
url: '/unit',
views: {
'tab-settings@tabs': {
controller: 'preferencesUnitController',
templateUrl: 'views/preferencesUnit.html'
}
}
})
.state('tabs.fee', {
url: '/fee',
views: {
@ -474,6 +478,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
/*
*
* Wallet preferences
@ -589,6 +594,16 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.preferencesCash.scan', {
url: '/cashScan',
views: {
'tab-settings@tabs': {
controller: 'cashScanController',
templateUrl: 'views/cashScan.html'
}
}
})
/*
*
* Addressbook
@ -676,12 +691,12 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
abstract: true,
params: {
id: null,
nextStep: 'tabs.paymentRequest.confirm'
nextStep: 'tabs.paymentRequest.confirm',
}
})
.state('tabs.paymentRequest.amount', {
url: '/amount',
url: '/amount/:coin',
views: {
'tab-receive@tabs': {
controller: 'amountController',
@ -690,7 +705,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.paymentRequest.confirm', {
url: '/confirm/:amount/:currency',
url: '/confirm/:amount/:currency/:coin',
views: {
'tab-receive@tabs': {
controller: 'customAmountController',
@ -923,6 +938,9 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
controllerAs: 'glidera',
templateUrl: 'views/glidera.html'
}
},
params: {
coin: 'btc',
}
})
.state('tabs.buyandsell.glidera.amount', {
@ -976,6 +994,9 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
controllerAs: 'coinbase',
templateUrl: 'views/coinbase.html'
}
},
params: {
coin: 'btc',
}
})
.state('tabs.preferences.coinbase', {
@ -1026,36 +1047,54 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
abstract: true
})
/*
*
* Mercado Libre Gift Card
*
*/
/* Explore Bitcoin.com */
.state('tabs.bitcoin-com', {
url: '/bitcoincom',
.state('tabs.giftcards.mercadoLibre', {
url: '/mercadoLibre',
views: {
'tab-home@tabs': {
controller: 'bitcoincomController',
templateUrl: 'views/bitcoincom.html'
controller: 'mercadoLibreController',
templateUrl: 'views/mercadoLibre.html'
}
}
})
/* buy.Bitcoin.com */
.state('tabs.buyandsell.bitcoindotcom', {
url: '/buyBitcoindotcom',
.state('tabs.giftcards.mercadoLibre.cards', {
url: '/cards',
views: {
'tab-home@tabs': {
controller: 'buyBitcoindotcomController',
templateUrl: 'views/buyBitcoindotcom.html'
controller: 'mercadoLibreCardsController',
templateUrl: 'views/mercadoLibreCards.html'
}
},
params: {
invoiceId: null
}
})
/* Price Chart */
.state('tabs.pricechart', {
url: '/pricechart',
.state('tabs.giftcards.mercadoLibre.amount', {
url: '/amount',
views: {
'tab-home@tabs': {
controller: 'pricechartController',
templateUrl: 'views/pricechart.html'
controller: 'amountController',
templateUrl: 'views/amount.html'
}
},
params: {
nextStep: 'tabs.giftcards.mercadoLibre.buy',
currency: 'BRL',
coin: 'btc',
fixedUnit: 1,
}
})
.state('tabs.giftcards.mercadoLibre.buy', {
url: '/buy/:amount/:currency',
views: {
'tab-home@tabs': {
controller: 'buyMercadoLibreController',
templateUrl: 'views/buyMercadoLibre.html'
}
}
})
@ -1098,7 +1137,8 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
params: {
nextStep: 'tabs.giftcards.amazon.buy',
currency: 'USD',
forceCurrency: true
coin: 'btc',
fixedUnit: true,
}
})
.state('tabs.giftcards.amazon.buy', {
@ -1138,6 +1178,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
params: {
id: null,
currency: 'USD',
coin: 'btc',
useSendMax: null
}
})
@ -1169,7 +1210,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
});
})
.run(function($rootScope, $state, $location, $log, $timeout, startupService, fingerprintService, $ionicHistory, $ionicPlatform, $window, appConfigService, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService, configService, emailService, /* plugins START HERE => */ glideraService, buydotbitcoindotcomService, amazonService, bitpayCardService, applicationService) {
.run(function($rootScope, $state, $location, $log, $timeout, startupService, ionicToast, fingerprintService, $ionicHistory, $ionicPlatform, $window, appConfigService, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService, configService, emailService, /* plugins START HERE => */ coinbaseService, glideraService, amazonService, bitpayCardService, applicationService, mercadoLibreService) {
uxLanguage.init();

View file

@ -1,8 +1,19 @@
'use strict';
angular.module('copayApp.services').factory('addressbookService', function(bitcore, storageService, lodash) {
angular.module('copayApp.services').factory('addressbookService', function($log, bitcore, bitcoreCash, storageService, lodash) {
var root = {};
var getNetwork = function(address) {
var network;
try {
network = (new bitcore.Address(address)).network.name;
} catch(e) {
$log.warn('No valid bitcoin address. Trying bitcoin cash...');
network = (new bitcoreCash.Address(address)).network.name;
}
return network;
};
root.get = function(addr, cb) {
storageService.getAddressbook('testnet', function(err, ab) {
if (err) return cb(err);
@ -35,7 +46,8 @@ angular.module('copayApp.services').factory('addressbookService', function(bitco
};
root.add = function(entry, cb) {
var network = (new bitcore.Address(entry.address)).network.name;
var network = getNetwork(entry.address);
if (lodash.isEmpty(network)) return cb('Not valid bitcoin address');
storageService.getAddressbook(network, function(err, ab) {
if (err) return cb(err);
if (ab) ab = JSON.parse(ab);
@ -53,7 +65,8 @@ angular.module('copayApp.services').factory('addressbookService', function(bitco
};
root.remove = function(addr, cb) {
var network = (new bitcore.Address(addr)).network.name;
var network = getNetwork(addr);
if (lodash.isEmpty(network)) return cb('Not valid bitcoin address');
storageService.getAddressbook(network, function(err, ab) {
if (err) return cb(err);
if (ab) ab = JSON.parse(ab);

View file

@ -0,0 +1,6 @@
'use strict';
angular.module('copayApp.services')
.factory('bitcoreCash', function bitcoreFactory(bwcService) {
var bitcoreCash = bwcService.getBitcoreCash();
return bitcoreCash;
});

View file

@ -53,6 +53,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
'wallet:sells:create,' +
'wallet:transactions:read,' +
'wallet:transactions:send,' +
'wallet:transactions:send:bypass-2fa,' +
'wallet:payment-methods:read';
// NW has a bug with Window Object
@ -169,9 +170,9 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
var _getNetAmount = function(amount, cb) {
// Fee Normal for a single transaction (450 bytes)
var txNormalFeeKB = 450 / 1000;
feeService.getFeeRate(null, 'normal', function(err, feePerKB) {
feeService.getFeeRate('btc', 'livenet', 'normal', function(err, feePerKb) {
if (err) return cb(err);
var feeBTC = (feePerKB * txNormalFeeKB / 100000000).toFixed(8);
var feeBTC = (feePerKb * txNormalFeeKB / 100000000).toFixed(8);
return cb(null, amount - feeBTC, feeBTC);
});

View file

@ -69,7 +69,8 @@ angular.module('copayApp.services').factory('configService', function(storageSer
bannedUntil: null,
},
// External services
cashSupport: false,
recentTransactions: {
enabled: true,
},
@ -141,6 +142,11 @@ angular.module('copayApp.services').factory('configService', function(storageSer
configCache.hideNextSteps = defaultConfig.hideNextSteps;
}
if (!configCache.cashSupport) {
configCache.cashSupport = defaultConfig.cashSupport;
}
if (!configCache.recentTransactions) {
configCache.recentTransactions = defaultConfig.recentTransactions;
}
@ -151,6 +157,14 @@ angular.module('copayApp.services').factory('configService', function(storageSer
configCache.bitpayAccount = defaultConfig.bitpayAccount;
}
if (configCache.wallet.settings.unitCode == 'bit') {
// Convert to BTC. Bits will be disabled
configCache.wallet.settings.unitName = defaultConfig.wallet.settings.unitName;
configCache.wallet.settings.unitToSatoshi = defaultConfig.wallet.settings.unitToSatoshi;
configCache.wallet.settings.unitDecimals = defaultConfig.wallet.settings.unitDecimals;
configCache.wallet.settings.unitCode = defaultConfig.wallet.settings.unitCode;
}
} else {
configCache = lodash.clone(defaultConfig);
};

View file

@ -17,6 +17,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
var cache = {
updateTs: 0,
coin: ''
};
root.getCurrentFeeLevel = function() {
@ -24,20 +25,20 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
};
root.getFeeRate = function(network, feeLevel, cb) {
root.getFeeRate = function(coin, network, feeLevel, cb) {
if (feeLevel == 'custom') return cb();
network = network || 'livenet';
root.getFeeLevels(function(err, levels, fromCache) {
root.getFeeLevels(coin, function(err, levels, fromCache) {
if (err) return cb(err);
var feeLevelRate = lodash.find(levels[network], {
level: feeLevel
});
if (!feeLevelRate || !feeLevelRate.feePerKB) {
if (!feeLevelRate || !feeLevelRate.feePerKb) {
return cb({
message: gettextCatalog.getString("Could not get dynamic fee for level: {{feeLevel}}", {
feeLevel: feeLevel
@ -45,34 +46,35 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
});
}
var feeRate = feeLevelRate.feePerKB;
var feeRate = feeLevelRate.feePerKb;
if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKB / 1000).toFixed() + ' SAT/B');
if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKb / 1000).toFixed() + ' SAT/B');
return cb(null, feeRate);
});
};
root.getCurrentFeeRate = function(network, cb) {
return root.getFeeRate(network, root.getCurrentFeeLevel(), cb);
root.getCurrentFeeRate = function(coin, network, cb) {
return root.getFeeRate(coin, network, root.getCurrentFeeLevel(), cb);
};
root.getFeeLevels = function(cb) {
root.getFeeLevels = function(coin, cb) {
coin = coin || 'btc';
if (cache.updateTs > Date.now() - CACHE_TIME_TS * 1000) {
if (cache.coin == coin && cache.updateTs > Date.now() - CACHE_TIME_TS * 1000) {
return cb(null, cache.data, true);
}
var walletClient = bwcService.getClient();
var unitName = configService.getSync().wallet.settings.unitName;
walletClient.getFeeLevels('livenet', function(errLivenet, levelsLivenet) {
walletClient.getFeeLevels('testnet', function(errTestnet, levelsTestnet) {
walletClient.getFeeLevels(coin, 'livenet', function(errLivenet, levelsLivenet) {
walletClient.getFeeLevels('btc', 'testnet', function(errTestnet, levelsTestnet) {
if (errLivenet || errTestnet) {
return cb(gettextCatalog.getString('Could not get dynamic fee'));
}
cache.updateTs = Date.now();
cache.coin = coin;
cache.data = {
'livenet': levelsLivenet,
'testnet': levelsTestnet

View file

@ -34,6 +34,7 @@ angular.module('copayApp.services')
};
root.add = function(level, msg) {
msg = msg.replace('/xpriv.*/', 'xpriv[Hidden]');
logs.push({
timestamp: new Date().toISOString(),
level: level,

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog) {
angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog) {
var root = {};
@ -46,7 +46,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
return true;
}
function goSend(addr, amount, message) {
function goSend(addr, amount, message, coin) {
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
@ -57,18 +57,20 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
$state.transitionTo('tabs.send.confirm', {
toAmount: amount,
toAddress: addr,
description: message
description: message,
coin: coin
});
} else {
$state.transitionTo('tabs.send.amount', {
toAddress: addr
toAddress: addr,
coin: coin
});
}
}, 100);
}
// data extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin:\?r=[\w+]/).exec(data)) {
data = decodeURIComponent(data.replace('bitcoin:?r=', ''));
if ((/^bitcoin(cash)?:\?r=[\w+]/).exec(data)) {
data = decodeURIComponent(data.replace(/bitcoin(cash)?:\?r=/, ''));
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
@ -82,8 +84,9 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
data = sanitizeUri(data);
// BIP21
// Bitcoin URL
if (bitcore.URI.isValid(data)) {
var coin = 'btc';
var parsed = new bitcore.URI(data);
var addr = parsed.address ? parsed.address.toString() : '';
@ -94,15 +97,84 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
if (parsed.r) {
payproService.getPayProDetails(parsed.r, function(err, details) {
if (err) {
if (addr && amount) goSend(addr, amount, message);
if (addr && amount) goSend(addr, amount, message, coin);
else popupService.showAlert(gettextCatalog.getString('Error'), err);
} else handlePayPro(details);
});
} else {
goSend(addr, amount, message);
goSend(addr, amount, message, coin);
}
return true;
// Cash URI
} else if (bitcoreCash.URI.isValid(data)) {
var coin = 'bch';
var parsed = new bitcoreCash.URI(data);
var addr = parsed.address ? parsed.address.toString() : '';
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
// paypro not yet supported on cash
if (parsed.r) {
payproService.getPayProDetails(parsed.r, function(err, details) {
if (err) {
if (addr && amount)
goSend(addr, amount, message, coin);
else
popupService.showAlert(gettextCatalog.getString('Error'), err);
}
handlePayPro(details, coin);
});
} else {
goSend(addr, amount, message, coin);
}
return true;
// Cash URI with bitcoin core address version number?
} else if (bitcore.URI.isValid(data.replace(/^bitcoincash:/,'bitcoin:'))) {
$log.debug('Handling bitcoincash URI with legacy address');
var coin = 'bch';
var parsed = new bitcore.URI(data.replace(/^bitcoincash:/,'bitcoin:'));
var oldAddr = parsed.address ? parsed.address.toString() : '';
if (!oldAddr) return false;
var addr = '';
var a = bitcore.Address(oldAddr).toObject();
addr = bitcoreCash.Address.fromObject(a).toString();
// Translate address
$log.debug('address transalated to:' + addr);
popupService.showConfirm(
gettextCatalog.getString('Bitcoin cash Payment'),
gettextCatalog.getString('Payment address was translated to new Bitcoin Cash address format: ' + addr),
gettextCatalog.getString('OK'),
gettextCatalog.getString('Cancel'),
function(ret) {
if (!ret) return false;
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
// paypro not yet supported on cash
if (parsed.r) {
payproService.getPayProDetails(parsed.r, function(err, details) {
if (err) {
if (addr && amount)
goSend(addr, amount, message, coin);
else
popupService.showAlert(gettextCatalog.getString('Error'), err);
}
handlePayPro(details, coin);
});
} else {
goSend(addr, amount, message, coin);
}
}
);
return true;
// Plain URL
} else if (/^https?:\/\//.test(data)) {
@ -127,6 +199,16 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
} else {
goToAmountPage(data);
}
} else if (bitcoreCash.Address.isValid(data, 'livenet')) {
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'bitcoinAddress',
coin: 'bch',
});
} else {
goToAmountPage(data, 'bch');
}
} else if (data && data.indexOf(appConfigService.name + '://glidera') === 0) {
var code = getParameterByName('code', data);
$ionicHistory.nextViewOptions({
@ -236,29 +318,29 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
});
}
}
return false;
};
function goToAmountPage(toAddress) {
function goToAmountPage(toAddress, coin) {
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
});
$timeout(function() {
$state.transitionTo('tabs.send.amount', {
toAddress: toAddress
toAddress: toAddress,
coin: coin,
});
}, 100);
}
function handlePayPro(payProDetails) {
function handlePayPro(payProDetails, coin) {
var stateParams = {
toAmount: payProDetails.amount,
toAddress: payProDetails.toAddress,
description: payProDetails.memo,
paypro: payProDetails
paypro: payProDetails,
coin: coin,
};
scannerService.pausePreview();
$state.go('tabs.send', {}, {

View file

@ -0,0 +1,183 @@
'use strict';
angular.module('copayApp.services').factory('mercadoLibreService', function($http, $log, lodash, moment, storageService, configService, platformInfo, nextStepsService, homeIntegrationsService) {
var root = {};
var credentials = {};
// Not used yet
var availableCountries = [{
'country': 'Brazil',
'currency': 'BRL',
'name': 'Mercado Livre',
'url': 'https://www.mercadolivre.com.br'
}];
/*
* Development: 'testnet'
* Production: 'livenet'
*/
credentials.NETWORK = 'livenet';
//credentials.NETWORK = 'testnet';
if (credentials.NETWORK == 'testnet') {
credentials.BITPAY_API_URL = "https://test.bitpay.com";
} else {
credentials.BITPAY_API_URL = "https://bitpay.com";
};
var homeItem = {
name: 'mercadoLibre',
title: 'Vales-Presente do Mercado Livre Brasil',
icon: 'icon-ml',
sref: 'tabs.giftcards.mercadoLibre',
};
var nextStepItem = {
name: 'mercadoLibre',
title: 'Comprar um Vale-Presente Mercado Livre',
icon: 'icon-ml',
sref: 'tabs.giftcards.mercadoLibre',
};
var _getBitPay = function(endpoint) {
return {
method: 'GET',
url: credentials.BITPAY_API_URL + endpoint,
headers: {
'content-type': 'application/json'
}
};
};
var _postBitPay = function(endpoint, data) {
return {
method: 'POST',
url: credentials.BITPAY_API_URL + endpoint,
headers: {
'content-type': 'application/json'
},
data: data
};
};
root.getNetwork = function() {
return credentials.NETWORK;
};
root.savePendingGiftCard = function(gc, opts, cb) {
var network = root.getNetwork();
storageService.getMercadoLibreGiftCards(network, function(err, oldGiftCards) {
if (lodash.isString(oldGiftCards)) {
oldGiftCards = JSON.parse(oldGiftCards);
}
if (lodash.isString(gc)) {
gc = JSON.parse(gc);
}
var inv = oldGiftCards || {};
inv[gc.invoiceId] = gc;
if (opts && (opts.error || opts.status)) {
inv[gc.invoiceId] = lodash.assign(inv[gc.invoiceId], opts);
}
if (opts && opts.remove) {
delete(inv[gc.invoiceId]);
}
inv = JSON.stringify(inv);
storageService.setMercadoLibreGiftCards(network, inv, function(err) {
homeIntegrationsService.register(homeItem);
nextStepsService.unregister(nextStepItem.name);
return cb(err);
});
});
};
root.getPendingGiftCards = function(cb) {
var network = root.getNetwork();
storageService.getMercadoLibreGiftCards(network, function(err, giftCards) {
var _gcds = giftCards ? JSON.parse(giftCards) : null;
return cb(err, _gcds);
});
};
root.createBitPayInvoice = function(data, cb) {
var dataSrc = {
currency: data.currency,
amount: data.amount,
clientId: data.uuid
};
$http(_postBitPay('/mercado-libre-gift/pay', dataSrc)).then(function(data) {
$log.info('BitPay Create Invoice: SUCCESS');
return cb(null, data.data);
}, function(data) {
$log.error('BitPay Create Invoice: ERROR', JSON.stringify(data.data));
return cb(data.data);
});
};
root.getBitPayInvoice = function(id, cb) {
$http(_getBitPay('/invoices/' + id)).then(function(data) {
$log.info('BitPay Get Invoice: SUCCESS');
return cb(null, data.data.data);
}, function(data) {
$log.error('BitPay Get Invoice: ERROR', JSON.stringify(data.data));
return cb(data.data);
});
};
root.createGiftCard = function(data, cb) {
var dataSrc = {
"clientId": data.uuid,
"invoiceId": data.invoiceId,
"accessKey": data.accessKey
};
$http(_postBitPay('/mercado-libre-gift/redeem', dataSrc)).then(function(data) {
var status = data.data.status == 'new' ? 'PENDING' : (data.data.status == 'paid') ? 'PENDING' : data.data.status;
data.data.status = status;
$log.info('Mercado Libre Gift Card Create/Update: ' + status);
return cb(null, data.data);
}, function(data) {
$log.error('Mercado Libre Gift Card Create/Update: ERROR', JSON.stringify(data.data));
return cb(data.data);
});
};
/*
* Disabled for now *
*/
/*
root.cancelGiftCard = function(data, cb) {
var dataSrc = {
"clientId": data.uuid,
"invoiceId": data.invoiceId,
"accessKey": data.accessKey
};
$http(_postBitPay('/mercado-libre-gift/cancel', dataSrc)).then(function(data) {
$log.info('Mercado Libre Gift Card Cancel: SUCCESS');
return cb(null, data.data);
}, function(data) {
$log.error('Mercado Libre Gift Card Cancel: ' + data.data.message);
return cb(data.data);
});
};
*/
var register = function() {
storageService.getMercadoLibreGiftCards(root.getNetwork(), function(err, giftCards) {
if (giftCards) {
homeIntegrationsService.register(homeItem);
} else {
nextStepsService.register(nextStepItem);
}
});
};
// Hide Mercado Libre
// register();
return root;
});

View file

@ -45,7 +45,8 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
'cancelingGiftCard': 'Canceling Gift Card...',
'creatingGiftCard': 'Creating Gift Card...',
'buyingGiftCard': 'Buying Gift Card...',
'topup': gettext('Top up in progress...')
'topup': gettext('Top up in progress...'),
'duplicatingWallet': gettext('Duplicating wallet...'),
};
root.clear = function() {

View file

@ -57,10 +57,10 @@ angular.module('copayApp.services').factory('openURLService', function($rootScop
// This event is sent to an existent instance of Copay (only for standalone apps)
gui.App.on('open', function(pathData) {
if (pathData.indexOf('bitcoin:') != -1) {
if (pathData.indexOf(/^bitcoin(cash)?:/) != -1) {
$log.debug('Bitcoin URL found');
handleOpenURL({
url: pathData.substring(pathData.indexOf('bitcoin:'))
url: pathData.substring(pathData.indexOf(/^bitcoin(cash)?:/))
});
} else if (pathData.indexOf(appConfigService.name + '://') != -1) {
$log.debug(appConfigService.name + ' URL found');
@ -84,6 +84,7 @@ angular.module('copayApp.services').factory('openURLService', function($rootScop
if (navigator.registerProtocolHandler) {
$log.debug('Registering Browser handlers base:' + base);
navigator.registerProtocolHandler('bitcoin', url, 'Copay Bitcoin Handler');
navigator.registerProtocolHandler('web+bitcoincash', url, 'Copay Bitcoin Cash Handler');
navigator.registerProtocolHandler('web+copay', url, 'Copay Wallet Handler');
navigator.registerProtocolHandler('web+bitpay', url, 'BitPay Wallet Handler');
}

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services')
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, $state) {
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, $state, sjcl, lodash, storageService, bwcService, configService, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, appConfigService) {
var isChromeApp = platformInfo.isChromeApp;
@ -89,6 +89,7 @@ angular.module('copayApp.services')
wallet.copayerId = wallet.credentials.copayerId;
wallet.m = wallet.credentials.m;
wallet.n = wallet.credentials.n;
wallet.coin = wallet.credentials.coin;
root.updateWalletSettings(wallet);
root.wallet[walletId] = wallet;
@ -222,11 +223,12 @@ angular.module('copayApp.services')
return ((config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url);
};
var client = bwcService.getClient(JSON.stringify(credentials), {
bwsurl: getBWSURL(credentials.walletId),
});
var skipKeyValidation = shouldSkipValidation(credentials.walletId);
if (!skipKeyValidation)
root.runValidation(client, 500);
@ -328,6 +330,7 @@ angular.module('copayApp.services')
passphrase: opts.passphrase,
account: opts.account || 0,
derivationStrategy: opts.derivationStrategy || 'BIP44',
coin: opts.coin
});
} catch (ex) {
@ -336,7 +339,12 @@ angular.module('copayApp.services')
}
} else if (opts.extendedPrivateKey) {
try {
walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey);
walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey, {
network: network,
account: opts.account || 0,
derivationStrategy: opts.derivationStrategy || 'BIP44',
coin: opts.coin,
});
} catch (ex) {
$log.warn(ex);
return cb(gettextCatalog.getString('Could not create using the specified extended private key'));
@ -346,6 +354,7 @@ angular.module('copayApp.services')
walletClient.seedFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
account: opts.account || 0,
derivationStrategy: opts.derivationStrategy || 'BIP44',
coin: opts.coin
});
walletClient.credentials.hwInfo = opts.hwInfo;
} catch (ex) {
@ -360,6 +369,7 @@ angular.module('copayApp.services')
passphrase: opts.passphrase,
language: lang,
account: 0,
coin: opts.coin
});
} catch (e) {
$log.info('Error creating recovery phrase: ' + e.message);
@ -369,6 +379,7 @@ angular.module('copayApp.services')
network: network,
passphrase: opts.passphrase,
account: 0,
coin: opts.coin
});
} else {
return cb(e);
@ -380,7 +391,11 @@ angular.module('copayApp.services')
// Creates a wallet on BWC/BWS
var doCreateWallet = function(opts, cb) {
$log.debug('Creating Wallet:', opts);
var showOpts = lodash.clone(opts);
if (showOpts.extendedPrivateKey) showOpts.extendedPrivateKey='[hidden]';
if (showOpts.mnemonic) showOpts.mnemonic='[hidden]';
$log.debug('Creating Wallet:', showOpts);
$timeout(function() {
seedWallet(opts, function(err, walletClient) {
if (err) return cb(err);
@ -392,6 +407,7 @@ angular.module('copayApp.services')
network: opts.networkName,
singleAddress: opts.singleAddress,
walletPrivKey: opts.walletPrivKey,
coin: opts.coin
}, function(err, secret) {
if (err) return bwcError.cb(err, gettextCatalog.getString('Error creating wallet'), cb);
return cb(null, walletClient, secret);
@ -435,7 +451,9 @@ angular.module('copayApp.services')
seedWallet(opts, function(err, walletClient) {
if (err) return cb(err);
walletClient.joinWallet(opts.secret, opts.myName || 'me', {}, function(err) {
walletClient.joinWallet(opts.secret, opts.myName || 'me', {
coin: opts.coin
}, function(err) {
if (err) return bwcError.cb(err, gettextCatalog.getString('Could not join wallet'), cb);
addAndBindWalletClient(walletClient, {
bwsurl: opts.bwsurl
@ -495,7 +513,9 @@ angular.module('copayApp.services')
var walletId = client.credentials.walletId
if (!root.profile.addWallet(JSON.parse(client.export())))
return cb(gettextCatalog.getString('Wallet already in Copay'));
return cb(gettextCatalog.getString("Wallet already in {{appName}}", {
appName: appConfigService.nameCase
}));
var skipKeyValidation = shouldSkipValidation(walletId);
@ -621,6 +641,7 @@ angular.module('copayApp.services')
entropySourcePath: opts.entropySourcePath,
derivationStrategy: opts.derivationStrategy || 'BIP44',
account: opts.account || 0,
coin: opts.coin
}, function(err) {
if (err) {
if (err instanceof errors.NOT_AUTHORIZED)
@ -642,6 +663,7 @@ angular.module('copayApp.services')
walletClient.importFromExtendedPublicKey(opts.extendedPublicKey, opts.externalSource, opts.entropySource, {
account: opts.account || 0,
derivationStrategy: opts.derivationStrategy || 'BIP44',
coin: opts.coin
}, function(err) {
if (err) {
@ -682,6 +704,7 @@ angular.module('copayApp.services')
opts.m = 1;
opts.n = 1;
opts.networkName = 'livenet';
opts.coin = 'btc';
root.createWallet(opts, cb);
};
@ -747,6 +770,12 @@ angular.module('copayApp.services')
var ret = lodash.values(root.wallet);
if (opts.coin) {
ret = lodash.filter(ret, function(x) {
return (x.credentials.coin == opts.coin);
});
}
if (opts.network) {
ret = lodash.filter(ret, function(x) {
return (x.credentials.network == opts.network);
@ -767,12 +796,14 @@ angular.module('copayApp.services')
if (opts.hasFunds) {
ret = lodash.filter(ret, function(w) {
if (!w.status) return;
return (w.status.availableBalanceSat > 0);
});
}
if (opts.minAmount) {
ret = lodash.filter(ret, function(w) {
if (!w.status) return;
return (w.status.availableBalanceSat > opts.minAmount);
});
}
@ -857,7 +888,7 @@ angular.module('copayApp.services')
x.types = [x.type];
if (x.data && x.data.amount)
x.amountStr = txFormatService.formatAmountStr(x.data.amount);
x.amountStr = txFormatService.formatAmountStr(x.wallet.coin, x.data.amount);
x.action = function() {
// TODO?

View file

@ -25,9 +25,10 @@ var RateService = function(opts) {
self._isAvailable = false;
self._rates = {};
self._alternatives = [];
self._ratesBCH = {};
self._queued = [];
self._fetchCurrencies();
self.updateRates();
};
@ -39,14 +40,20 @@ RateService.singleton = function(opts) {
return _instance;
};
RateService.prototype._fetchCurrencies = function() {
RateService.prototype.updateRates = function() {
var self = this;
var backoffSeconds = 5;
var updateFrequencySeconds = 5 * 60;
var rateServiceUrl = 'https://bitpay.com/api/rates';
var bchRateServiceUrl = 'https://api.kraken.com/0/public/Ticker?pair=BCHUSD,BCHEUR';
function getBTC(cb, tries) {
tries = tries || 0;
if (!self.httprequest) return;
if (tries > 5) return cb('could not get BTC rates');
var retrieve = function() {
//log.info('Fetching exchange rates');
self.httprequest.get(rateServiceUrl).success(function(res) {
self.lodash.each(res, function(currency) {
@ -57,26 +64,63 @@ RateService.prototype._fetchCurrencies = function() {
rate: currency.rate
});
});
return cb();
}).error(function() {
//log.debug('Error fetching exchange rates', err);
setTimeout(function() {
backoffSeconds *= 1.5;
getBTC(cb, tries++);
}, backoffSeconds * 1000);
return;
})
}
function getBCH(cb, tries) {
tries = tries || 0;
if (!self.httprequest) return;
if (tries > 5) return cb('could not get BCH rates');
function retry(tries) {
//log.debug('Error fetching exchange rates', err);
setTimeout(function() {
backoffSeconds *= 1.5;
getBTC(cb, tries++);
}, backoffSeconds * 1000);
return;
}
self.httprequest.get(bchRateServiceUrl).success(function(res) {
self.lodash.each(res.result, function(data, paircode) {
var code = paircode.substr(3,3);
var rate =data.c[0];
self._ratesBCH[code] = rate;
})
return cb();
}).error(function() {
return retry(tries);
})
}
getBTC(function(err) {
if (err) return;
getBCH(function(err) {
if (err) return;
self._isAvailable = true;
self.lodash.each(self._queued, function(callback) {
setTimeout(callback, 1);
});
setTimeout(retrieve, updateFrequencySeconds * 1000);
}).error(function(err) {
//log.debug('Error fetching exchange rates', err);
setTimeout(function() {
backoffSeconds *= 1.5;
retrieve();
}, backoffSeconds * 1000);
return;
});
setTimeout( self.updateRates , updateFrequencySeconds * 1000);
})
})
};
retrieve();
};
RateService.prototype.getRate = function(code) {
RateService.prototype.getRate = function(code, chain) {
if (chain == 'bch')
return this._ratesBCH[code];
else
return this._rates[code];
};
@ -90,25 +134,25 @@ RateService.prototype.isAvailable = function() {
RateService.prototype.whenAvailable = function(callback) {
if (this.isAvailable()) {
setTimeout(callback, 1);
setTimeout(callback, 10);
} else {
this._queued.push(callback);
}
};
RateService.prototype.toFiat = function(satoshis, code) {
RateService.prototype.toFiat = function(satoshis, code, chain) {
if (!this.isAvailable()) {
return null;
}
return satoshis * this.SAT_TO_BTC * this.getRate(code);
return satoshis * this.SAT_TO_BTC * this.getRate(code, chain);
};
RateService.prototype.fromFiat = function(amount, code) {
RateService.prototype.fromFiat = function(amount, code, chain) {
if (!this.isAvailable()) {
return null;
}
return amount / this.getRate(code) * this.BTC_TO_SAT;
return amount / this.getRate(code, chain) * this.BTC_TO_SAT;
};
RateService.prototype.listAlternatives = function(sort) {

View file

@ -10,7 +10,7 @@ angular.module('copayApp.services').service('sendMaxService', function(feeServic
*
*/
this.getInfo = function(wallet, cb) {
feeService.getCurrentFeeRate(wallet.credentials.network, function(err, feePerKb) {
feeService.getCurrentFeeRate(wallet.coin, wallet.credentials.network, function(err, feePerKb) {
if (err) return cb(err);
var config = configService.getSync().wallet;

View file

@ -610,5 +610,17 @@ angular.module('copayApp.services')
storage.remove('txConfirmNotif-' + txid, cb);
};
root.setMercadoLibreGiftCards = function(network, gcs, cb) {
storage.set('mercadoLibreGiftCards-' + network, gcs, cb);
};
root.getMercadoLibreGiftCards = function(network, cb) {
storage.get('mercadoLibreGiftCards-' + network, cb);
};
root.removeMercadoLibreGiftCards = function(network, cb) {
storage.remove('MercadoLibreGiftCards-' + network, cb);
};
return root;
});

View file

@ -7,7 +7,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
root.formatAmount = function(satoshis, fullPrecision) {
var config = configService.getSync().wallet.settings;
var config = configService.getDefaults().wallet.settings;
if (config.unitCode == 'sat') return satoshis;
//TODO : now only works for english, specify opts to change thousand separator and decimal separator
@ -17,16 +17,15 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
return this.Utils.formatAmount(satoshis, config.unitCode, opts);
};
root.formatAmountStr = function(satoshis) {
root.formatAmountStr = function(coin, satoshis) {
if (isNaN(satoshis)) return;
var config = configService.getSync().wallet.settings;
return root.formatAmount(satoshis) + ' ' + config.unitName;
return root.formatAmount(satoshis) + ' ' + (coin).toUpperCase();
};
root.toFiat = function(satoshis, code, cb) {
root.toFiat = function(coin, satoshis, code, cb) {
if (isNaN(satoshis)) return;
var val = function() {
var v1 = rateService.toFiat(satoshis, code);
var v1 = rateService.toFiat(satoshis, code, coin);
if (!v1) return null;
return v1.toFixed(2);
@ -43,10 +42,10 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
};
};
root.formatToUSD = function(satoshis, cb) {
root.formatToUSD = function(coin, satoshis, cb) {
if (isNaN(satoshis)) return;
var val = function() {
var v1 = rateService.toFiat(satoshis, 'USD');
var v1 = rateService.toFiat(satoshis, 'USD', coin);
if (!v1) return null;
return v1.toFixed(2);
@ -63,12 +62,12 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
};
};
root.formatAlternativeStr = function(satoshis, cb) {
root.formatAlternativeStr = function(coin, satoshis, cb) {
if (isNaN(satoshis)) return;
var config = configService.getSync().wallet.settings;
var val = function() {
var v1 = parseFloat((rateService.toFiat(satoshis, config.alternativeIsoCode)).toFixed(2));
var v1 = parseFloat((rateService.toFiat(satoshis, config.alternativeIsoCode, coin)).toFixed(2));
v1 = $filter('formatFiatAmount')(v1);
if (!v1) return null;
@ -86,7 +85,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
};
};
root.processTx = function(tx) {
root.processTx = function(coin, tx) {
if (!tx || tx.action == 'invalid')
return tx;
@ -101,17 +100,17 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
tx.hasMultiplesOutputs = true;
}
tx.amount = lodash.reduce(tx.outputs, function(total, o) {
o.amountStr = root.formatAmountStr(o.amount);
o.alternativeAmountStr = root.formatAlternativeStr(o.amount);
o.amountStr = root.formatAmountStr(coin, o.amount);
o.alternativeAmountStr = root.formatAlternativeStr(coin, o.amount);
return total + o.amount;
}, 0);
}
tx.toAddress = tx.outputs[0].toAddress;
}
tx.amountStr = root.formatAmountStr(tx.amount);
tx.alternativeAmountStr = root.formatAlternativeStr(tx.amount);
tx.feeStr = root.formatAmountStr(tx.fee || tx.fees);
tx.amountStr = root.formatAmountStr(coin, tx.amount);
tx.alternativeAmountStr = root.formatAlternativeStr(coin, tx.amount);
tx.feeStr = root.formatAmountStr(coin, tx.fee || tx.fees);
if (tx.amountStr) {
tx.amountValueStr = tx.amountStr.split(' ')[0];
@ -145,8 +144,6 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
lodash.each(txps, function(tx) {
tx = txFormatService.processTx(tx);
// no future transactions...
if (tx.createdOn > now)
tx.createdOn = now;
@ -157,6 +154,8 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
return;
}
tx = txFormatService.processTx(tx.wallet.coin, tx);
var action = lodash.find(tx.actions, {
copayerId: tx.wallet.copayerId
});
@ -180,7 +179,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
return txps;
};
root.parseAmount = function(amount, currency) {
root.parseAmount = function(coin, amount, currency) {
var config = configService.getSync().wallet.settings;
var satToBtc = 1 / 100000000;
var unitToSatoshi = config.unitToSatoshi;
@ -189,21 +188,21 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
var alternativeIsoCode = config.alternativeIsoCode;
// If fiat currency
if (currency != 'bits' && currency != 'BTC' && currency != 'sat') {
if (currency != 'BCH' && currency != 'BTC' && currency != 'sat') {
amountUnitStr = $filter('formatFiatAmount')(amount) + ' ' + currency;
amountSat = rateService.fromFiat(amount, currency).toFixed(0);
amountSat = rateService.fromFiat(amount, currency, coin).toFixed(0);
} else if (currency == 'sat') {
amountSat = amount;
amountUnitStr = root.formatAmountStr(amountSat);
// convert sat to BTC
amountUnitStr = root.formatAmountStr(coin, amountSat);
// convert sat to BTC or BCH
amount = (amountSat * satToBtc).toFixed(8);
currency = 'BTC';
currency = (coin).toUpperCase();
} else {
amountSat = parseInt((amount * unitToSatoshi).toFixed(0));
amountUnitStr = root.formatAmountStr(amountSat);
// convert unit to BTC
amountUnitStr = root.formatAmountStr(coin, amountSat);
// convert unit to BTC or BCH
amount = (amountSat * satToBtc).toFixed(8);
currency = 'BTC';
currency = (coin).toUpperCase();
}
return {

View file

@ -17,6 +17,9 @@ angular.module('copayApp.services')
}, {
name: 'Italiano',
isoCode: 'it',
}, {
name: 'Nederlands',
isoCode: 'nl',
}, {
name: 'Polski',
isoCode: 'pl',

View file

@ -104,7 +104,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.getStatus = function(wallet, opts, cb) {
opts = opts || {};
var walletId = wallet.id;
function processPendingTxps(status) {
var txps = status.pendingTxps;
@ -130,7 +130,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
lodash.each(txps, function(tx) {
tx = txFormatService.processTx(tx);
tx = txFormatService.processTx(wallet.coin, tx);
// no future transactions...
if (tx.createdOn > now)
@ -213,14 +213,13 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// Selected unit
cache.unitToSatoshi = config.settings.unitToSatoshi;
cache.satToUnit = 1 / cache.unitToSatoshi;
cache.unitName = config.settings.unitName;
//STR
cache.totalBalanceStr = txFormatService.formatAmount(cache.totalBalanceSat) + ' ' + cache.unitName;
cache.lockedBalanceStr = txFormatService.formatAmount(cache.lockedBalanceSat) + ' ' + cache.unitName;
cache.availableBalanceStr = txFormatService.formatAmount(cache.availableBalanceSat) + ' ' + cache.unitName;
cache.spendableBalanceStr = txFormatService.formatAmount(cache.spendableAmount) + ' ' + cache.unitName;
cache.pendingBalanceStr = txFormatService.formatAmount(cache.pendingAmount) + ' ' + cache.unitName;
cache.totalBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.totalBalanceSat);
cache.lockedBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.lockedBalanceSat);
cache.availableBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.availableBalanceSat);
cache.spendableBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.spendableAmount);
cache.pendingBalanceStr = txFormatService.formatAmountStr(wallet.coin, cache.pendingAmount);
cache.alternativeName = config.settings.alternativeName;
cache.alternativeIsoCode = config.settings.alternativeIsoCode;
@ -238,11 +237,11 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
rateService.whenAvailable(function() {
var totalBalanceAlternative = rateService.toFiat(cache.totalBalanceSat, cache.alternativeIsoCode);
var pendingBalanceAlternative = rateService.toFiat(cache.pendingAmount, cache.alternativeIsoCode);
var lockedBalanceAlternative = rateService.toFiat(cache.lockedBalanceSat, cache.alternativeIsoCode);
var spendableBalanceAlternative = rateService.toFiat(cache.spendableAmount, cache.alternativeIsoCode);
var alternativeConversionRate = rateService.toFiat(100000000, cache.alternativeIsoCode);
var totalBalanceAlternative = rateService.toFiat(cache.totalBalanceSat, cache.alternativeIsoCode, wallet.coin);
var pendingBalanceAlternative = rateService.toFiat(cache.pendingAmount, cache.alternativeIsoCode, wallet.coin);
var lockedBalanceAlternative = rateService.toFiat(cache.lockedBalanceSat, cache.alternativeIsoCode, wallet.coin);
var spendableBalanceAlternative = rateService.toFiat(cache.spendableAmount, cache.alternativeIsoCode, wallet.coin);
var alternativeConversionRate = rateService.toFiat(100000000, cache.alternativeIsoCode, wallet.coin);
cache.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative);
cache.pendingBalanceAlternative = $filter('formatFiatAmount')(pendingBalanceAlternative);
@ -260,6 +259,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
function cacheStatus(status) {
if (status.wallet && status.wallet.scanStatus == 'running') return;
wallet.cachedStatus = status ||  {};
var cache = wallet.cachedStatus;
cache.statusUpdatedOn = Date.now();
@ -304,6 +305,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
cacheStatus(status);
wallet.scanning = status.wallet && status.wallet.scanStatus == 'running';
return cb(null, status);
});
};
@ -366,7 +369,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
wallet.hasUnsafeConfirmed = false;
lodash.each(txs, function(tx) {
tx = txFormatService.processTx(tx);
tx = txFormatService.processTx(wallet.coin, tx);
// no future transactions...
if (tx.time > now)
@ -400,7 +403,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
var LIMIT = 50;
var requestLimit = FIRST_LIMIT;
var walletId = wallet.credentials.walletId;
var config = configService.getSync().wallet.settings;
var opts = opts || {};
var progressFn = opts.progressFn || function() {};
@ -414,18 +416,16 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
var fixTxsUnit = function(txs) {
if (!txs || !txs[0] || !txs[0].amountStr) return;
var cacheUnit = txs[0].amountStr.split(' ')[1];
var cacheCoin = txs[0].amountStr.split(' ')[1];
if (cacheUnit == config.unitName)
return;
if (cacheCoin == 'bits') {
var name = ' ' + config.unitName;
$log.debug('Fixing Tx Cache Unit to:' + name)
$log.debug('Fixing Tx Cache Unit to: ' + wallet.coin)
lodash.each(txs, function(tx) {
tx.amountStr = txFormatService.formatAmount(tx.amount) + name;
tx.feeStr = txFormatService.formatAmount(tx.fees) + name;
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.amount);
tx.feeStr = txFormatService.formatAmountStr(wallet.coin, tx.fees);
});
}
};
getSavedTxs(walletId, function(err, txsFromLocal) {
@ -788,7 +788,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
//prefs.email (may come from arguments)
prefs.email = config.emailNotifications.email;
prefs.language = uxLanguage.getCurrentLanguage();
prefs.unit = walletSettings.unitCode;
// prefs.unit = walletSettings.unitCode; // TODO: remove, not used
updateRemotePreferencesFor(lodash.clone(clients), prefs, function(err) {
if (err) return cb(err);
@ -820,13 +820,10 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
$log.debug('Scanning wallet ' + wallet.id);
if (!wallet.isComplete()) return;
wallet.updating = true;
ongoingProcess.set('scanning', true);
wallet.scanning = true;
wallet.startScan({
includeCopayerBranches: true,
}, function(err) {
wallet.updating = false;
ongoingProcess.set('scanning', false);
return cb(err);
});
};
@ -926,7 +923,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.getMinFee = function(wallet, feeLevels, nbOutputs) {
var lowLevelRate = (lodash.find(feeLevels[wallet.network], {
level: 'normal',
}).feePerKB / 1000).toFixed(0);
}).feePerKb / 1000).toFixed(0);
var size = root.getEstimatedTxSize(wallet, nbOutputs);
return size * lowLevelRate;
@ -935,15 +932,17 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// Approx utxo amount, from which the uxto is economically redeemable
root.getLowAmount = function(wallet, feeLevels, nbOutputs) {
var minFee = root.getMinFee(wallet,feeLevels, nbOutputs);
return parseInt( minFee / LOW_AMOUNT_RATIO);
var minFee = root.getMinFee(wallet, feeLevels, nbOutputs);
return parseInt(minFee / LOW_AMOUNT_RATIO);
};
root.getLowUtxos = function(wallet, levels, cb) {
wallet.getUtxos({}, function(err, resp) {
wallet.getUtxos({
coin: wallet.coin
}, function(err, resp) {
if (err || !resp || !resp.length) return cb();
var minFee = root.getMinFee(wallet, levels, resp.length);
@ -1236,5 +1235,38 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.getProtocolHandler = function(wallet) {
if (wallet.coin== 'bch') return 'bitcoincash';
else return 'bitcoin';
}
root.copyCopayers = function(wallet, newWallet, cb) {
var c = wallet.credentials;
var walletPrivKey = bitcore.PrivateKey.fromString(c.walletPrivKey);
var copayer = 1,
i = 0,
l = c.publicKeyRing.length;
var mainErr = null;
lodash.each(c.publicKeyRing, function(item) {
var name = item.copayerName || ('copayer ' + copayer++);
newWallet._doJoinWallet(newWallet.credentials.walletId, walletPrivKey, item.xPubKey, item.requestPubKey, name, {
coin: newWallet.credentials.coin,
}, function(err) {
//Ignore error is copayer already in wallet
if (err && !(err instanceof errors.COPAYER_IN_WALLET)) {
mainErr = err;
}
if (++i == l) {
return cb(mainErr);
}
});
});
};
return root;
});

View file

@ -139,6 +139,19 @@
}
}
.amount {
.icon-toggle {
font-size: 1.2em;
width: auto;
margin: 0.8em auto;
border: 1px solid $v-subtle-gray;
color: $v-dark-gray;
border-radius: 3px;
padding: 0 10px;
cursor: pointer;
@media(max-height: 280px) {
margin: 0.1em auto;
}
}
&__editable {
&--minimize {
font-size: 22px;
@ -187,7 +200,7 @@
&__result {
color: $v-light-gray;
font-size: .9em;
margin-bottom: -.9em;
//margin-bottom: -.9em; TODO matias
line-height: 1;
@media(max-height: 480px) {
margin-bottom: 0;
@ -196,7 +209,6 @@
&__result-equiv {
color: $v-mid-gray;
font-size: 1.2em;
margin-top: 2rem;
@media(max-height: 480px) {
margin-top: 0;
font-size: 16px;

View file

@ -0,0 +1,43 @@
#cash-scan {
.comment {
color: #667;
font-size: 0.9em;
}
.item {
color: $v-dark-gray;
padding-top: 1.3rem;
padding-bottom: 1.3rem;
}
.heading {
font-size: 17px;
color: $v-dark-gray;
margin: 1rem 0;
padding-top: 5px;
padding-bottom: 5px;
border: none;
}
.text-disabled {
color: $v-light-gray;
}
.supported {
display: flex;
.wallet-content {
padding-left: 7px;
}
}
.duplicate-button {
position: absolute;
right: 15px;
padding-top: .5rem;
}
a {
cursor: pointer;
}
}

View file

@ -99,6 +99,9 @@
i {
padding-right: 20px;
}
span {
display: inline;
}
}
}
.toggle-unconfirmed {

View file

@ -7,6 +7,17 @@ wallet-selector {
padding-right: .75rem;
}
.subheader {
margin: 20px 0 10px 0;
font-weight: bold;
padding-bottom: 10px;
border-bottom: 1px solid #EFEFEF;
.wallet-coin-logo {
vertical-align: middle;
margin-right: 5px;
}
}
.wallet-selector {
.wallet {
border: 0;

View file

@ -1,6 +1,7 @@
@import "coinbase";
@import "glidera";
@import "amazon";
@import "mercadolibre";
#coinbase, #glidera {
.button-small {

View file

@ -0,0 +1,202 @@
#mercadolibre {
$item-lateral-padding: 20px;
$item-vertical-padding: 10px;
$item-border-color: #EFEFEF;
$item-label-color: #6C6C6E;
@extend .deflash-blue;
.icon-amazon {
background-image: url("../img/mercado-libre/icon-ml.svg");
}
.spinner svg {
stroke: black;
fill: black;
}
.add-bottom-for-cta {
bottom: 92px;
}
.head {
padding: 30px $item-lateral-padding 4rem;
border-top: 0;
.sending-label {
display: flex;
font-size: 18px;
align-items: center;
margin-bottom: 1.8rem;
img {
margin-right: 1rem;
height: 35px;
width: 35px;
}
span {
text-transform: capitalize;
}
.big-icon-svg {
padding: 0 7px 0 0;
margin-right: 0.6rem;
}
.big-icon-svg > .bg {
height: 28px;
box-shadow: none;
}
}
.amount-label{
line-height: 30px;
.amount{
font-size: 38px;
margin-bottom: .5rem;
> .unit {
font-family: "Roboto-Light";
}
}
.alternative {
font-size: 12px;
font-family: "Roboto-Light";
color: #9B9B9B;
}
}
}
.item {
border-color: $item-border-color;
}
.info {
.badge {
border-radius: 0;
padding: .5rem;
}
.item {
color: #4A4A4A;
padding-top: $item-vertical-padding;
padding-bottom: $item-vertical-padding;
padding-left: $item-lateral-padding;
&:not(.item-icon-right) {
padding-right: $item-lateral-padding;
}
.label {
font-size: 14px;
color: $item-label-color;
margin-bottom: 8px;
}
.capitalized {
text-transform: capitalize;
}
.wallet .big-icon-svg > .bg {
height: 24px;
width: 24px;
padding: 2px;
box-shadow: none;
vertical-align: middle;
}
.total-amount {
font-weight: bold;
}
&.single-line {
display: flex;
align-items: center;
padding-top: 17px;
padding-bottom: 17px;
.label {
margin: 0;
flex-grow: 1;
}
}
}
.item-divider {
padding-top: 1.2rem;
color: $item-label-color;
font-size: 15px;
}
.wallet {
display: flex;
align-items: center;
padding: .2rem 0;
margin-bottom: 5px;
~ .bp-arrow-right {
top: 14px;
}
> i {
padding: 0;
position: static;
> img {
height: 24px;
width: 24px;
padding: 2px;
margin-right: .7rem;
box-shadow: none;
}
}
}
}
}
#meli-list-cards {
img.item-logo {
width: auto;
height: auto;
border-radius: 0;
}
}
#meli-card {
.card-head {
margin: 20px 0;
text-align: center;
.date {
font-size: 12px;
margin: 10px 0;
}
.amount {
font-size: 16px;
font-weight: bold;
}
}
.card-status {
text-align: center;
margin-bottom: 25px;
.card-status-desc {
margin-top: 5px;
font-size: 12px;
color: $v-text-secondary-color;
}
.redeem-pin {
font-weight: bold;
font-size: 22px;
}
.button-redeem {
margin-top: 10px;
background: transparent;
border: none;
font-size: 12px;
color: $v-text-accent-color;
}
}
.card-remove {
text-align: center;
margin-top: 30px;
.button-remove {
margin-top: 10px;
background: transparent;
border: none;
font-size: 12px;
color: red;
}
}
}

View file

@ -17,6 +17,11 @@
.icon-amazon {
background-image: url("../img/icon-amazon.svg");
}
.icon-ml {
background-image: url("../img/mercado-libre/icon-ml.svg");
background-position: center;
background-size: 85%;
}
.bg {
&.wallet {
padding: .25rem
@ -54,6 +59,10 @@
}
}
}
.wallet-coin-logo {
vertical-align: middle;
margin-right: 5px;
}
.wallet-details__item.item {
padding-top: 0;
padding-bottom: 0;

View file

@ -20,6 +20,12 @@
.has-comment {
border-bottom: 0 none;
}
.scan-label {
cursor: pointer;
cursor: hand;
color: $link-color;
font-weight: bold;
}
.comment {
padding: 15px;
background-color: #fff;
@ -34,6 +40,9 @@
width: 20px;
}
}
a {
cursor: pointer;
}
}
&-explanation, &-button-group {
padding: 0 1rem;
@ -137,6 +146,14 @@
.log-level {
font-weight: bold;
}
.alt-currency-radio {
.item-content {
padding-right: 16px;
}
.radio-icon {
display: none;
}
}
}
#tab-settings {
@ -149,6 +166,13 @@
border-radius: 0;
box-shadow: none;
}
& > .bch {
background-color: #9b9bab;
border-radius: 1rem;
}
& > .bch-enabled {
background-color: #ff9900 !important;
}
&.circle{
left:8px;
.bg {

View file

@ -50,3 +50,4 @@
@import "includes/pin";
@import "includes/logOptions";
@import "includes/checkBar";
@import "cashScan";

View file

@ -1,4 +1,22 @@
#wallet-backup-phrase {
.comment {
color: #667;
font-size: 0.9em;
}
.item {
color: $v-dark-gray;
padding-top: 1.3rem;
padding-bottom: 1.3rem;
border: none;
}
.heading {
font-size: 17px;
color: $v-dark-gray;
margin: 1rem 0;
padding-top: 5px;
padding-bottom: 5px;
border: none;
}
h3 {
padding: 15px;
}

View file

@ -88,6 +88,27 @@
padding: 1rem;
background: #f8f8f9;
}
&__no-transaction {
color: $v-mid-gray;
font-size: 12.5px;
text-align: center;
padding-top: 2rem;
}
&__no-update-history {
color: $v-error-color;
font-size: 12.5px;
text-align: center;
padding-top: 2rem;
}
&__updating-history {
color: $v-mid-gray;
font-size: 12.5px;
text-align: center;
padding-top: 1rem;
}
}
#walletDetails {
@ -177,10 +198,6 @@
align-items: center;
justify-content: center;
&.collapsible {
margin-bottom: 10px;
}
&__balance {
-webkit-transform: scale3d(1, 1, 1) translateY(45px);
transform: scale3d(1, 1, 1) translateY(45px);
@ -198,8 +215,21 @@
&__button-balance {
background-color: transparent;
border: 1px solid rgba(255,255,255,0.25);
margin-top: 10px;
i.icon {
margin-right: 7px;
vertical-align: middle;
}
}
&__error {
font-size: 14px;
padding: 35px 20px;
}
}
.no-alternative {
padding-top: 45px;
}
.item.item-footer {
font-weight: lighter;
@ -237,6 +267,17 @@
font-size: 20px;
color: #fff;
width:95%;
.actions{
float: right;
a {
color: white;
font-size: 14px;
cursor: pointer;
padding: 5px;
line-height: 16px;
}
}
}
.wallet-not-backed-up-warning {

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="49px" height="33px" viewBox="0 0 49 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 4</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Home/Overview" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-4" transform="translate(0.000000, -6.000000)" fill-rule="nonzero">
<g id="element-icon-bitcoin" transform="translate(24.500000, 22.000000) rotate(-26.000000) translate(-24.500000, -22.000000) translate(8.000000, 6.000000)">
<g id="bitcoin" transform="translate(0.000000, -0.000000)">
<path d="M32.1867932,19.8608311 C30.0167764,28.4289206 21.2022608,33.6584288 12.4778868,31.5222983 C3.77643011,29.3856664 -1.5352984,20.7067588 0.634718392,12.1386692 C2.80422591,3.57108109 11.6192507,-1.65842708 20.3207074,0.477703406 C29.0450815,2.59177057 34.3563007,11.2932429 32.1867932,19.8608311" id="Shape" fill="#09C286"></path>
<path d="M20.8680125,13.6901518 C20.3834958,15.7120375 17.1993056,14.7348781 16.1611297,14.4850856 L17.0381466,10.8958545 C18.0529285,11.1456469 21.3754035,11.5776651 20.8680125,13.6901518 L20.8680125,13.6901518 Z M20.3601017,19.4829825 C19.8298366,21.7091043 15.9994508,20.5507429 14.7767218,20.255394 L15.7228811,16.3027353 C16.9684843,16.5980842 20.9137609,17.1432255 20.3601017,19.4829825 Z M24.4670572,13.6901518 C24.7670209,11.5090745 23.0826493,10.3732354 20.7754761,9.60082384 L21.4908141,6.64784689 L19.6681577,6.21634063 L18.9762137,9.1012389 C18.4916969,8.98760381 18.0071801,8.87396871 17.5226634,8.78336775 L18.2146074,5.89795761 L16.3690768,5.46645136 L15.6537387,8.41994018 C15.2612385,8.32882735 14.8692582,8.26074867 14.4767581,8.16963584 L11.9621576,7.57944995 L11.500515,9.48770061 C11.500515,9.48770061 12.8620487,9.78253763 12.8157805,9.80557177 C13.5539927,9.98728555 13.6922776,10.4643482 13.6694034,10.8277758 L12.8620487,14.1897367 C12.9077971,14.2127709 12.9774594,14.2127709 13.0466018,14.2578154 C12.9774594,14.2352931 12.9311911,14.2352931 12.8620487,14.2127709 L11.7542105,18.914807 C11.6621939,19.1420772 11.4313726,19.4599483 10.9239817,19.3468251 C10.9468558,19.3693474 9.58584199,19.0284421 9.58584199,19.0284421 L8.70882504,21.0958842 L11.0851407,21.6410256 C11.5239091,21.7546607 11.9621576,21.8452617 12.377532,21.9588968 L11.6621939,24.9349079 L13.4848503,25.3664141 L14.2001884,22.4134371 C14.7075794,22.5495945 15.1920961,22.6632296 15.6537387,22.7768647 L14.9384006,25.7073194 L16.7610571,26.1388257 L17.4763951,23.1633264 C20.590923,23.7084678 22.9209704,23.4581634 23.8671298,20.664378 C24.6287361,18.4377443 23.7979873,17.1657478 22.1370098,16.3252576 C23.3597388,16.052431 24.2362359,15.2805313 24.4670572,13.6901518 L24.4670572,13.6901518 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</g>
<path d="M15.921528,5.99999821 L0,5.99999821 L0,37.9999982 L15.913948,37.9999982 C8.54220902,34.2663545 4.45416261,25.9154102 6.54600075,17.6560011 C7.87168946,12.4207324 11.4030305,8.29337503 15.921528,5.99999821 Z M32.6737493,5.99999821 L49,5.99999821 L49,37.9999982 L32.6621291,37.9999982 C37.1847176,35.7065678 40.716075,31.5790477 42.0420849,26.3434332 C44.1319349,18.09042 40.0515857,9.72697744 32.6737493,5.99999821 Z" id="Combined-Shape" fill="#09C286"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

12
www/img/icon-bch.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g id="Fondo" transform="matrix(0.0814451,-0.0296277,0.0296277,0.0814451,-44.594,-27.3805)">
<g transform="matrix(10.8433,3.94452,-3.94452,10.8433,375.543,472.796)">
<use xlink:href="#_Image1" x="7.552" y="5.557" width="23.603px" height="28.885px" transform="matrix(0.983467,0,0,0.996049,0,0)"/>
</g>
</g>
<defs>
<image id="_Image1" width="24px" height="29px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAdCAYAAACwuqxLAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACgUlEQVRIia2WzYuPURTHP88Y7zNEaWJKppnEUKRkM5shdlIipSSDSLazQFYkK/EXeMnChsWUkvK2mCJvWXgpZczGa6EmJn7Mx+K50zzu3OeZ35Tv6t5zzj3fc8859wUmAbVJbZjMmnqcZuo69ZT6XN31v5wuV+eE+XXH8FptrNfXP9tVZ6n7gD7gLrA6qO4BhnEHME1dpk6bbORz1duFaC8E0hXq24L8g/pJfa9eVddMhuSk/+KY+shqDKqb6nE+Q92hfpvAYQrP1QWxz0ydAiwAFgMHgJ6KGPqBH8AMoAvIIn17lmVv4qh71C8VkT1Rj6ob1Jawplk9H9kNq5tTaTlU4fyPurcknV3mxR7FV7UptmsErgNbw3YXAcsK+gZgeUm6BoH5hfltoJa0DHXAvMAxHqvtkf0ctS/scETtj23KiFrUOwmS3UG/VD2nDiXqdDOQ7hkNuIzkeILggflZeFdRr1HU1MtVBN2OPwcjCUfPzHt/MKH/re4vI5hufvxTeKGeUDeqbcF+ieNbVvWjOrOM5HBiwX21s8R+ZmIXI2pz2ePRD7yJZJ1Aa4l9E3nbFjEEtCYJsix7CtyKxM3k10MKs4G5kWwgy7JXVc/fvYSsW11YFKjzgHPAvKIYKO+ksLBNfZioxc6gX6+eVV8m8n9Jba4kCE5OJwgG1BuhS2L8VI9Y75OqbjHv6Sr8iebTiz4m+oJ8B4ZLdDXgM3AGGCjIe9X4nRgX+VTzCyzObS2KvCPY3yzIfxRTVNamNfLrNwN+AV+Ba0Av8KWwdlUYXwHeAxeBbYz9QCp3sTYUc3tB1mB+Y46iz/DbU1snTE09UHvN2/eguvK/OI0IZsedUoW/zudIHkrmZMIAAAAASUVORK5CYII="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -1,93 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 51 51"
style="enable-background:new 0 0 51 51;"
xml:space="preserve"
sodipodi:docname="icon-bitcoin.svg"
inkscape:version="0.92.1 r15371"><metadata
id="metadata23"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview21"
showgrid="false"
inkscape:zoom="15.54902"
inkscape:cx="25.5"
inkscape:cy="25.5"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><style
type="text/css"
id="style2">
.st0{fill:url(#coin_1_);}
.st1{filter:url(#Adobe_OpacityMaskFilter);}
.st2{fill:#FFFFFF;}
.st3{mask:url(#mask-3);fill:#FFFFFF;}
</style><linearGradient
id="coin_1_"
gradientUnits="userSpaceOnUse"
x1="-273.8224"
y1="413.966"
x2="-274.172"
y2="414.57"
gradientTransform="matrix(50.3194 0 0 -50.3886 13809.4346 20893.9863)"><stop
offset="0"
style="stop-color:#FFA24B"
id="stop4" /><stop
offset="1"
style="stop-color:#F7891C"
id="stop6" /></linearGradient><path
id="coin"
class="st0"
d="M25.5,51c14.1,0,25.5-11.4,25.5-25.5S39.5,0,25.5,0S0,11.4,0,25.5S11.4,51,25.5,51z"
style="fill:#fab915;fill-opacity:1" /><defs
id="defs13"><filter
id="Adobe_OpacityMaskFilter"
filterUnits="userSpaceOnUse"
x="15"
y="11"
width="21"
height="28.7"><feColorMatrix
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"
id="feColorMatrix10" /></filter></defs><mask
maskUnits="userSpaceOnUse"
x="15"
y="11"
width="21"
height="28.7"
id="mask-3"><g
class="st1"
id="g16"><path
id="path-2"
class="st2"
d="M26.2,50.4c13.9,0,25.2-11.3,25.2-25.2S40.1,0,26.2,0S1,11.3,1,25.2S12.3,50.4,26.2,50.4z" /></g></mask><g
transform="matrix(2.7667404,0,0,2.7667404,-215.6998,-317.27681)"
aria-label=""
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:icomoon;-inkscape-font-specification:icomoon;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4487"><path
inkscape:connector-curvature="0"
d="m 89.710383,123.60521 v -0.0207 q 0.506429,-0.24804 0.785482,-0.7338 0.289388,-0.48576 0.289388,-1.11621 0,-0.69247 -0.330729,-1.1989 -0.33073,-0.50642 -0.847494,-0.74414 -0.320394,-0.14469 -0.682129,-0.19637 -0.3514,-0.0517 -0.816487,-0.0517 H 87.78802 v -0.7028 q 0,-0.10336 -0.07235,-0.1757 -0.07235,-0.0724 -0.1757,-0.0724 H 86.8165 q -0.103353,0 -0.1757,0.0724 -0.07235,0.0723 -0.07235,0.1757 v 0.7028 h -0.806152 v -0.7028 q 0,-0.10336 -0.07235,-0.1757 -0.07235,-0.0724 -0.1757,-0.0724 h -0.7028 q -0.113688,0 -0.186035,0.0724 -0.06201,0.0723 -0.06201,0.1757 v 0.7028 h -0.74414 q -0.06201,0 -0.113688,0.0517 -0.04134,0.0413 -0.04134,0.11369 v 0.68213 q 0,0.0724 0.04134,0.12402 0.05168,0.0413 0.113688,0.0413 h 0.74414 v 6.57324 h -0.692464 q -0.08268,0 -0.144694,0.062 -0.05168,0.0517 -0.05168,0.13436 v 0.59944 q 0,0.0827 0.05168,0.1447 0.06201,0.0517 0.144694,0.0517 h 0.692464 v 0.68213 q 0,0.11369 0.06201,0.1757 0.07235,0.0724 0.186035,0.0724 h 0.72347 q 0.103353,0 0.165365,-0.0724 0.07235,-0.062 0.07235,-0.1757 v -0.68213 h 0.816487 v 0.68213 q 0,0.11369 0.07235,0.1757 0.07235,0.0724 0.1757,0.0724 h 0.72347 q 0.103353,0 0.1757,-0.0724 0.07235,-0.062 0.07235,-0.1757 v -0.68213 h 0.475423 q 0.3514,0 0.661459,-0.0207 0.320394,-0.031 0.620117,-0.14469 0.661458,-0.21704 1.126546,-0.81649 0.465088,-0.60978 0.465088,-1.47794 -0.01034,-0.71314 -0.382406,-1.28158 -0.361735,-0.57877 -1.064534,-0.77515 z m -1.601969,-3.0489 q 0.279052,0 0.485758,0.0413 0.217041,0.0413 0.392741,0.13436 0.279053,0.15502 0.423747,0.45475 0.144694,0.29972 0.144694,0.67179 0,0.57878 -0.3514,0.94051 -0.341065,0.36174 -0.919841,0.36174 h -2.511474 v -2.60449 h 2.335775 z m 0.971516,6.44921 q -0.175699,0.0827 -0.382405,0.10336 -0.206706,0.0207 -0.382406,0.0207 h -2.54248 v -2.97657 h 2.645833 q 0.682129,0 1.085205,0.40308 0.403076,0.39274 0.403076,1.0542 0,0.49609 -0.227376,0.86816 -0.217041,0.37207 -0.599447,0.5271 z"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
id="path4527" /></g></svg>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 51 51" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<path id="coin" d="M25.5,51C39.6,51 51,39.6 51,25.5C51,11.4 39.5,0 25.5,0C11.5,0 0,11.4 0,25.5C0,39.6 11.4,51 25.5,51Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
<g id="BitPay.-Bitcoin_Symbol" transform="matrix(0.939973,0.341249,-0.341249,0.939973,10.2185,-7.19166)">
<path d="M33.5,19.1C32.9,16.1 30.2,15.3 27,15.2L26.5,11L24,11.2L24.5,15.4C23.8,15.5 23.2,15.5 22.5,15.6L22,11.4L19.5,11.6L20,15.9C19.5,15.9 18.9,16 18.4,16.1L15,16.4L15.3,19.2C15.3,19.2 17.1,19 17.1,19.1C18.1,19 18.5,19.6 18.6,20.1L19.1,25C19.2,25 19.3,25 19.4,25C19.3,25 19.2,25 19.1,25L19.9,31.8C19.9,32.1 19.7,32.7 19.1,32.7L17.3,32.9L17.2,36L20.4,35.7C21,35.7 21.6,35.6 22.2,35.6L22.7,39.9L25.2,39.7L24.7,35.4C25.4,35.4 26.1,35.3 26.7,35.3L27.2,39.5L29.7,39.3L29.2,35C33.4,34.4 36.2,33 36.1,28.9C36,25.6 34.4,24.2 31.9,23.8C33.2,22.8 34,21.4 33.5,19.1ZM31,28.7C31.4,31.9 26,32 24.3,32.2L23.7,26.5C25.5,26.3 30.7,25.3 31,28.7ZM23.5,23.8L23,18.6C24.4,18.5 28.8,17.6 29.1,20.7C29.3,23.6 24.9,23.7 23.5,23.8Z" style="fill:white;fill-rule:nonzero;"/>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-17.5917,-30.4347,30.4347,-17.5917,30.8557,34.8191)"><stop offset="0" style="stop-color:rgb(255,162,75);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(247,137,28);stop-opacity:1"/></linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

12
www/img/icon-btc.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g id="Fondo" transform="matrix(0.0814603,0.0295859,-0.0295859,0.0814603,0.874237,-57.7095)">
<g transform="matrix(10.8453,-3.93895,3.93895,10.8453,217.833,629.321)">
<use xlink:href="#_Image1" x="7.557" y="5.56" width="22.579px" height="28.881px" transform="matrix(0.981703,0,0,0.995881,0,0)"/>
</g>
</g>
<defs>
<image id="_Image1" width="23px" height="29px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAdCAYAAABBsffGAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACiElEQVRIia3VPYhfRRQF8PM2myya1SxBcLUQP+JXYQpBkJUlGgu1EoRgIyxkUUQUEU0hKQ1io6K9YBRBC0Fj4UdhghYWURHBQtFI0oioWUVFs5vkZ/Hers/J/N9/F7zdO+/MuWfu3JmbbDAwg/PXw50YIzRVgfcmOYqnMY9mowaDObyE23AptmAT3vFvvIFpTOKadSfCgZ7IKbyI+7HUw+/puIs40SV+AJOD5cBr+NXoOI0bO/4HPfyPQfFekgX8PZDgczzZuV6N/esqD+7uHG4k1ppgsFuSzCXZVGBHkrw/gr+c5AVsH+f6ShytONuDi3ArnsJXFc4hbBsSv6+y6CNcUvB24kiFuzBUll0V7HDTND/0gaZpvkzySJIzBXe+Kq5tsdsL+M8kH48wMpvk9wK7YZTzuSRXFNjmJM/jGVzbmZjCTOd8puC/W3M9hTfHtNvPOIm38CPOFP//wo6a+G7n3sxf8MWYhP3Ei2hq1/SWJGUbvZ1kf5Lr057FniRX9/4vJTmU5HiSl5Mcb5pG6fpiHK64WSh4O/Eqzvbc7sOWitkEE7i3IvwZrqrwp/Fhj7eCJ0rS5TiG1/F1RfzZqpt27aMF91tc0Cc8NHA4Z8uS9NZN4r2Cv4z5Vc5EkukkJ5OcGmHwYW1v7+5EV5/ThSR3FNzNaQ81SbL27uKuJI8luSnJhTn3xfwpybHOhCQ3Jyln7HKS2aZplqo2tTPzt95WywsyKk7j8RG7XxM/UCx6TnsLVwaEV3BwnPA2/22vg9iqnf7f9/AT2sP+Bp9gL4YHj/ZduROvaKf8rg7fV5RnB7ZjdlBwRJIGl2Fr993fzXfWM903kOg6PIhPsfi/CFcSnTe2tr34BwJIUvoJiuf+AAAAAElFTkSuQmCC"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="57px" height="16px" viewBox="0 0 57 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>Group 2 Copy</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-2-Copy">
<rect id="Rectangle" stroke="#FBFBFB" x="0.5" y="0.5" width="56" height="15" rx="2"></rect>
<path d="M9.75390625,4.55761719 L7.41601562,4.55761719 L7.41601562,13 L5.48144531,13 L5.48144531,4.55761719 L3.20507812,4.55761719 L3.20507812,3.046875 L9.75390625,3.046875 L9.75390625,4.55761719 Z M15.6669922,8.60449219 L12.625,8.60449219 L12.625,11.4960938 L16.2685547,11.4960938 L16.2685547,13 L10.6972656,13 L10.6972656,3.046875 L16.2548828,3.046875 L16.2548828,4.55761719 L12.625,4.55761719 L12.625,7.10058594 L15.6669922,7.10058594 L15.6669922,8.60449219 Z M21.8808594,10.4160156 C21.8808594,10.0195293 21.7794606,9.7062186 21.5766602,9.47607422 C21.3738597,9.24592984 21.0104193,9.02148547 20.4863281,8.80273438 C19.4654897,8.44270653 18.6975937,8.03483301 18.1826172,7.57910156 C17.6676407,7.12337012 17.4101562,6.48535566 17.4101562,5.66503906 C17.4101562,4.84927978 17.7040986,4.18506116 18.2919922,3.67236328 C18.8798858,3.15966541 19.6295527,2.90332031 20.5410156,2.90332031 C21.5071663,2.90332031 22.2841767,3.17675508 22.8720703,3.72363281 C23.4599639,4.27051055 23.7447918,5.00422717 23.7265625,5.92480469 L23.7128906,5.96582031 L21.8398438,5.96582031 C21.8398438,5.45084378 21.7247733,5.06005993 21.4946289,4.79345703 C21.2644845,4.52685414 20.9352235,4.39355469 20.5068359,4.39355469 C20.1376935,4.39355469 19.850587,4.51546102 19.6455078,4.75927734 C19.4404287,5.00309367 19.3378906,5.30728984 19.3378906,5.671875 C19.3378906,6.00911627 19.4541004,6.28710828 19.6865234,6.50585938 C19.9189465,6.72461047 20.3177055,6.96386589 20.8828125,7.22363281 C21.8444058,7.54264482 22.5724259,7.9459611 23.0668945,8.43359375 C23.5613631,8.9212264 23.8085938,9.57746983 23.8085938,10.4023438 C23.8085938,11.2545616 23.5192086,11.9244767 22.9404297,12.4121094 C22.3616508,12.899742 21.5937548,13.1435547 20.6367188,13.1435547 C19.6933547,13.1435547 18.8867221,12.8803737 18.2167969,12.3540039 C17.5468717,11.8276341 17.2233072,11.0266981 17.2460938,9.95117188 L17.2597656,9.91015625 L19.1396484,9.91015625 C19.1396484,10.5345083 19.266112,10.9833971 19.519043,11.2568359 C19.7719739,11.5302748 20.1445288,11.6669922 20.6367188,11.6669922 C21.0514344,11.6669922 21.3624664,11.553061 21.5698242,11.3251953 C21.777182,11.0973296 21.8808594,10.7942727 21.8808594,10.4160156 Z M31.0478516,4.55761719 L28.7099609,4.55761719 L28.7099609,13 L26.7753906,13 L26.7753906,4.55761719 L24.4990234,4.55761719 L24.4990234,3.046875 L31.0478516,3.046875 L31.0478516,4.55761719 Z M39.0458984,13 L37.1181641,13 L33.9599609,6.64941406 L33.9189453,6.65625 L33.9189453,13 L31.9912109,13 L31.9912109,3.046875 L33.9189453,3.046875 L37.0771484,9.40429688 L37.1181641,9.39746094 L37.1181641,3.046875 L39.0458984,3.046875 L39.0458984,13 Z M45.546875,8.60449219 L42.5048828,8.60449219 L42.5048828,11.4960938 L46.1484375,11.4960938 L46.1484375,13 L40.5771484,13 L40.5771484,3.046875 L46.1347656,3.046875 L46.1347656,4.55761719 L42.5048828,4.55761719 L42.5048828,7.10058594 L45.546875,7.10058594 L45.546875,8.60449219 Z M53.3603516,4.55761719 L51.0224609,4.55761719 L51.0224609,13 L49.0878906,13 L49.0878906,4.55761719 L46.8115234,4.55761719 L46.8115234,3.046875 L53.3603516,3.046875 L53.3603516,4.55761719 Z" id="TESTNET" fill="#FFFFFF"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g id="flask" transform="matrix(1.27778,0,0,1.27778,7.86111,8)">
<path d="M13.358,7.26C13.563,7.465 13.78,7.69 14.009,7.937C14.237,8.183 14.571,8.552 15.011,9.044C15.45,9.536 15.854,10.034 16.224,10.538C16.593,11.042 16.959,11.58 17.322,12.151C17.686,12.722 17.973,13.295 18.184,13.869C18.395,14.443 18.5,14.962 18.5,15.425C18.5,16.134 18.248,16.74 17.744,17.244C17.24,17.748 16.634,18 15.925,18L3.075,18C2.366,18 1.76,17.748 1.256,17.244C0.752,16.74 0.5,16.134 0.5,15.425C0.5,14.962 0.605,14.443 0.816,13.869C1.027,13.295 1.314,12.722 1.678,12.151C2.041,11.58 2.407,11.042 2.776,10.538C3.146,10.034 3.55,9.536 3.989,9.044C4.429,8.552 4.763,8.183 4.991,7.937C5.22,7.69 5.437,7.465 5.642,7.26L5.642,1.925L4.675,1.925C4.587,1.925 4.512,1.894 4.451,1.833C4.389,1.771 4.358,1.696 4.358,1.608L4.358,0.316C4.358,0.229 4.389,0.154 4.451,0.092C4.512,0.031 4.587,0 4.675,0L14.325,0C14.413,0 14.488,0.031 14.549,0.092C14.611,0.154 14.642,0.229 14.642,0.316L14.642,1.608C14.642,1.696 14.611,1.771 14.549,1.833C14.488,1.894 14.413,1.925 14.325,1.925L13.358,1.925L13.358,7.26ZM5,1.283L14,1.283L14,0.642L5,0.642L5,1.283ZM12.717,1.925L6.283,1.925L6.283,4.14C6.705,4.011 7.105,3.926 7.483,3.885C7.861,3.844 8.168,3.838 8.406,3.867C8.643,3.896 8.863,3.948 9.065,4.021C9.267,4.094 9.408,4.156 9.487,4.206C9.566,4.255 9.632,4.304 9.685,4.351C9.737,4.392 9.806,4.437 9.891,4.487C9.976,4.537 10.124,4.601 10.335,4.68C10.546,4.759 10.763,4.811 10.985,4.834C11.208,4.857 11.478,4.835 11.794,4.768C12.11,4.701 12.418,4.576 12.717,4.395L12.717,1.925ZM15.925,17.358C16.458,17.358 16.914,17.169 17.292,16.792C17.669,16.414 17.858,15.958 17.858,15.425C17.858,14.897 17.688,14.273 17.349,13.553C17.009,12.832 16.615,12.161 16.167,11.54C15.718,10.919 15.228,10.295 14.694,9.668C14.161,9.041 13.747,8.574 13.451,8.266C13.155,7.958 12.942,7.746 12.813,7.629C12.749,7.564 12.717,7.485 12.717,7.392L12.717,5.124C12.242,5.353 11.753,5.467 11.249,5.467C10.903,5.467 10.566,5.413 10.238,5.304C9.91,5.196 9.679,5.101 9.544,5.019C9.409,4.937 9.321,4.875 9.28,4.834C9.233,4.799 9.192,4.77 9.157,4.746C9.122,4.723 9.012,4.679 8.828,4.614C8.643,4.55 8.45,4.51 8.248,4.496C8.045,4.481 7.767,4.496 7.413,4.54C7.058,4.583 6.682,4.673 6.283,4.808L6.283,7.392C6.283,7.485 6.251,7.564 6.187,7.629C6.058,7.746 5.845,7.958 5.549,8.266C5.253,8.574 4.839,9.041 4.306,9.668C3.772,10.295 3.282,10.919 2.833,11.54C2.385,12.161 1.991,12.832 1.651,13.553C1.312,14.273 1.142,14.897 1.142,15.425C1.142,15.958 1.331,16.414 1.708,16.792C2.086,17.169 2.542,17.358 3.075,17.358L15.925,17.358ZM9.825,6.425C10.358,6.425 10.812,6.614 11.187,6.992C11.563,7.37 11.75,7.825 11.75,8.358C11.75,8.892 11.563,9.346 11.187,9.721C10.812,10.096 10.358,10.283 9.825,10.283C9.292,10.283 8.836,10.096 8.458,9.721C8.081,9.346 7.892,8.892 7.892,8.358C7.892,7.825 8.081,7.37 8.458,6.992C8.836,6.614 9.292,6.425 9.825,6.425ZM9.825,9.642C10.177,9.642 10.479,9.517 10.73,9.268C10.982,9.019 11.108,8.716 11.108,8.358C11.108,8.001 10.982,7.696 10.73,7.444C10.479,7.192 10.177,7.066 9.825,7.066C9.468,7.066 9.163,7.192 8.911,7.444C8.659,7.696 8.533,8.001 8.533,8.358C8.533,8.716 8.659,9.019 8.911,9.268C9.163,9.517 9.468,9.642 9.825,9.642ZM7.892,10.925C8.249,10.925 8.552,11.051 8.801,11.303C9.05,11.555 9.175,11.859 9.175,12.217C9.175,12.568 9.05,12.87 8.801,13.122C8.552,13.374 8.249,13.5 7.892,13.5C7.534,13.5 7.231,13.374 6.982,13.122C6.733,12.87 6.608,12.568 6.608,12.217C6.608,11.859 6.733,11.555 6.982,11.303C7.231,11.051 7.534,10.925 7.892,10.925ZM7.892,12.858C8.073,12.858 8.226,12.795 8.349,12.669C8.472,12.543 8.533,12.393 8.533,12.217C8.533,12.035 8.472,11.881 8.349,11.755C8.226,11.629 8.073,11.566 7.892,11.566C7.716,11.566 7.565,11.629 7.439,11.755C7.313,11.881 7.25,12.035 7.25,12.217C7.25,12.393 7.313,12.543 7.439,12.669C7.565,12.795 7.716,12.858 7.892,12.858ZM10.783,13.5C11.053,13.5 11.281,13.594 11.469,13.781C11.656,13.969 11.75,14.197 11.75,14.467C11.75,14.73 11.656,14.956 11.469,15.144C11.281,15.331 11.053,15.425 10.783,15.425C10.52,15.425 10.294,15.331 10.106,15.144C9.919,14.956 9.825,14.73 9.825,14.467C9.825,14.197 9.919,13.969 10.106,13.781C10.294,13.594 10.52,13.5 10.783,13.5ZM10.783,14.783C10.871,14.783 10.947,14.752 11.012,14.691C11.076,14.629 11.108,14.555 11.108,14.467C11.108,14.373 11.076,14.295 11.012,14.234C10.947,14.172 10.871,14.142 10.783,14.142C10.695,14.142 10.621,14.172 10.559,14.234C10.498,14.295 10.467,14.373 10.467,14.467C10.467,14.555 10.498,14.629 10.559,14.691C10.621,14.752 10.695,14.783 10.783,14.783Z" style="fill:white;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 63 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show more