Fix buy and sell
This commit is contained in:
parent
4febe719b0
commit
1a586f6fcc
8 changed files with 268 additions and 228 deletions
|
|
@ -72,6 +72,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.coinbasePaymentMethods = [];
|
$scope.coinbasePaymentMethods = [];
|
||||||
|
$scope.coinbaseSelectedPaymentMethodId = { value : null };
|
||||||
coinbaseService.getPaymentMethods(data.accessToken, function(err, p) {
|
coinbaseService.getPaymentMethods(data.accessToken, function(err, p) {
|
||||||
lodash.each(p.data, function(pm) {
|
lodash.each(p.data, function(pm) {
|
||||||
if ($scope.isCoinbase == 'sell') {
|
if ($scope.isCoinbase == 'sell') {
|
||||||
|
|
@ -79,14 +80,14 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
||||||
$scope.coinbasePaymentMethods.push(pm);
|
$scope.coinbasePaymentMethods.push(pm);
|
||||||
}
|
}
|
||||||
if (pm.allow_sell && pm.primary_sell) {
|
if (pm.allow_sell && pm.primary_sell) {
|
||||||
$scope.coinbaseSelectedPaymentMethod = pm;
|
$scope.coinbaseSelectedPaymentMethodId.value = pm.id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (pm.allow_buy) {
|
if (pm.allow_buy) {
|
||||||
$scope.coinbasePaymentMethods.push(pm);
|
$scope.coinbasePaymentMethods.push(pm);
|
||||||
}
|
}
|
||||||
if (pm.allow_buy && pm.primary_buy) {
|
if (pm.allow_buy && pm.primary_buy) {
|
||||||
$scope.coinbaseSelectedPaymentMethod = pm;
|
$scope.coinbaseSelectedPaymentMethodId.value = pm.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -398,7 +399,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
||||||
glideraAccessToken: $scope.glideraAccessToken
|
glideraAccessToken: $scope.glideraAccessToken
|
||||||
});
|
});
|
||||||
} else if ($scope.isCoinbase) {
|
} else if ($scope.isCoinbase) {
|
||||||
if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethod)) {
|
if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethodId.value)) {
|
||||||
popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected');
|
popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -407,12 +408,17 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount));
|
var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount));
|
||||||
|
if (amountUSD < 1) {
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 USD');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
|
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
|
||||||
$state.transitionTo('tabs.buyandsell.coinbase.confirm', {
|
$state.transitionTo('tabs.buyandsell.coinbase.confirm', {
|
||||||
toAmount: (amount * unitToSatoshi).toFixed(0),
|
toAmount: (amount * unitToSatoshi).toFixed(0),
|
||||||
toAddress: $scope.toAddress,
|
toAddress: $scope.toAddress,
|
||||||
isCoinbase: $scope.isCoinbase,
|
isCoinbase: $scope.isCoinbase,
|
||||||
coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id,
|
coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethodId.value,
|
||||||
coinbaseAmount: amountUSD,
|
coinbaseAmount: amountUSD,
|
||||||
coinbaseAmountCurrency: 'USD'
|
coinbaseAmountCurrency: 'USD'
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function
|
||||||
|
|
||||||
$scope.updateTransactions = function() {
|
$scope.updateTransactions = function() {
|
||||||
$log.debug('Getting transactions...');
|
$log.debug('Getting transactions...');
|
||||||
coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) {
|
$scope.pendingTransactions = { data: {} };
|
||||||
$scope.pendingTransactions = txs;
|
coinbaseService.getPendingTransactions($scope.pendingTransactions);
|
||||||
});
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.openAuthenticateWindow = function() {
|
this.openAuthenticateWindow = function() {
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
if ($scope.paypro) _paymentTimeControl($scope.paypro.expires);
|
if ($scope.paypro) _paymentTimeControl($scope.paypro.expires);
|
||||||
|
|
||||||
displayValues();
|
displayValues();
|
||||||
if ($scope.wallets.length > 1) $scope.showWalletSelector();
|
if ($scope.wallets.length > 1 && $scope.isCoinbase != 'buy') $scope.showWalletSelector();
|
||||||
else setWallet($scope.wallets[0]);
|
else setWallet($scope.wallets[0]);
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
|
|
@ -175,6 +175,8 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
$scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.cardAmountUSD) + ' USD';
|
$scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.cardAmountUSD) + ' USD';
|
||||||
} else if ($scope.giftCardAmountUSD) {
|
} else if ($scope.giftCardAmountUSD) {
|
||||||
$scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.giftCardAmountUSD) + ' USD';
|
$scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.giftCardAmountUSD) + ' USD';
|
||||||
|
} else if ($scope.coinbaseAmount) {
|
||||||
|
$scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.coinbaseAmount) + ' ' + $scope.coinbaseAmountCurrency;
|
||||||
} else {
|
} else {
|
||||||
txFormatService.formatAlternativeStr(toAmount, function(v) {
|
txFormatService.formatAlternativeStr(toAmount, function(v) {
|
||||||
$scope.alternativeAmountStr = v;
|
$scope.alternativeAmountStr = v;
|
||||||
|
|
@ -197,6 +199,10 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
coinbaseService.sellPrice(data.accessToken, $scope.coinbaseAmountCurrency, function(err, sell) {
|
coinbaseService.sellPrice(data.accessToken, $scope.coinbaseAmountCurrency, function(err, sell) {
|
||||||
$scope.coinbaseSellPrice = sell.data;
|
$scope.coinbaseSellPrice = sell.data;
|
||||||
});
|
});
|
||||||
|
coinbaseService.getPaymentMethod(data.accessToken, $scope.coinbasePaymentMethodId, function(err, data) {
|
||||||
|
if (err) $log.error(err);
|
||||||
|
$scope.coinbasePaymentMethodInfo = data.data;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -567,30 +573,45 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
var accountId = res.accountId;
|
var accountId = res.accountId;
|
||||||
coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) {
|
coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$log.error(err);
|
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), 'Could not complete purchase');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var tx = b.data.transaction;
|
var tx = b.data.transaction;
|
||||||
if (!tx) return;
|
if (!tx) {
|
||||||
|
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), 'Transaction not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) {
|
$timeout(function() {
|
||||||
if (err) $log.debug(err);
|
coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) {
|
||||||
walletService.getAddress($scope.wallet, false, function(err, walletAddr) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), 'Transaction error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updatedTx.data['toAddr'] = walletAddr;
|
walletService.getAddress($scope.wallet, false, function(err, walletAddr) {
|
||||||
coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) {
|
if (err) {
|
||||||
if (err) $log.debug(err);
|
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
||||||
if (updatedTx.data.status == 'completed') {
|
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||||
coinbaseSendToCopay(token, accountId, updatedTx.data, onSendStatusChange);
|
return;
|
||||||
} else {
|
}
|
||||||
|
updatedTx.data['toAddr'] = walletAddr;
|
||||||
|
updatedTx.data['status'] = 'pending'; // Forcing "pending" status to process later
|
||||||
|
|
||||||
|
$log.debug('Saving transaction to process later...');
|
||||||
|
coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
||||||
$scope.coinbaseBuySuccess = updatedTx.data;
|
$scope.coinbaseBuySuccess = updatedTx.data;
|
||||||
}
|
$timeout(function() {
|
||||||
|
$scope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}, 8000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -610,7 +631,6 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
|
|
||||||
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
|
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
$scope.sendStatus = '';
|
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
});
|
});
|
||||||
|
|
@ -618,56 +638,13 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
}
|
}
|
||||||
// SIMULATE publishAndSign
|
// SIMULATE publishAndSign
|
||||||
//$log.warn('Simulating publishAndSign...');
|
//$log.warn('Simulating publishAndSign...');
|
||||||
publishAndSign(wallet, txp, onSendStatusChange);
|
$log.debug('Plublish and Sign...');
|
||||||
|
publishAndSign(wallet, txp, function() {}, function(err) {
|
||||||
$timeout(function() {
|
if (err) return setSendError(err);
|
||||||
|
$log.debug('Finished publish and sign. Trying to sell...');
|
||||||
coinbaseService.init(function(err, res) {
|
var count = 0;
|
||||||
if (err) {
|
checkTransaction(count, txp, onSendStatusChange);
|
||||||
$log.error(err);
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
var token = res.accessToken;
|
|
||||||
var accountId = res.accountId;
|
|
||||||
|
|
||||||
coinbaseService.getAddressTransactions(token, accountId, $scope.toAddress, function(err, ctxs) {
|
|
||||||
if (err) {
|
|
||||||
ongoingProcess.clear();
|
|
||||||
$log.debug(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var coinbaseTransactions = ctxs.data;
|
|
||||||
var txFound = false;
|
|
||||||
var ctx;
|
|
||||||
for(var i = 0; i < coinbaseTransactions.length; i++) {
|
|
||||||
ctx = coinbaseTransactions[i];
|
|
||||||
if (ctx.type == 'send' && ctx.from) {
|
|
||||||
txFound = true;
|
|
||||||
if (ctx.status == 'completed') {
|
|
||||||
coinbaseSellRequest(token, accountId, ctx, onSendStatusChange);
|
|
||||||
} else {
|
|
||||||
$log.debug('Saving transaction to process later');
|
|
||||||
ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data;
|
|
||||||
ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : '';
|
|
||||||
ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD';
|
|
||||||
ctx['description'] = $window.appConfig.nameCase + ' Wallet: ' + $scope.wallet.name;
|
|
||||||
$scope.coinbaseSendInfo = ctx;
|
|
||||||
coinbaseService.savePendingTransaction(ctx, null, function(err) {
|
|
||||||
ongoingProcess.set('sellingBitcoin', false, onSendStatusChange);
|
|
||||||
if (err) $log.debug(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!txFound) {
|
|
||||||
// Transaction sent, but can not verify it on Coinbase.com
|
|
||||||
// TODO: improve error message
|
|
||||||
ongoingProcess.clear();
|
|
||||||
return setSendError('Transaction not found. Please, verify if transaction exists on Coinbase.com');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -727,7 +704,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
((processName === 'signingTx') && $scope.wallet.m > 1) ||
|
((processName === 'signingTx') && $scope.wallet.m > 1) ||
|
||||||
(processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) ||
|
(processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) ||
|
||||||
(processName == 'buyingBitcoin') ||
|
(processName == 'buyingBitcoin') ||
|
||||||
(processName == 'sellingBitcoin' && ($scope.coinbaseSellRequest || $scope.coinbaseSendInfo))
|
(processName == 'sellingBitcoin')
|
||||||
) && !isOn) {
|
) && !isOn) {
|
||||||
$scope.sendStatus = 'success';
|
$scope.sendStatus = 'success';
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
|
|
@ -960,18 +937,26 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function publishAndSign(wallet, txp, onSendStatusChange) {
|
function publishAndSign(wallet, txp, onSendStatusChange, cb) {
|
||||||
|
|
||||||
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
|
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
|
||||||
$log.info('No signing proposal: No private key');
|
$log.info('No signing proposal: No private key');
|
||||||
|
|
||||||
return walletService.onlyPublish(wallet, txp, function(err) {
|
walletService.onlyPublish(wallet, txp, function(err) {
|
||||||
if (err) setSendError(err);
|
if (err) {
|
||||||
|
if (cb) return cb(err);
|
||||||
|
else return setSendError(err);
|
||||||
|
}
|
||||||
|
if (cb) return cb();
|
||||||
|
else return;
|
||||||
}, onSendStatusChange);
|
}, onSendStatusChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
walletService.publishAndSign(wallet, txp, function(err, txp) {
|
walletService.publishAndSign(wallet, txp, function(err, txp) {
|
||||||
if (err) return setSendError(err);
|
if (err) {
|
||||||
|
if (cb) return cb(err);
|
||||||
|
else return setSendError(err);
|
||||||
|
}
|
||||||
|
|
||||||
var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName;
|
var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName;
|
||||||
var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false;
|
var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false;
|
||||||
|
|
@ -990,8 +975,72 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
ongoingProcess.set('creatingGiftCard', true);
|
ongoingProcess.set('creatingGiftCard', true);
|
||||||
debounceCreate(count, dataSrc, onSendStatusChange);
|
debounceCreate(count, dataSrc, onSendStatusChange);
|
||||||
}
|
}
|
||||||
|
if (cb) return cb();
|
||||||
}, onSendStatusChange);
|
}, onSendStatusChange);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
var checkTransaction = lodash.throttle(function(count, txp, onSendStatusChange) {
|
||||||
|
$log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5');
|
||||||
|
coinbaseService.init(function(err, res) {
|
||||||
|
if (err) {
|
||||||
|
ongoingProcess.set('sellingBitcoin', false, onSendStatusChange);
|
||||||
|
$log.error(err);
|
||||||
|
checkTransaction(count, txp, onSendStatusChange);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var token = res.accessToken;
|
||||||
|
var accountId = res.accountId;
|
||||||
|
|
||||||
|
coinbaseService.getTransactions(token, accountId, function(err, ctxs) {
|
||||||
|
if (err) {
|
||||||
|
ongoingProcess.set('sellingBitcoin', false, onSendStatusChange);
|
||||||
|
$log.debug(err);
|
||||||
|
checkTransaction(count, txp, onSendStatusChange);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TX amount in BTC
|
||||||
|
var satToBtc = 1 / 100000000;
|
||||||
|
var amountBTC = (txp.amount * satToBtc).toFixed(8);
|
||||||
|
|
||||||
|
var coinbaseTransactions = ctxs.data;
|
||||||
|
var txFound = false;
|
||||||
|
var ctx;
|
||||||
|
for(var i = 0; i < coinbaseTransactions.length; i++) {
|
||||||
|
ctx = coinbaseTransactions[i];
|
||||||
|
if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) {
|
||||||
|
$log.warn('Transaction found!', ctx);
|
||||||
|
txFound = true;
|
||||||
|
$log.debug('Saving transaction to process later...');
|
||||||
|
ctx['payment_method'] = $scope.coinbasePaymentMethodId;
|
||||||
|
ctx['status'] = 'pending'; // Forcing "pending" status to process later
|
||||||
|
ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data;
|
||||||
|
ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : '';
|
||||||
|
ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD';
|
||||||
|
ctx['description'] = $window.appConfig.nameCase + ' Wallet: ' + $scope.wallet.name;
|
||||||
|
coinbaseService.savePendingTransaction(ctx, null, function(err) {
|
||||||
|
ongoingProcess.set('sellingBitcoin', false, onSendStatusChange);
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
$scope.coinbaseSendInfo = ctx;
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!txFound) {
|
||||||
|
// Transaction sent, but could not be verified by Coinbase.com
|
||||||
|
$log.warn('Transaction not found in Coinbase.');
|
||||||
|
if (count < 5) {
|
||||||
|
checkTransaction(count + 1, txp, onSendStatusChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 8000, {
|
||||||
|
'leading': true
|
||||||
|
});
|
||||||
|
|
||||||
var debounceCreate = lodash.throttle(function(count, dataSrc) {
|
var debounceCreate = lodash.throttle(function(count, dataSrc) {
|
||||||
debounceCreateGiftCard(count, dataSrc);
|
debounceCreateGiftCard(count, dataSrc);
|
||||||
|
|
@ -1067,12 +1116,25 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
};
|
};
|
||||||
coinbaseService.init(function(err, res) {
|
coinbaseService.init(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
$scope.showWallets = null;
|
||||||
$log.error(err);
|
$log.error(err);
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), 'Could not connect to Coinbase', function() {
|
||||||
|
$ionicHistory.goBack();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
coinbaseService.getPaymentMethod(res.accessToken, paymentMethodId, function(err, data) {
|
||||||
|
if (err) $log.error(err);
|
||||||
|
$scope.coinbasePaymentMethodInfo = data.data;
|
||||||
|
});
|
||||||
|
|
||||||
coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) {
|
coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
$scope.showWallets = null;
|
||||||
$log.error(err);
|
$log.error(err);
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), 'Could not create a buy request', function() {
|
||||||
|
$ionicHistory.goBack();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$scope.coinbaseBuyRequest = data.data;
|
$scope.coinbaseBuyRequest = data.data;
|
||||||
|
|
@ -1080,48 +1142,4 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var coinbaseSellRequest = function(accessToken, accountId, ctx, onSendStatusChange) {
|
|
||||||
var dataSrc = ctx.amount;
|
|
||||||
dataSrc['payment_method'] = $scope.coinbasePaymentMethodId || null;
|
|
||||||
dataSrc['commit'] = true;
|
|
||||||
|
|
||||||
coinbaseService.sellRequest(accessToken, accountId, dataSrc, function(err, data) {
|
|
||||||
if (err) {
|
|
||||||
ongoingProcess.clear();
|
|
||||||
$log.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$scope.coinbaseSellRequest = data.data;
|
|
||||||
coinbaseService.savePendingTransaction($scope.coinbaseSellRequest, null, function(err) {
|
|
||||||
ongoingProcess.set('sellingBitcoin', false, onSendStatusChange);
|
|
||||||
if (err) $log.debug(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var coinbaseSendToCopay = function(token, accountId, tx, onSendStatusChange) {
|
|
||||||
var data = {
|
|
||||||
to: tx.toAddr,
|
|
||||||
amount: tx.amount.amount,
|
|
||||||
currency: tx.amount.currency,
|
|
||||||
description: 'Copay Wallet: ' + $scope.wallet.name
|
|
||||||
};
|
|
||||||
coinbaseService.sendTo(token, accountId, data, function(err, res) {
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$scope.coinbaseReceiveInfo = res.data;
|
|
||||||
if (!res.data.id) return;
|
|
||||||
coinbaseService.getTransaction(token, accountId, res.data.id, function(err, sendTx) {
|
|
||||||
coinbaseService.savePendingTransaction(tx, {
|
|
||||||
remove: true
|
|
||||||
}, function(err) {
|
|
||||||
coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) {
|
|
||||||
ongoingProcess.set('buyingBitcoin', false, onSendStatusChange);
|
|
||||||
// TODO
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -476,74 +476,90 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getPendingTransactions = function(accessToken, accountId, cb) {
|
root.getPendingTransactions = function(coinbasePendingTransactions) {
|
||||||
var coinbasePendingTransactions;
|
root.init(function(err, data) {
|
||||||
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) {
|
var accessToken = data.accessToken;
|
||||||
txs = txs ? JSON.parse(txs) : {};
|
var accountId = data.accountId;
|
||||||
coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs;
|
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) {
|
||||||
lodash.forEach(coinbasePendingTransactions, function(dataFromStorage, txId) {
|
txs = txs ? JSON.parse(txs) : {};
|
||||||
if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
|
console.log('[coinbaseService.js:484] getCoinbaseTxs',err, txs); //TODO
|
||||||
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
coinbasePendingTransactions.data = lodash.isEmpty(txs) ? null : txs;
|
||||||
dataFromStorage.status == 'error' ||
|
|
||||||
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed'))
|
lodash.forEach(coinbasePendingTransactions.data, function(dataFromStorage, txId) {
|
||||||
return cb(null, coinbasePendingTransactions);
|
if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
|
||||||
root.getTransaction(accessToken, accountId, txId, function(err, tx) {
|
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
||||||
if (err || lodash.isEmpty(tx)) {
|
dataFromStorage.status == 'error' ||
|
||||||
_savePendingTransaction(dataFromStorage, {
|
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed'))
|
||||||
status: 'error',
|
return;
|
||||||
error: err
|
root.getTransaction(accessToken, accountId, txId, function(err, tx) {
|
||||||
}, function(err) {
|
console.log('[coinbaseService.js:494] getTransaction to UPDATE from Coinbase',err, tx); //TODO
|
||||||
if (err) $log.debug(err);
|
if (err || lodash.isEmpty(tx) || (tx.data && tx.data.error)) {
|
||||||
});
|
_savePendingTransaction(dataFromStorage, {
|
||||||
return cb(err);
|
status: 'error',
|
||||||
}
|
error: (tx.data && tx.data.error) ? tx.data.error : err
|
||||||
_updateCoinbasePendingTransactions(dataFromStorage, tx.data);
|
}, function(err) {
|
||||||
coinbasePendingTransactions[txId] = dataFromStorage;
|
if (err) $log.debug(err);
|
||||||
if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) {
|
_updateTxs(coinbasePendingTransactions);
|
||||||
root.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) {
|
});
|
||||||
if (err) {
|
return;
|
||||||
_savePendingTransaction(dataFromStorage, {
|
}
|
||||||
status: 'error',
|
_updateCoinbasePendingTransactions(dataFromStorage, tx.data);
|
||||||
error: err
|
coinbasePendingTransactions.data[txId] = dataFromStorage;
|
||||||
}, function(err) {
|
if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) {
|
||||||
if (err) $log.debug(err);
|
root.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) {
|
||||||
});
|
if (err) {
|
||||||
return cb(err);
|
_savePendingTransaction(dataFromStorage, {
|
||||||
}
|
status: 'error',
|
||||||
var newSellPrice = s.data.amount;
|
error: err
|
||||||
var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100);
|
}, function(err) {
|
||||||
if (variance < dataFromStorage.price_sensitivity.value) {
|
if (err) $log.debug(err);
|
||||||
_sellPending(tx.data, accessToken, accountId);
|
_updateTxs(coinbasePendingTransactions);
|
||||||
} else {
|
});
|
||||||
var error = {
|
return;
|
||||||
errors: [{
|
}
|
||||||
message: 'Price falls over the selected percentage'
|
var newSellPrice = s.data.amount;
|
||||||
}]
|
var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100);
|
||||||
};
|
if (variance < dataFromStorage.price_sensitivity.value) {
|
||||||
_savePendingTransaction(dataFromStorage, {
|
_sellPending(dataFromStorage, accessToken, accountId, coinbasePendingTransactions);
|
||||||
status: 'error',
|
} else {
|
||||||
error: error
|
var error = {
|
||||||
}, function(err) {
|
errors: [{
|
||||||
if (err) $log.debug(err);
|
message: 'Price falls over the selected percentage'
|
||||||
});
|
}]
|
||||||
}
|
};
|
||||||
});
|
_savePendingTransaction(dataFromStorage, {
|
||||||
} else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) {
|
status: 'error',
|
||||||
_sendToCopay(dataFromStorage, accessToken, accountId);
|
error: error
|
||||||
} else {
|
}, function(err) {
|
||||||
_savePendingTransaction(dataFromStorage, {}, function(err) {
|
if (err) $log.debug(err);
|
||||||
if (err) $log.debug(err);
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return cb(null, coinbasePendingTransactions);
|
});
|
||||||
|
} else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) {
|
||||||
|
_sendToWallet(dataFromStorage, accessToken, accountId, coinbasePendingTransactions);
|
||||||
|
} else {
|
||||||
|
_savePendingTransaction(dataFromStorage, {}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var _sellPending = function(tx, accessToken, accountId) {
|
var _updateTxs = function(coinbasePendingTransactions) {
|
||||||
if (!tx) return;
|
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) {
|
||||||
|
txs = txs ? JSON.parse(txs) : {};
|
||||||
|
coinbasePendingTransactions.data = lodash.isEmpty(txs) ? null : txs;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _sellPending = function(tx, accessToken, accountId, coinbasePendingTransactions) {
|
||||||
var data = tx.amount;
|
var data = tx.amount;
|
||||||
|
data['payment_method'] = tx.payment_method || null;
|
||||||
data['commit'] = true;
|
data['commit'] = true;
|
||||||
root.sellRequest(accessToken, accountId, data, function(err, res) {
|
root.sellRequest(accessToken, accountId, data, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -552,14 +568,16 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
error: err
|
error: err
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) $log.debug(err);
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (!res.data.transaction) {
|
if (res.data && !res.data.transaction) {
|
||||||
_savePendingTransaction(tx, {
|
_savePendingTransaction(tx, {
|
||||||
status: 'error',
|
status: 'error',
|
||||||
error: err
|
error: err
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) $log.debug(err);
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -569,6 +587,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
root.getTransaction(accessToken, accountId, res.data.transaction.id, function(err, updatedTx) {
|
root.getTransaction(accessToken, accountId, res.data.transaction.id, function(err, updatedTx) {
|
||||||
_savePendingTransaction(updatedTx.data, {}, function(err) {
|
_savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||||
if (err) $log.debug(err);
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -576,38 +595,43 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var _sendToCopay = function(tx, accessToken, accountId) {
|
var _sendToWallet = function(tx, accessToken, accountId, coinbasePendingTransactions) {
|
||||||
if (!tx) return;
|
if (!tx) return;
|
||||||
var data = {
|
var data = {
|
||||||
to: tx.toAddr,
|
to: tx.toAddr,
|
||||||
amount: tx.amount.amount,
|
amount: tx.amount.amount,
|
||||||
currency: tx.amount.currency,
|
currency: tx.amount.currency,
|
||||||
description: 'To Copay Wallet'
|
description: 'To Wallet'
|
||||||
};
|
};
|
||||||
root.sendTo(accessToken, accountId, data, function(err, res) {
|
root.sendTo(accessToken, accountId, data, function(err, res) {
|
||||||
|
console.log('[coinbaseService.js:591] SEND TO COPAY',err, res); //TODO
|
||||||
if (err) {
|
if (err) {
|
||||||
_savePendingTransaction(tx, {
|
_savePendingTransaction(tx, {
|
||||||
status: 'error',
|
status: 'error',
|
||||||
error: err
|
error: err
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) $log.debug(err);
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (!res.data.id) {
|
if (res.data && !res.data.id) {
|
||||||
_savePendingTransaction(tx, {
|
_savePendingTransaction(tx, {
|
||||||
status: 'error',
|
status: 'error',
|
||||||
error: err
|
error: err
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) $log.debug(err);
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) {
|
root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) {
|
||||||
|
console.log('[coinbaseService.js:633] GET UPDATED TX', err, sendTx); //TODO
|
||||||
_savePendingTransaction(tx, {
|
_savePendingTransaction(tx, {
|
||||||
remove: true
|
remove: true
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
_savePendingTransaction(sendTx.data, {}, function(err) {
|
_savePendingTransaction(sendTx.data, {}, function(err) {
|
||||||
// TODO
|
if (err) $log.debug(err);
|
||||||
|
_updateTxs(coinbasePendingTransactions);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@
|
||||||
<span ng-show="isCoinbase == 'buy'">Payment Method</span>
|
<span ng-show="isCoinbase == 'buy'">Payment Method</span>
|
||||||
<span ng-show="isCoinbase == 'sell'">Deposit into</span>
|
<span ng-show="isCoinbase == 'sell'">Deposit into</span>
|
||||||
</div>
|
</div>
|
||||||
<select ng-model="coinbaseSelectedPaymentMethod.id"
|
<select ng-model="coinbaseSelectedPaymentMethodId.value"
|
||||||
ng-options="item.id as item.name for item in coinbasePaymentMethods">
|
ng-options="item.id as item.name for item in coinbasePaymentMethods">
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@
|
||||||
Activity
|
Activity
|
||||||
</div>
|
</div>
|
||||||
<a class="item"
|
<a class="item"
|
||||||
ng-if="pendingTransactions && !error"
|
ng-if="pendingTransactions.data && !error"
|
||||||
ng-repeat="(id, tx) in pendingTransactions | orderObjectBy:'updated_at':true track by $index"
|
ng-repeat="(id, tx) in pendingTransactions.data | orderObjectBy:'updated_at':true track by $index"
|
||||||
ng-click="coinbase.openTxModal(tx)">
|
ng-click="coinbase.openTxModal(tx)">
|
||||||
|
|
||||||
<span class="item-note">
|
<span class="item-note">
|
||||||
|
|
|
||||||
|
|
@ -46,19 +46,18 @@
|
||||||
will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one
|
will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one
|
||||||
hour).
|
hour).
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<span class="label" ng-if="coinbaseSellPrice">Estimated sale value:
|
||||||
<div>
|
{{coinbaseSellPrice.amount * coinbaseAmountBTC | currency : 'USD ' : 2}}
|
||||||
Estimated sale value:
|
</span>
|
||||||
<span ng-if="coinbaseSellPrice">
|
<span class="label" ng-if="coinbaseSellPrice">Still sell if price fall until:
|
||||||
{{coinbaseSellPrice.amount * coinbaseAmountBTC | currency : 'USD ' : 2}}
|
{{(coinbaseSellPrice.amount - (selectedPriceSensitivity.data.value / 100) *
|
||||||
</span>
|
coinbaseSellPrice.amount) * coinbaseAmountBTC | currency : 'USD ' : 2}}
|
||||||
</div>
|
</span>
|
||||||
Still sell if price fall until:
|
</div>
|
||||||
<span ng-if="coinbaseSellPrice">
|
<div class="item single-line" ng-if="isCoinbase && coinbasePaymentMethodInfo">
|
||||||
{{(coinbaseSellPrice.amount - (selectedPriceSensitivity.data.value / 100) *
|
<span class="label" ng-show="isCoinbase == 'buy'">Payment Method</span>
|
||||||
coinbaseSellPrice.amount) * coinbaseAmountBTC | currency : 'USD ' : 2}}
|
<span class="label" ng-show="isCoinbase == 'sell'">Deposit into</span>
|
||||||
</span>
|
<span class="item-note">{{coinbasePaymentMethodInfo.name}}</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<span class="label" ng-if="!(isGlidera || isCoinbase)" translate>To</span>
|
<span class="label" ng-if="!(isGlidera || isCoinbase)" translate>To</span>
|
||||||
|
|
@ -147,15 +146,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="size-12 m10" ng-show="isCoinbase == 'buy'">
|
<div ng-show="isCoinbase == 'buy'">
|
||||||
<div>
|
<div class="item single-line">
|
||||||
Subtotal: {{coinbaseBuyRequest.subtotal.amount}} {{coinbaseBuyRequest.subtotal.currency}}
|
<span class="label">Subtotal</span>
|
||||||
|
<span class="item-note">{{coinbaseBuyRequest.subtotal.amount}}
|
||||||
|
{{coinbaseBuyRequest.subtotal.currency}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="item single-line">
|
||||||
Total: {{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}}
|
<span class="label">Total</span>
|
||||||
|
<span class="item-note">{{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="item single-line">
|
||||||
Payout at: {{coinbaseBuyRequest.payout_at | amCalendar}}
|
<span class="label">Payout at</span>
|
||||||
|
<span class="item-note">{{coinbaseBuyRequest.payout_at | amCalendar}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center" ng-show="noMatchingWallet">
|
<div class="text-center" ng-show="noMatchingWallet">
|
||||||
|
|
@ -191,7 +194,6 @@
|
||||||
slide-success-hide-on-confirm="true">
|
slide-success-hide-on-confirm="true">
|
||||||
<span ng-show="isCoinbase == 'buy'">Bought</span>
|
<span ng-show="isCoinbase == 'buy'">Bought</span>
|
||||||
<span ng-show="isCoinbase == 'sell' && coinbaseSendInfo">Funds sent to Coinbase Account</span>
|
<span ng-show="isCoinbase == 'sell' && coinbaseSendInfo">Funds sent to Coinbase Account</span>
|
||||||
<span ng-show="isCoinbase == 'sell' && coinbaseSellRequest">Sale initiated</span>
|
|
||||||
<div ng-show="!isCoinbase">
|
<div ng-show="!isCoinbase">
|
||||||
<span ng-show="wallet.m == 1 && (wallet.canSign() || wallet.isPrivKeyExternal())" translate>Payment Sent</span>
|
<span ng-show="wallet.m == 1 && (wallet.canSign() || wallet.isPrivKeyExternal())" translate>Payment Sent</span>
|
||||||
<span ng-show="wallet.m > 1 && (wallet.canSign() || wallet.isPrivKeyExternal())" translate>Proposal Created</span>
|
<span ng-show="wallet.m > 1 && (wallet.canSign() || wallet.isPrivKeyExternal())" translate>Proposal Created</span>
|
||||||
|
|
@ -204,18 +206,12 @@
|
||||||
<div ng-show="isCoinbase" class="glidera-success">
|
<div ng-show="isCoinbase" class="glidera-success">
|
||||||
<span ng-show="isCoinbase == 'buy'">
|
<span ng-show="isCoinbase == 'buy'">
|
||||||
<span ng-show="coinbaseBuySuccess">
|
<span ng-show="coinbaseBuySuccess">
|
||||||
Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet
|
Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet
|
||||||
</span>
|
|
||||||
<span ng-show="coinbaseReceiveInfo">
|
|
||||||
Buy confirmed. Funds will be send soon to your selected wallet
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span ng-show="isCoinbase == 'sell'">
|
<span ng-show="isCoinbase == 'sell'">
|
||||||
<span ng-show="coinbaseSendInfo">
|
<span ng-show="coinbaseSendInfo">
|
||||||
The transaction is not yet confirmed, and will show as "Processing" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase.
|
The transaction is not yet confirmed, and will show as "Pending" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase.
|
||||||
</span>
|
|
||||||
<span ng-show="coinbaseSellRequest">
|
|
||||||
A transfer has been initiated to your bank account and should arrive at {{coinbaseSellRequest.payout_at | amCalendar}}.
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="box-notification error" ng-show="tx.error">
|
<ul class="card list" ng-show="tx.error">
|
||||||
<ul class="card list">
|
<li class="item" ng-repeat="err in tx.error.errors" ng-bind-html="err.message"></li>
|
||||||
<li class="item" ng-repeat="err in tx.error.errors" ng-bind-html="err.message"></li>
|
</ul>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
<li ng-show="tx.details && tx.status != 'pending'" class="item">
|
<li ng-show="tx.details && tx.status != 'pending'" class="item">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue