Merge pull request #5334 from cmgustavo/feat/coinbase-integration
Re-enable Coinbase integration
This commit is contained in:
commit
6bbb1fd442
30 changed files with 1632 additions and 1241 deletions
|
|
@ -20,6 +20,9 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
$scope.isGlidera = data.stateParams.isGlidera;
|
||||
$scope.glideraAccessToken = data.stateParams.glideraAccessToken;
|
||||
|
||||
// Go to...
|
||||
$scope.nextStep = data.stateParams.nextStep;
|
||||
|
||||
$scope.cardId = data.stateParams.cardId;
|
||||
$scope.showMenu = $ionicHistory.backView() && $ionicHistory.backView().stateName == 'tabs.send';
|
||||
var isWallet = data.stateParams.isWallet || 'false';
|
||||
|
|
@ -27,13 +30,13 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
$scope.toAddress = data.stateParams.toAddress;
|
||||
$scope.toName = data.stateParams.toName;
|
||||
$scope.toEmail = data.stateParams.toEmail;
|
||||
$scope.showAlternativeAmount = !!$scope.cardId || !!$scope.isGiftCard || !!$scope.isGlidera;
|
||||
$scope.showAlternativeAmount = !!$scope.cardId || !!$scope.isGiftCard || !!$scope.isGlidera || !!$scope.nextStep;
|
||||
$scope.toColor = data.stateParams.toColor;
|
||||
$scope.showSendMax = false;
|
||||
|
||||
$scope.customAmount = data.stateParams.customAmount;
|
||||
|
||||
if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !data.stateParams.toAddress) {
|
||||
if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !$scope.nextStep && !data.stateParams.toAddress) {
|
||||
$log.error('Bad params at amount')
|
||||
throw ('bad params');
|
||||
}
|
||||
|
|
@ -72,7 +75,11 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
|
||||
var config = configService.getSync().wallet.settings;
|
||||
$scope.unitName = config.unitName;
|
||||
$scope.alternativeIsoCode = !!$scope.cardId || !!$scope.isGiftCard ? 'USD' : config.alternativeIsoCode;
|
||||
if (data.stateParams.currency) {
|
||||
$scope.alternativeIsoCode = data.stateParams.currency;
|
||||
} else {
|
||||
$scope.alternativeIsoCode = !!$scope.cardId || !!$scope.isGiftCard ? 'USD' : config.alternativeIsoCode;
|
||||
}
|
||||
$scope.specificAmount = $scope.specificAlternativeAmount = '';
|
||||
$scope.isCordova = platformInfo.isCordova;
|
||||
unitToSatoshi = config.unitToSatoshi;
|
||||
|
|
@ -350,6 +357,11 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
isGlidera: $scope.isGlidera,
|
||||
glideraAccessToken: $scope.glideraAccessToken
|
||||
});
|
||||
} else if ($scope.nextStep) {
|
||||
$state.transitionTo($scope.nextStep, {
|
||||
amount: _amount,
|
||||
currency: $scope.showAlternativeAmount ? $scope.alternativeIsoCode : ''
|
||||
});
|
||||
} else {
|
||||
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
|
||||
if ($scope.customAmount) {
|
||||
|
|
|
|||
|
|
@ -1,175 +1,210 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('buyCoinbaseController',
|
||||
function($scope, $log, $ionicModal, $timeout, lodash, profileService, coinbaseService, addressService, ongoingProcess) {
|
||||
var self = this;
|
||||
angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService) {
|
||||
|
||||
this.init = function(testnet) {
|
||||
self.allWallets = profileService.getWallets(testnet ? 'testnet' : 'livenet');
|
||||
var amount;
|
||||
var currency;
|
||||
|
||||
var client = profileService.focusedClient;
|
||||
if (client) {
|
||||
$timeout(function() {
|
||||
self.selectedWalletId = client.credentials.walletId;
|
||||
self.selectedWalletName = client.credentials.walletName;
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
var showErrorAndBack = function(err) {
|
||||
$scope.sendStatus = '';
|
||||
$log.error(err);
|
||||
err = err.errors ? err.errors[0].message : err;
|
||||
popupService.showAlert('Error', err, function() {
|
||||
$ionicHistory.goBack();
|
||||
});
|
||||
};
|
||||
|
||||
var showError = function(err) {
|
||||
$scope.sendStatus = '';
|
||||
$log.error(err);
|
||||
err = err.errors ? err.errors[0].message : err;
|
||||
popupService.showAlert('Error', err);
|
||||
};
|
||||
|
||||
var statusChangeHandler = function (processName, showName, isOn) {
|
||||
$log.debug('statusChangeHandler: ', processName, showName, isOn);
|
||||
if ( processName == 'buyingBitcoin' && !isOn) {
|
||||
$scope.sendStatus = 'success';
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 100);
|
||||
} else if (showName) {
|
||||
$scope.sendStatus = showName;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
coinbaseService.setCredentials();
|
||||
|
||||
$scope.isFiat = data.stateParams.currency ? true : false;
|
||||
[amount, currency, $scope.amountUnitStr] = coinbaseService.parseAmount(
|
||||
data.stateParams.amount,
|
||||
data.stateParams.currency);
|
||||
|
||||
$scope.network = coinbaseService.getNetwork();
|
||||
$scope.wallets = profileService.getWallets({
|
||||
onlyComplete: true,
|
||||
network: $scope.network
|
||||
});
|
||||
$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;
|
||||
|
||||
this.getPaymentMethods = function(token) {
|
||||
coinbaseService.getPaymentMethods(token, function(err, p) {
|
||||
coinbaseService.buyPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, b) {
|
||||
$scope.buyPrice = b.data || null;
|
||||
});
|
||||
|
||||
$scope.paymentMethods = [];
|
||||
$scope.selectedPaymentMethodId = { value : null };
|
||||
coinbaseService.getPaymentMethods(accessToken, function(err, p) {
|
||||
if (err) {
|
||||
self.error = err;
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
showErrorAndBack(err);
|
||||
return;
|
||||
}
|
||||
self.paymentMethods = [];
|
||||
lodash.each(p.data, function(pm) {
|
||||
|
||||
var hasPrimary;
|
||||
var pm;
|
||||
for(var i = 0; i < p.data.length; i++) {
|
||||
pm = p.data[i];
|
||||
if (pm.allow_buy) {
|
||||
self.paymentMethods.push(pm);
|
||||
$scope.paymentMethods.push(pm);
|
||||
}
|
||||
if (pm.allow_buy && pm.primary_buy) {
|
||||
$scope.selectedPaymentMethod = pm;
|
||||
hasPrimary = true;
|
||||
$scope.selectedPaymentMethodId.value = pm.id;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (lodash.isEmpty($scope.paymentMethods)) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
showErrorAndBack('No payment method available to buy');
|
||||
return;
|
||||
}
|
||||
if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id;
|
||||
$scope.buyRequest();
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
this.getPrice = function(token) {
|
||||
var currency = 'USD';
|
||||
coinbaseService.buyPrice(token, currency, function(err, b) {
|
||||
if (err) return;
|
||||
self.buyPrice = b.data || null;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openWalletsModal = function(wallets) {
|
||||
self.error = null;
|
||||
|
||||
$scope.type = 'BUY';
|
||||
$scope.wallets = wallets;
|
||||
$scope.noColor = true;
|
||||
$scope.self = self;
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/wallets.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.walletsModal = modal;
|
||||
$scope.walletsModal.show();
|
||||
});
|
||||
|
||||
$scope.$on('walletSelected', function(ev, walletId) {
|
||||
$timeout(function() {
|
||||
var client = profileService.getClient(walletId);
|
||||
self.selectedWalletId = walletId;
|
||||
self.selectedWalletName = client.credentials.walletName;
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
$scope.walletsModal.hide();
|
||||
});
|
||||
};
|
||||
|
||||
this.buyRequest = function(token, account) {
|
||||
self.error = null;
|
||||
var accountId = account.id;
|
||||
var amount = $scope.amount ? $scope.amount : $scope.fiat;
|
||||
var currency = $scope.amount ? 'BTC' : 'USD';
|
||||
if (!amount) return;
|
||||
$scope.buyRequest = function() {
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
coinbaseService.init(function(err, res) {
|
||||
if (err) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
showErrorAndBack(err);
|
||||
return;
|
||||
}
|
||||
var accessToken = res.accessToken;
|
||||
var accountId = res.accountId;
|
||||
var dataSrc = {
|
||||
amount: amount,
|
||||
currency: currency,
|
||||
payment_method: $scope.selectedPaymentMethod.id || null
|
||||
payment_method: $scope.selectedPaymentMethodId.value,
|
||||
quote: true
|
||||
};
|
||||
ongoingProcess.set('Sending request...', true);
|
||||
coinbaseService.buyRequest(token, accountId, dataSrc, function(err, data) {
|
||||
ongoingProcess.set('Sending request...', false);
|
||||
coinbaseService.buyRequest(accessToken, accountId, dataSrc, function(err, data) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
self.error = err;
|
||||
showErrorAndBack(err);
|
||||
return;
|
||||
}
|
||||
self.buyInfo = data.data;
|
||||
$scope.buyRequestInfo = data.data;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.confirmBuy = function(token, account, buy) {
|
||||
self.error = null;
|
||||
var accountId = account.id;
|
||||
var buyId = buy.id;
|
||||
ongoingProcess.set('Buying Bitcoin...', true);
|
||||
coinbaseService.buyCommit(token, accountId, buyId, function(err, b) {
|
||||
ongoingProcess.set('Buying Bitcoin...', false);
|
||||
$scope.buyConfirm = function() {
|
||||
var message = 'Buy bitcoin for ' + amount + ' ' + currency;
|
||||
var okText = 'Confirm';
|
||||
var cancelText = 'Cancel';
|
||||
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
|
||||
if (!ok) return;
|
||||
|
||||
ongoingProcess.set('buyingBitcoin', true, statusChangeHandler);
|
||||
coinbaseService.init(function(err, res) {
|
||||
if (err) {
|
||||
self.error = err;
|
||||
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
} else {
|
||||
var tx = b.data.transaction;
|
||||
if (!tx) return;
|
||||
}
|
||||
var accessToken = res.accessToken;
|
||||
var accountId = res.accountId;
|
||||
var dataSrc = {
|
||||
amount: amount,
|
||||
currency: currency,
|
||||
payment_method: $scope.selectedPaymentMethodId.value,
|
||||
commit: true
|
||||
};
|
||||
coinbaseService.buyRequest(accessToken, accountId, dataSrc, function(err, b) {
|
||||
if (err) {
|
||||
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
var tx = b.data ? b.data.transaction : null;
|
||||
if (!tx) {
|
||||
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
|
||||
showError('Transaction not found');
|
||||
return;
|
||||
}
|
||||
|
||||
ongoingProcess.set('Fetching transaction...', true);
|
||||
coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) {
|
||||
ongoingProcess.set('Fetching transaction...', false);
|
||||
if (err) $log.debug(err);
|
||||
addressService.getAddress(self.selectedWalletId, false, function(err, addr) {
|
||||
$timeout(function() {
|
||||
coinbaseService.getTransaction(accessToken, accountId, tx.id, function(err, updatedTx) {
|
||||
if (err) {
|
||||
self.error = {
|
||||
errors: [{
|
||||
message: 'Could not create address'
|
||||
}]
|
||||
};
|
||||
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
updatedTx.data['toAddr'] = addr;
|
||||
coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
if (updatedTx.data.status == 'completed') {
|
||||
self.sendToCopay(token, account, updatedTx.data);
|
||||
} else {
|
||||
self.success = updatedTx.data;
|
||||
$timeout(function() {
|
||||
$scope.$emit('Local/CoinbaseTx');
|
||||
}, 1000);
|
||||
walletService.getAddress($scope.wallet, false, function(err, walletAddr) {
|
||||
if (err) {
|
||||
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}, 8000);
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.sendToCopay = function(token, account, tx) {
|
||||
self.error = null;
|
||||
var accountId = account.id;
|
||||
$scope.showWalletSelector = function() {
|
||||
$scope.walletSelectorTitle = 'Receive in';
|
||||
$scope.showWallets = true;
|
||||
};
|
||||
|
||||
ongoingProcess.set('Sending funds to Copay...', true);
|
||||
var data = {
|
||||
to: tx.toAddr,
|
||||
amount: tx.amount.amount,
|
||||
currency: tx.amount.currency,
|
||||
description: 'Copay Wallet: ' + self.selectedWalletName
|
||||
};
|
||||
coinbaseService.sendTo(token, accountId, data, function(err, res) {
|
||||
ongoingProcess.set('Sending funds to Copay...', false);
|
||||
if (err) {
|
||||
self.error = err;
|
||||
} else {
|
||||
self.receiveInfo = 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) {
|
||||
$timeout(function() {
|
||||
$scope.$emit('Local/CoinbaseTx');
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
$scope.onWalletSelect = function(wallet) {
|
||||
$scope.wallet = wallet;
|
||||
};
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
$scope.goBackHome = function() {
|
||||
$scope.sendStatus = '';
|
||||
$ionicHistory.nextViewOptions({
|
||||
disableAnimate: true,
|
||||
historyRoot: true
|
||||
});
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.home').then(function() {
|
||||
$state.transitionTo('tabs.buyandsell.coinbase');
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,78 +1,117 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('coinbaseController',
|
||||
function($rootScope, $scope, $timeout, $ionicModal, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess) {
|
||||
angular.module('copayApp.controllers').controller('coinbaseController', function($scope, $timeout, $ionicModal, $log, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, externalLinkService) {
|
||||
|
||||
var isNW = platformInfo.isNW;
|
||||
var isNW = platformInfo.isNW;
|
||||
var isCordova = platformInfo.isCordova;
|
||||
|
||||
if (platformInfo.isCordova && StatusBar.isVisible) {
|
||||
StatusBar.backgroundColorByHexString("#4B6178");
|
||||
}
|
||||
|
||||
this.openAuthenticateWindow = function() {
|
||||
var oauthUrl = this.getAuthenticateUrl();
|
||||
if (!isNW) {
|
||||
$rootScope.openExternalLink(oauthUrl, '_system');
|
||||
} else {
|
||||
var self = this;
|
||||
var gui = require('nw.gui');
|
||||
var win = gui.Window.open(oauthUrl, {
|
||||
focus: true,
|
||||
position: 'center'
|
||||
});
|
||||
win.on('loaded', function() {
|
||||
var title = win.title;
|
||||
if (title.indexOf('Coinbase') == -1) {
|
||||
$scope.code = title;
|
||||
self.submitOauthCode(title);
|
||||
win.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.getAuthenticateUrl = function() {
|
||||
return coinbaseService.getOauthCodeUrl();
|
||||
};
|
||||
|
||||
this.submitOauthCode = function(code) {
|
||||
var self = this;
|
||||
var coinbaseTestnet = configService.getSync().coinbase.testnet;
|
||||
var network = coinbaseTestnet ? 'testnet' : 'livenet';
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
this.error = null;
|
||||
$timeout(function() {
|
||||
coinbaseService.getToken(code, function(err, data) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
var init = function() {
|
||||
$scope.currency = coinbaseService.getAvailableCurrency();
|
||||
coinbaseService.getStoredToken(function(at) {
|
||||
$scope.accessToken = at;
|
||||
|
||||
// Update Access Token if necessary
|
||||
$scope.loading = true;
|
||||
coinbaseService.init(function(err, data) {
|
||||
$scope.loading = false;
|
||||
if (err || lodash.isEmpty(data)) {
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
} else if (data && data.access_token && data.refresh_token) {
|
||||
storageService.setCoinbaseToken(network, data.access_token, function() {
|
||||
storageService.setCoinbaseRefreshToken(network, data.refresh_token, function() {
|
||||
$scope.$emit('Local/CoinbaseUpdated', data.access_token);
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
popupService.showAlert('Error', err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Show rates
|
||||
coinbaseService.buyPrice(data.accessToken, $scope.currency, function(err, b) {
|
||||
$scope.buyPrice = b.data || null;
|
||||
});
|
||||
coinbaseService.sellPrice(data.accessToken, $scope.currency, function(err, s) {
|
||||
$scope.sellPrice = s.data || null;
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.openTxModal = function(tx) {
|
||||
$scope.tx = tx;
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/coinbase-tx-details.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.coinbaseTxDetailsModal = modal;
|
||||
$scope.coinbaseTxDetailsModal.show();
|
||||
// Updating accessToken and accountId
|
||||
$timeout(function() {
|
||||
$scope.accessToken = data.accessToken;
|
||||
$scope.accountId = data.accountId;
|
||||
$scope.updateTransactions();
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updateTransactions = function() {
|
||||
$log.debug('Getting transactions...');
|
||||
$scope.pendingTransactions = { data: {} };
|
||||
coinbaseService.getPendingTransactions($scope.pendingTransactions);
|
||||
};
|
||||
|
||||
this.openAuthenticateWindow = function() {
|
||||
var oauthUrl = this.getAuthenticateUrl();
|
||||
if (!isNW) {
|
||||
externalLinkService.open(oauthUrl);
|
||||
} else {
|
||||
var self = this;
|
||||
var gui = require('nw.gui');
|
||||
gui.Window.open(oauthUrl, {
|
||||
focus: true,
|
||||
position: 'center'
|
||||
}, function(new_win) {
|
||||
new_win.on('loaded', function() {
|
||||
var title = new_win.window.document.title;
|
||||
$timeout(function() {
|
||||
if (title.indexOf('Coinbase') == -1) {
|
||||
$scope.code = title;
|
||||
self.submitOauthCode($scope.code);
|
||||
new_win.close();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.getAuthenticateUrl = function() {
|
||||
$scope.showOauthForm = isCordova || isNW ? false : true;
|
||||
return coinbaseService.getOauthCodeUrl();
|
||||
};
|
||||
|
||||
this.submitOauthCode = function(code) {
|
||||
var self = this;
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
coinbaseService.getToken(code, function(err, accessToken) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
popupService.showAlert('Error', err);
|
||||
return;
|
||||
}
|
||||
$scope.accessToken = accessToken;
|
||||
init();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.openTxModal = function(tx) {
|
||||
$scope.tx = tx;
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/coinbase-tx-details.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.modal = modal;
|
||||
$scope.modal.show();
|
||||
});
|
||||
};
|
||||
|
||||
var self = this;
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
coinbaseService.setCredentials();
|
||||
if (data.stateParams && data.stateParams.code) {
|
||||
self.submitOauthCode(data.stateParams.code);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.controllers').controller('coinbaseUriController',
|
||||
function($scope, $stateParams, $timeout, profileService, configService, coinbaseService, storageService, $state, ongoingProcess) {
|
||||
|
||||
this.submitOauthCode = function(code) {
|
||||
var self = this;
|
||||
var coinbaseTestnet = configService.getSync().coinbase.testnet;
|
||||
var network = coinbaseTestnet ? 'testnet' : 'livenet';
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
this.error = null;
|
||||
$timeout(function() {
|
||||
coinbaseService.getToken(code, function(err, data) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
self.error = err;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
} else if (data && data.access_token && data.refresh_token) {
|
||||
storageService.setCoinbaseToken(network, data.access_token, function() {
|
||||
storageService.setCoinbaseRefreshToken(network, data.refresh_token, function() {
|
||||
$scope.$emit('Local/CoinbaseUpdated', data.access_token);
|
||||
$timeout(function() {
|
||||
$state.go('coinbase');
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.checkCode = function() {
|
||||
if ($stateParams.url) {
|
||||
var match = $stateParams.url.match(/code=(.+)&/);
|
||||
if (match && match[1]) {
|
||||
this.code = match[1];
|
||||
return this.submitOauthCode(this.code);
|
||||
}
|
||||
}
|
||||
$log.error('Bad state: ' + JSON.stringify($stateParams));
|
||||
}
|
||||
});
|
||||
|
|
@ -528,7 +528,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ongoingProcess.set('creatingTx', true, onSendStatusChange);
|
||||
createTx(wallet, false, function(err, txp) {
|
||||
ongoingProcess.set('creatingTx', false, onSendStatusChange);
|
||||
|
|
@ -577,12 +577,14 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
$log.debug('statusChangeHandler: ', processName, showName, isOn);
|
||||
if (
|
||||
(
|
||||
processName === 'broadcastingTx' ||
|
||||
((processName === 'signingTx') && $scope.wallet.m > 1) ||
|
||||
processName === 'broadcastingTx' ||
|
||||
((processName === 'signingTx') && $scope.wallet.m > 1) ||
|
||||
(processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal())
|
||||
) && !isOn) {
|
||||
$scope.sendStatus = 'success';
|
||||
$scope.$digest();
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 100);
|
||||
} else if (showName) {
|
||||
$scope.sendStatus = showName;
|
||||
}
|
||||
|
|
@ -831,7 +833,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
debounceCreate(count, dataSrc, onSendStatusChange);
|
||||
}
|
||||
}, onSendStatusChange);
|
||||
}
|
||||
};
|
||||
|
||||
var debounceCreate = lodash.throttle(function(count, dataSrc) {
|
||||
debounceCreateGiftCard(count, dataSrc);
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('coinbaseConfirmationController', function($scope, $timeout, coinbaseService, applicationService) {
|
||||
|
||||
$scope.ok = function() {
|
||||
|
||||
coinbaseService.logout($scope.network, function() {
|
||||
|
||||
$timeout(function() {
|
||||
applicationService.restart();
|
||||
}, 1000);
|
||||
});
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.coinbaseConfirmationModal.hide();
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -1,18 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('coinbaseTxDetailsController', function($scope, $rootScope, coinbaseService) {
|
||||
angular.module('copayApp.controllers').controller('coinbaseTxDetailsController', function($scope, coinbaseService, popupService) {
|
||||
|
||||
$scope.remove = function() {
|
||||
coinbaseService.savePendingTransaction($scope.tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
$rootScope.$emit('Local/CoinbaseTx');
|
||||
$scope.cancel();
|
||||
coinbaseService.setCredentials();
|
||||
$scope.updateRequired = false;
|
||||
var message = 'Are you sure you want to remove this transaction?';
|
||||
popupService.showConfirm(null, message, null, null, function(ok) {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
coinbaseService.savePendingTransaction($scope.tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
$scope.updateRequired = true;
|
||||
$scope.close();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$scope.coinbaseTxDetailsModal.hide();
|
||||
$scope.close = function() {
|
||||
$scope.modal.hide().then(function() {
|
||||
if ($scope.updateRequired) $scope.updateTransactions();
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,18 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesCoinbaseController',
|
||||
function($scope, $timeout, $ionicModal, applicationService, coinbaseService) {
|
||||
angular.module('copayApp.controllers').controller('preferencesCoinbaseController', function($scope, $timeout, $state, $ionicHistory, lodash, ongoingProcess, popupService, coinbaseService) {
|
||||
|
||||
this.revokeToken = function(testnet) {
|
||||
$scope.network = testnet ? 'testnet' : 'livenet';
|
||||
$scope.revokeToken = function() {
|
||||
popupService.showConfirm('Coinbase', 'Are you sure you would like to log out of your Coinbase account?', null, null, function(res) {
|
||||
if (res) {
|
||||
coinbaseService.logout(function() {
|
||||
$ionicHistory.clearHistory();
|
||||
$timeout(function() {
|
||||
$state.go('tabs.home');
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/coinbase-confirmation.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.coinbaseConfirmationModal = modal;
|
||||
$scope.coinbaseConfirmationModal.show();
|
||||
$scope.$on("$ionicView.enter", function(event, data){
|
||||
coinbaseService.setCredentials();
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
coinbaseService.init(function(err, data) {
|
||||
if (err || lodash.isEmpty(data)) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var accessToken = data.accessToken;
|
||||
var accountId = data.accountId;
|
||||
coinbaseService.getAccount(accessToken, accountId, function(err, account) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
$scope.coinbaseAccount = account.data;
|
||||
});
|
||||
};
|
||||
|
||||
coinbaseService.getCurrentUser(accessToken, function(err, user) {
|
||||
$scope.coinbaseUser = user.data;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,187 +1,258 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('sellCoinbaseController',
|
||||
function($rootScope, $scope, $log, $timeout, $ionicModal, lodash, profileService, coinbaseService, configService, walletService, fingerprintService, ongoingProcess, go) {
|
||||
angular.module('copayApp.controllers').controller('sellCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, appConfigService, configService) {
|
||||
|
||||
var self = this;
|
||||
var client;
|
||||
var amount;
|
||||
var currency;
|
||||
|
||||
$scope.priceSensitivity = [
|
||||
{
|
||||
value: 0.5,
|
||||
name: '0.5%'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
name: '1%'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: '2%'
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
name: '5%'
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
name: '10%'
|
||||
}
|
||||
];
|
||||
$scope.selectedPriceSensitivity = $scope.priceSensitivity[1];
|
||||
var showErrorAndBack = function(err) {
|
||||
$scope.sendStatus = '';
|
||||
$log.error(err);
|
||||
err = err.errors ? err.errors[0].message : err;
|
||||
popupService.showAlert('Error', err, function() {
|
||||
$ionicHistory.goBack();
|
||||
});
|
||||
};
|
||||
|
||||
this.init = function(testnet) {
|
||||
self.allWallets = profileService.getWallets(testnet ? 'testnet' : 'livenet', 1);
|
||||
var showError = function(err) {
|
||||
$scope.sendStatus = '';
|
||||
$log.error(err);
|
||||
err = err.errors ? err.errors[0].message : err;
|
||||
popupService.showAlert('Error', err);
|
||||
};
|
||||
|
||||
client = profileService.focusedClient;
|
||||
if (client && client.credentials.m == 1) {
|
||||
$timeout(function() {
|
||||
self.selectedWalletId = client.credentials.walletId;
|
||||
self.selectedWalletName = client.credentials.walletName;
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
this.getPaymentMethods = function(token) {
|
||||
coinbaseService.getPaymentMethods(token, function(err, p) {
|
||||
if (err) {
|
||||
self.error = err;
|
||||
return;
|
||||
}
|
||||
self.paymentMethods = [];
|
||||
lodash.each(p.data, function(pm) {
|
||||
if (pm.allow_sell) {
|
||||
self.paymentMethods.push(pm);
|
||||
}
|
||||
if (pm.allow_sell && pm.primary_sell) {
|
||||
$scope.selectedPaymentMethod = pm;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
walletService.publishAndSign(wallet, txp, function(err, txp) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, txp);
|
||||
}, onSendStatusChange);
|
||||
};
|
||||
|
||||
this.getPrice = function(token) {
|
||||
var currency = 'USD';
|
||||
coinbaseService.sellPrice(token, currency, function(err, s) {
|
||||
if (err) return;
|
||||
self.sellPrice = s.data || null;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openWalletsModal = function(wallets) {
|
||||
self.error = null;
|
||||
|
||||
$scope.type = 'SELL';
|
||||
$scope.wallets = wallets;
|
||||
$scope.noColor = true;
|
||||
$scope.self = self;
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/wallets.html', {
|
||||
scope: $scope,
|
||||
animation: 'slide-in-up'
|
||||
}).then(function(modal) {
|
||||
$scope.walletsModal = modal;
|
||||
$scope.walletsModal.show();
|
||||
});
|
||||
|
||||
$scope.$on('walletSelected', function(ev, walletId) {
|
||||
$timeout(function() {
|
||||
client = profileService.getClient(walletId);
|
||||
self.selectedWalletId = walletId;
|
||||
self.selectedWalletName = client.credentials.walletName;
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
$scope.walletsModal.hide();
|
||||
});
|
||||
};
|
||||
|
||||
this.depositFunds = function(token, account) {
|
||||
self.error = null;
|
||||
if ($scope.amount) {
|
||||
this.createTx(token, account, $scope.amount)
|
||||
} else if ($scope.fiat) {
|
||||
var btcValue = ($scope.fiat / self.sellPrice.amount).toFixed(8);
|
||||
this.createTx(token, account, btcValue);
|
||||
}
|
||||
};
|
||||
|
||||
this.sellRequest = function(token, account, ctx) {
|
||||
self.error = null;
|
||||
if (!ctx.amount) return;
|
||||
var accountId = account.id;
|
||||
var data = ctx.amount;
|
||||
data['payment_method'] = $scope.selectedPaymentMethod.id || null;
|
||||
ongoingProcess.set('Sending request...', true);
|
||||
coinbaseService.sellRequest(token, accountId, data, function(err, sell) {
|
||||
ongoingProcess.set('Sending request...', false);
|
||||
if (err) {
|
||||
self.error = err;
|
||||
return;
|
||||
}
|
||||
self.sellInfo = sell.data;
|
||||
});
|
||||
};
|
||||
|
||||
this.confirmSell = function(token, account, sell) {
|
||||
self.error = null;
|
||||
var accountId = account.id;
|
||||
var sellId = sell.id;
|
||||
ongoingProcess.set('Selling Bitcoin...', true);
|
||||
coinbaseService.sellCommit(token, accountId, sellId, function(err, data) {
|
||||
ongoingProcess.set('Selling Bitcoin...', false);
|
||||
if (err) {
|
||||
self.error = err;
|
||||
return;
|
||||
}
|
||||
self.success = data.data;
|
||||
$scope.$emit('Local/CoinbaseTx');
|
||||
});
|
||||
};
|
||||
|
||||
this.createTx = function(token, account, amount) {
|
||||
self.error = null;
|
||||
|
||||
if (!client) {
|
||||
self.error = 'No wallet selected';
|
||||
var checkTransaction = lodash.throttle(function(count, txp) {
|
||||
$log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5');
|
||||
// TX amount in BTC
|
||||
var satToBtc = 1 / 100000000;
|
||||
var amountBTC = (txp.amount * satToBtc).toFixed(8);
|
||||
coinbaseService.init(function(err, res) {
|
||||
if (err) {
|
||||
$log.error(err);
|
||||
checkTransaction(count, txp);
|
||||
return;
|
||||
}
|
||||
var accessToken = res.accessToken;
|
||||
var accountId = res.accountId;
|
||||
var sellPrice = null;
|
||||
|
||||
coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, sell) {
|
||||
if (err) {
|
||||
$log.debug(err);
|
||||
checkTransaction(count, txp);
|
||||
return;
|
||||
}
|
||||
sellPrice = sell.data;
|
||||
|
||||
var accountId = account.id;
|
||||
var dataSrc = {
|
||||
name: 'Received from Copay: ' + self.selectedWalletName
|
||||
};
|
||||
var outputs = [];
|
||||
var config = configService.getSync();
|
||||
var configWallet = config.wallet;
|
||||
var walletSettings = configWallet.settings;
|
||||
|
||||
|
||||
ongoingProcess.set('Creating Transaction...', true);
|
||||
$timeout(function() {
|
||||
|
||||
coinbaseService.createAddress(token, accountId, dataSrc, function(err, data) {
|
||||
coinbaseService.getTransactions(accessToken, accountId, function(err, ctxs) {
|
||||
if (err) {
|
||||
ongoingProcess.set('Creating Transaction...', false);
|
||||
self.error = err;
|
||||
$log.debug(err);
|
||||
checkTransaction(count, txp);
|
||||
return;
|
||||
}
|
||||
|
||||
var address, comment;
|
||||
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.selectedPaymentMethodId.value;
|
||||
ctx['status'] = 'pending'; // Forcing "pending" status to process later
|
||||
ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data;
|
||||
ctx['sell_price_amount'] = sellPrice ? sellPrice.amount : '';
|
||||
ctx['sell_price_currency'] = sellPrice ? sellPrice.currency : 'USD';
|
||||
ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name;
|
||||
coinbaseService.savePendingTransaction(ctx, null, function(err) {
|
||||
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
|
||||
if (err) $log.debug(err);
|
||||
});
|
||||
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);
|
||||
} else {
|
||||
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
|
||||
showError('No transaction found');
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 8000, {
|
||||
'leading': true
|
||||
});
|
||||
|
||||
address = data.data.address;
|
||||
amount = parseInt((amount * 100000000).toFixed(0));
|
||||
comment = 'Send funds to Coinbase Account: ' + account.name;
|
||||
var statusChangeHandler = function (processName, showName, isOn) {
|
||||
$log.debug('statusChangeHandler: ', processName, showName, isOn);
|
||||
if ( processName == 'sellingBitcoin' && !isOn) {
|
||||
$scope.sendStatus = 'success';
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 100);
|
||||
} else if (showName) {
|
||||
$scope.sendStatus = showName;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
coinbaseService.setCredentials();
|
||||
|
||||
$scope.isFiat = data.stateParams.currency ? true : false;
|
||||
[amount, currency, $scope.amountUnitStr] = coinbaseService.parseAmount(
|
||||
data.stateParams.amount,
|
||||
data.stateParams.currency);
|
||||
|
||||
$scope.priceSensitivity = coinbaseService.priceSensitivity;
|
||||
$scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity };
|
||||
|
||||
$scope.network = coinbaseService.getNetwork();
|
||||
$scope.wallets = profileService.getWallets({
|
||||
m: 1, // Only 1-signature wallet
|
||||
onlyComplete: true,
|
||||
network: $scope.network
|
||||
});
|
||||
$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.sellRequest = function() {
|
||||
ongoingProcess.set('connectingCoinbase', true);
|
||||
coinbaseService.init(function(err, res) {
|
||||
if (err) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
showErrorAndBack(err);
|
||||
return;
|
||||
}
|
||||
var accessToken = res.accessToken;
|
||||
var accountId = res.accountId;
|
||||
var dataSrc = {
|
||||
amount: amount,
|
||||
currency: currency,
|
||||
payment_method: $scope.selectedPaymentMethodId.value,
|
||||
quote: true
|
||||
};
|
||||
coinbaseService.sellRequest(accessToken, accountId, dataSrc, function(err, data) {
|
||||
ongoingProcess.set('connectingCoinbase', false);
|
||||
if (err) {
|
||||
showErrorAndBack(err);
|
||||
return;
|
||||
}
|
||||
$scope.sellRequestInfo = data.data;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.sellConfirm = function() {
|
||||
var config = configService.getSync();
|
||||
var configWallet = config.wallet;
|
||||
var walletSettings = configWallet.settings;
|
||||
|
||||
var message = 'Selling bitcoin for ' + amount + ' ' + currency;
|
||||
var okText = 'Confirm';
|
||||
var cancelText = 'Cancel';
|
||||
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
|
||||
if (!ok) return;
|
||||
|
||||
ongoingProcess.set('sellingBitcoin', true, statusChangeHandler);
|
||||
coinbaseService.init(function(err, res) {
|
||||
if (err) {
|
||||
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
var accessToken = res.accessToken;
|
||||
var accountId = res.accountId;
|
||||
|
||||
var dataSrc = {
|
||||
name: 'Received from ' + appConfigService.nameCase
|
||||
};
|
||||
coinbaseService.createAddress(accessToken, accountId, dataSrc, function(err, data) {
|
||||
if (err) {
|
||||
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
var outputs = [];
|
||||
var toAddress = data.data.address;
|
||||
var amountSat = parseInt(($scope.sellRequestInfo.amount.amount * 100000000).toFixed(0));
|
||||
var comment = 'Sell bitcoin (Coinbase)';
|
||||
|
||||
outputs.push({
|
||||
'toAddress': address,
|
||||
'amount': amount,
|
||||
'toAddress': toAddress,
|
||||
'amount': amountSat,
|
||||
'message': comment
|
||||
});
|
||||
|
||||
var txp = {
|
||||
toAddress: address,
|
||||
amount: amount,
|
||||
toAddress: toAddress,
|
||||
amount: amountSat,
|
||||
outputs: outputs,
|
||||
message: comment,
|
||||
payProUrl: null,
|
||||
|
|
@ -189,73 +260,47 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController',
|
|||
feeLevel: walletSettings.feeLevel || 'normal'
|
||||
};
|
||||
|
||||
walletService.createTx(client, txp, function(err, createdTxp) {
|
||||
walletService.createTx($scope.wallet, txp, function(err, ctxp) {
|
||||
if (err) {
|
||||
$log.debug(err);
|
||||
ongoingProcess.set('Creating Transaction...', false);
|
||||
self.error = {
|
||||
errors: [{
|
||||
message: 'Could not create transaction: ' + err.message
|
||||
}]
|
||||
};
|
||||
$scope.$apply();
|
||||
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
ongoingProcess.set('Creating Transaction...', false);
|
||||
$scope.$emit('Local/NeedsConfirmation', createdTxp, function(accept) {
|
||||
if (accept) {
|
||||
self.confirmTx(createdTxp, function(err, tx) {
|
||||
ongoingProcess.clear();
|
||||
if (err) {
|
||||
self.error = {
|
||||
errors: [{
|
||||
message: 'Could not create transaction: ' + err.message
|
||||
}]
|
||||
};
|
||||
return;
|
||||
}
|
||||
ongoingProcess.set('Checking Transaction...', false);
|
||||
coinbaseService.getTransactions(token, accountId, function(err, ctxs) {
|
||||
if (err) {
|
||||
$log.debug(err);
|
||||
return;
|
||||
}
|
||||
lodash.each(ctxs.data, function(ctx) {
|
||||
if (ctx.type == 'send' && ctx.from) {
|
||||
ongoingProcess.clear();
|
||||
if (ctx.status == 'completed') {
|
||||
self.sellRequest(token, account, ctx);
|
||||
} else {
|
||||
// Save to localstorage
|
||||
ctx['price_sensitivity'] = $scope.selectedPriceSensitivity;
|
||||
ctx['sell_price_amount'] = self.sellPrice ? self.sellPrice.amount : '';
|
||||
ctx['sell_price_currency'] = self.sellPrice ? self.sellPrice.currency : 'USD';
|
||||
ctx['description'] = 'Copay Wallet: ' + client.credentials.walletName;
|
||||
coinbaseService.savePendingTransaction(ctx, null, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
self.sendInfo = ctx;
|
||||
$timeout(function() {
|
||||
$scope.$emit('Local/CoinbaseTx');
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
go.path('coinbase');
|
||||
$log.debug('Transaction created.');
|
||||
publishAndSign($scope.wallet, ctxp, function() {}, function(err, txSent) {
|
||||
if (err) {
|
||||
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
|
||||
showError(err);
|
||||
return;
|
||||
}
|
||||
$log.debug('Transaction broadcasted. Wait for Coinbase confirmation...');
|
||||
checkTransaction(1, txSent);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.confirmTx = function(txp, cb) {
|
||||
$scope.showWalletSelector = function() {
|
||||
$scope.walletSelectorTitle = 'Sell From';
|
||||
$scope.showWallets = true;
|
||||
};
|
||||
|
||||
// TODO see walletService createAndPublish
|
||||
};
|
||||
$scope.onWalletSelect = function(wallet) {
|
||||
$scope.wallet = wallet;
|
||||
};
|
||||
|
||||
});
|
||||
$scope.goBackHome = function() {
|
||||
$scope.sendStatus = '';
|
||||
$ionicHistory.nextViewOptions({
|
||||
disableAnimate: true,
|
||||
historyRoot: true
|
||||
});
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.home').then(function() {
|
||||
$state.transitionTo('tabs.buyandsell.coinbase');
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('tabHomeController',
|
||||
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, bitpayCardService, startupService, addressbookService, feedbackService, bwcError) {
|
||||
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, bitpayCardService, startupService, addressbookService, feedbackService, bwcError, coinbaseService) {
|
||||
var wallet;
|
||||
var listeners = [];
|
||||
var notifications = [];
|
||||
|
|
@ -83,6 +83,10 @@ angular.module('copayApp.controllers').controller('tabHomeController',
|
|||
var wallet = profileService.getWallet(walletId);
|
||||
updateWallet(wallet);
|
||||
if ($scope.recentTransactionsEnabled) getNotifications();
|
||||
if (type == 'NewBlock' && n && n.data && n.data.network == 'livenet') {
|
||||
// Update Coinbase
|
||||
coinbaseService.updatePendingTransactions();
|
||||
}
|
||||
}),
|
||||
$rootScope.$on('Local/TxAction', function(e, walletId) {
|
||||
$log.debug('Got action for wallet ' + walletId);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, appConfigService, $ionicModal, $log, lodash, uxLanguage, platformInfo, profileService, feeService, configService, externalLinkService, bitpayCardService, storageService, glideraService, gettextCatalog) {
|
||||
angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, appConfigService, $log, lodash, uxLanguage, platformInfo, profileService, feeService, configService, externalLinkService, bitpayCardService, storageService, glideraService, coinbaseService, gettextCatalog) {
|
||||
|
||||
var updateConfig = function() {
|
||||
var isCordova = platformInfo.isCordova;
|
||||
|
|
@ -26,6 +26,7 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
|
|||
|
||||
$scope.bitpayCardEnabled = config.bitpayCard.enabled;
|
||||
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
|
||||
$scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp;
|
||||
|
||||
if ($scope.bitpayCardEnabled) {
|
||||
bitpayCardService.getBitpayDebitCards(function(err, cards) {
|
||||
|
|
@ -41,6 +42,13 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
|
|||
});
|
||||
}
|
||||
|
||||
if ($scope.coinbaseEnabled) {
|
||||
coinbaseService.setCredentials();
|
||||
coinbaseService.getStoredToken(function(at) {
|
||||
$scope.coinbaseToken = at;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -142,10 +142,6 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
controller: 'glideraUriController',
|
||||
templateUrl: 'views/glideraUri.html'
|
||||
})
|
||||
.state('uricoinbase', {
|
||||
url: '/uri-coinbase/:url',
|
||||
templateUrl: 'views/coinbaseUri.html'
|
||||
})
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
@ -926,22 +922,52 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
*
|
||||
*/
|
||||
|
||||
.state('coinbase', {
|
||||
url: '/coinbase',
|
||||
templateUrl: 'views/coinbase.html'
|
||||
})
|
||||
.state('preferencesCoinbase', {
|
||||
url: '/preferencesCoinbase',
|
||||
templateUrl: 'views/preferencesCoinbase.html'
|
||||
})
|
||||
.state('buyCoinbase', {
|
||||
url: '/buycoinbase',
|
||||
templateUrl: 'views/buyCoinbase.html'
|
||||
})
|
||||
.state('sellCoinbase', {
|
||||
url: '/sellcoinbase',
|
||||
templateUrl: 'views/sellCoinbase.html'
|
||||
})
|
||||
.state('tabs.buyandsell.coinbase', {
|
||||
url: '/coinbase/:code',
|
||||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'coinbaseController',
|
||||
controllerAs: 'coinbase',
|
||||
templateUrl: 'views/coinbase.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.preferences.coinbase', {
|
||||
url: '/coinbase',
|
||||
views: {
|
||||
'tab-settings@tabs': {
|
||||
controller: 'preferencesCoinbaseController',
|
||||
templateUrl: 'views/preferencesCoinbase.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.buyandsell.coinbase.amount', {
|
||||
url: '/amount/:nextStep/:currency',
|
||||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'amountController',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.buyandsell.coinbase.buy', {
|
||||
url: '/buy/:amount/:currency',
|
||||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'buyCoinbaseController',
|
||||
templateUrl: 'views/buyCoinbase.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.buyandsell.coinbase.sell', {
|
||||
url: '/sell/:amount/:currency',
|
||||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'sellCoinbaseController',
|
||||
templateUrl: 'views/sellCoinbase.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,11 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('coinbaseService', function($http, $log, platformInfo, lodash, storageService, configService) {
|
||||
angular.module('copayApp.services').factory('coinbaseService', function($http, $log, $window, $filter, platformInfo, lodash, storageService, configService, appConfigService, txFormatService) {
|
||||
var root = {};
|
||||
var credentials = {};
|
||||
var isCordova = platformInfo.isCordova;
|
||||
var isNW = platformInfo.isNW;
|
||||
|
||||
root.setCredentials = function(network) {
|
||||
root.priceSensitivity = [
|
||||
{
|
||||
value: 0.5,
|
||||
name: '0.5%'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
name: '1%'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: '2%'
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
name: '5%'
|
||||
},
|
||||
{
|
||||
value: 10,
|
||||
name: '10%'
|
||||
}
|
||||
];
|
||||
|
||||
root.selectedPriceSensitivity = root.priceSensitivity[1];
|
||||
|
||||
root.setCredentials = function() {
|
||||
|
||||
if (!$window.externalServices || !$window.externalServices.coinbase) {
|
||||
return;
|
||||
}
|
||||
|
||||
var coinbase = $window.externalServices.coinbase;
|
||||
|
||||
/*
|
||||
* Development: 'testnet'
|
||||
* Production: 'livenet'
|
||||
*/
|
||||
credentials.NETWORK = 'livenet';
|
||||
|
||||
// Coinbase permissions
|
||||
credentials.SCOPE = ''
|
||||
+ 'wallet:accounts:read,'
|
||||
+ 'wallet:addresses:read,'
|
||||
|
|
@ -20,26 +60,78 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
+ 'wallet:transactions:send,'
|
||||
+ 'wallet:payment-methods:read';
|
||||
|
||||
// NW has a bug with Window Object
|
||||
if (isCordova) {
|
||||
credentials.REDIRECT_URI = 'copay://coinbase';
|
||||
credentials.REDIRECT_URI = coinbase.redirect_uri.mobile;
|
||||
} else {
|
||||
credentials.REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob';
|
||||
credentials.REDIRECT_URI = coinbase.redirect_uri.desktop;
|
||||
}
|
||||
|
||||
if (network == 'testnet') {
|
||||
credentials.HOST = 'https://sandbox.coinbase.com';
|
||||
credentials.API = 'https://api.sandbox.coinbase.com';
|
||||
credentials.CLIENT_ID = '6cdcc82d5d46654c46880e93ab3d2a43c639776347dd88022904bd78cd067841';
|
||||
credentials.CLIENT_SECRET = '228cb6308951f4b6f41ba010c7d7981b2721a493c40c50fd2425132dcaccce59';
|
||||
if (credentials.NETWORK == 'testnet') {
|
||||
credentials.HOST = coinbase.sandbox.host;
|
||||
credentials.API = coinbase.sandbox.api;
|
||||
credentials.CLIENT_ID = coinbase.sandbox.client_id;
|
||||
credentials.CLIENT_SECRET = coinbase.sandbox.client_secret;
|
||||
}
|
||||
else {
|
||||
credentials.HOST = 'https://coinbase.com';
|
||||
credentials.API = 'https://api.coinbase.com';
|
||||
credentials.CLIENT_ID = window.coinbase_client_id;
|
||||
credentials.CLIENT_SECRET = window.coinbase_client_secret;
|
||||
credentials.HOST = coinbase.production.host;
|
||||
credentials.API = coinbase.production.api;
|
||||
credentials.CLIENT_ID = coinbase.production.client_id;
|
||||
credentials.CLIENT_SECRET = coinbase.production.client_secret;
|
||||
};
|
||||
};
|
||||
|
||||
var _afterTokenReceived = function(data, cb) {
|
||||
if (data && data.access_token && data.refresh_token) {
|
||||
storageService.setCoinbaseToken(credentials.NETWORK, data.access_token, function() {
|
||||
storageService.setCoinbaseRefreshToken(credentials.NETWORK, data.refresh_token, function() {
|
||||
return cb(null, data.access_token);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return cb('Could not get the access token');
|
||||
}
|
||||
};
|
||||
|
||||
root.getNetwork = function() {
|
||||
return credentials.NETWORK;
|
||||
};
|
||||
|
||||
root.getStoredToken = function(cb) {
|
||||
storageService.getCoinbaseToken(credentials.NETWORK, function(err, accessToken) {
|
||||
if (err || !accessToken) return cb();
|
||||
return cb(accessToken);
|
||||
});
|
||||
};
|
||||
|
||||
root.getAvailableCurrency = function() {
|
||||
var config = configService.getSync().wallet.settings;
|
||||
// ONLY "USD"
|
||||
switch(config.alternativeIsoCode) {
|
||||
default : return 'USD'
|
||||
};
|
||||
};
|
||||
|
||||
root.parseAmount = function(amount, currency) {
|
||||
var config = configService.getSync().wallet.settings;
|
||||
var satToBtc = 1 / 100000000;
|
||||
var unitToSatoshi = config.unitToSatoshi;
|
||||
var amountUnitStr;
|
||||
|
||||
// IF 'USD'
|
||||
if (currency) {
|
||||
amountUnitStr = $filter('formatFiatAmount')(amount) + ' ' + currency;
|
||||
} else {
|
||||
var amountSat = parseInt((amount * unitToSatoshi).toFixed(0));
|
||||
amountUnitStr = txFormatService.formatAmountStr(amountSat);
|
||||
// convert unit to BTC
|
||||
amount = (amountSat * satToBtc).toFixed(8);
|
||||
currency = 'BTC';
|
||||
}
|
||||
|
||||
return [amount, currency, amountUnitStr];
|
||||
};
|
||||
|
||||
root.getOauthCodeUrl = function() {
|
||||
return credentials.HOST
|
||||
+ '/oauth/authorize?response_type=code&client_id='
|
||||
|
|
@ -54,7 +146,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
root.getToken = function(code, cb) {
|
||||
var req = {
|
||||
method: 'POST',
|
||||
url: credentials.API + '/oauth/token',
|
||||
url: credentials.HOST + '/oauth/token',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
|
|
@ -71,18 +163,18 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
$http(req).then(function(data) {
|
||||
$log.info('Coinbase Authorization Access Token: SUCCESS');
|
||||
// Show pending task from the UI
|
||||
storageService.setNextStep('BuyAndSell', true, function(err) {});
|
||||
return cb(null, data.data);
|
||||
storageService.setNextStep('BuyAndSell', 'true', function(err) {});
|
||||
_afterTokenReceived(data.data, cb);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Authorization Access Token: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
return cb(data.data || 'Could not get the access token');
|
||||
});
|
||||
};
|
||||
|
||||
root.refreshToken = function(refreshToken, cb) {
|
||||
var _refreshToken = function(refreshToken, cb) {
|
||||
var req = {
|
||||
method: 'POST',
|
||||
url: credentials.API + '/oauth/token',
|
||||
url: credentials.HOST + '/oauth/token',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
|
|
@ -98,10 +190,58 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
|
||||
$http(req).then(function(data) {
|
||||
$log.info('Coinbase Refresh Access Token: SUCCESS');
|
||||
return cb(null, data.data);
|
||||
_afterTokenReceived(data.data, cb);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Refresh Access Token: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
return cb(data.data || 'Could not get the access token');
|
||||
});
|
||||
};
|
||||
|
||||
var _getMainAccountId = function(accessToken, cb) {
|
||||
root.getAccounts(accessToken, function(err, a) {
|
||||
if (err) return cb(err);
|
||||
var data = a.data;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].primary && data[i].type == 'wallet') {
|
||||
return cb(null, data[i].id);
|
||||
}
|
||||
}
|
||||
root.logout(function() {});
|
||||
return cb('Your primary account should be a WALLET. Set your wallet account as primary and try again');
|
||||
});
|
||||
};
|
||||
|
||||
root.init = function(cb) {
|
||||
if (lodash.isEmpty(credentials.CLIENT_ID)) {
|
||||
return cb('Coinbase is Disabled');
|
||||
}
|
||||
$log.debug('Trying to initialise Coinbase...');
|
||||
|
||||
storageService.getCoinbaseToken(credentials.NETWORK, function(err, accessToken) {
|
||||
if (err || !accessToken) return cb();
|
||||
else {
|
||||
_getMainAccountId(accessToken, function(err, accountId) {
|
||||
if (err) {
|
||||
if (err.errors && err.errors[0] && err.errors[0].id == 'expired_token') {
|
||||
$log.debug('Refresh token');
|
||||
storageService.getCoinbaseRefreshToken(credentials.NETWORK, function(err, refreshToken) {
|
||||
if (err) return cb(err);
|
||||
_refreshToken(refreshToken, function(err, newToken) {
|
||||
if (err) return cb(err);
|
||||
_getMainAccountId(newToken, function(err, accountId) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, {accessToken: newToken, accountId: accountId});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return cb(err);
|
||||
}
|
||||
} else {
|
||||
return cb(null, {accessToken: accessToken, accountId: accountId});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -124,7 +264,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
return cb(null, data.data);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Get Accounts: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
return cb(data.data || 'Could not get the accounts');
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -172,6 +312,17 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
});
|
||||
};
|
||||
|
||||
root.getAddressTransactions = function(token, accountId, addressId, cb) {
|
||||
if (!token) return cb('Invalid Token');
|
||||
$http(_get('/accounts/' + accountId + '/addresses/' + addressId + '/transactions', token)).then(function(data) {
|
||||
$log.info('Coinbase Address s Transactions: SUCCESS');
|
||||
return cb(null, data.data);
|
||||
}, function(data) {
|
||||
$log.error('Coinbase Address s Transactions: ERROR ' + data.statusText);
|
||||
return cb(data.data);
|
||||
});
|
||||
};
|
||||
|
||||
root.getTransactions = function(token, accountId, cb) {
|
||||
if (!token) return cb('Invalid Token');
|
||||
$http(_get('/accounts/' + accountId + '/transactions', token)).then(function(data) {
|
||||
|
|
@ -252,7 +403,8 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
amount: data.amount,
|
||||
currency: data.currency,
|
||||
payment_method: data.payment_method || null,
|
||||
commit: data.commit || false
|
||||
commit: data.commit || false,
|
||||
quote: data.quote || false
|
||||
};
|
||||
$http(_post('/accounts/' + accountId + '/sells', token, data)).then(function(data) {
|
||||
$log.info('Coinbase Sell Request: SUCCESS');
|
||||
|
|
@ -278,7 +430,8 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
amount: data.amount,
|
||||
currency: data.currency,
|
||||
payment_method: data.payment_method || null,
|
||||
commit: false
|
||||
commit: data.commit || false,
|
||||
quote: data.quote || false
|
||||
};
|
||||
$http(_post('/accounts/' + accountId + '/buys', token, data)).then(function(data) {
|
||||
$log.info('Coinbase Buy Request: SUCCESS');
|
||||
|
|
@ -330,10 +483,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
};
|
||||
|
||||
// Pending transactions
|
||||
|
||||
|
||||
root.savePendingTransaction = function(ctx, opts, cb) {
|
||||
var network = configService.getSync().coinbase.testnet ? 'testnet' : 'livenet';
|
||||
storageService.getCoinbaseTxs(network, function(err, oldTxs) {
|
||||
_savePendingTransaction(ctx, opts, cb);
|
||||
};
|
||||
|
||||
var _savePendingTransaction = function(ctx, opts, cb) {
|
||||
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, oldTxs) {
|
||||
if (lodash.isString(oldTxs)) {
|
||||
oldTxs = JSON.parse(oldTxs);
|
||||
}
|
||||
|
|
@ -350,23 +506,200 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
|||
}
|
||||
tx = JSON.stringify(tx);
|
||||
|
||||
storageService.setCoinbaseTxs(network, tx, function(err) {
|
||||
storageService.setCoinbaseTxs(credentials.NETWORK, tx, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
root.getPendingTransactions = function(cb) {
|
||||
var network = configService.getSync().coinbase.testnet ? 'testnet' : 'livenet';
|
||||
storageService.getCoinbaseTxs(network, function(err, txs) {
|
||||
var _txs = txs ? JSON.parse(txs) : {};
|
||||
return cb(err, _txs);
|
||||
root.getPendingTransactions = function(coinbasePendingTransactions) {
|
||||
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) {
|
||||
txs = txs ? JSON.parse(txs) : {};
|
||||
coinbasePendingTransactions.data = lodash.isEmpty(txs) ? null : txs;
|
||||
|
||||
root.init(function(err, data) {
|
||||
if (err || lodash.isEmpty(data)) {
|
||||
if (err) $log.error(err);
|
||||
return;
|
||||
}
|
||||
var accessToken = data.accessToken;
|
||||
var accountId = data.accountId;
|
||||
|
||||
lodash.forEach(coinbasePendingTransactions.data, function(dataFromStorage, txId) {
|
||||
if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
|
||||
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
||||
dataFromStorage.status == 'error' ||
|
||||
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed'))
|
||||
return;
|
||||
root.getTransaction(accessToken, accountId, txId, function(err, tx) {
|
||||
if (err || lodash.isEmpty(tx) || (tx.data && tx.data.error)) {
|
||||
_savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: (tx.data && tx.data.error) ? tx.data.error : err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
_updateCoinbasePendingTransactions(dataFromStorage, tx.data);
|
||||
coinbasePendingTransactions.data[txId] = dataFromStorage;
|
||||
if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) {
|
||||
root.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) {
|
||||
if (err) {
|
||||
_savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
_sellPending(dataFromStorage, accessToken, accountId, coinbasePendingTransactions);
|
||||
} else {
|
||||
var error = {
|
||||
errors: [{
|
||||
message: 'Price falls over the selected percentage'
|
||||
}]
|
||||
};
|
||||
_savePendingTransaction(dataFromStorage, {
|
||||
status: 'error',
|
||||
error: error
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(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);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
root.logout = function(network, cb) {
|
||||
storageService.removeCoinbaseToken(network, function() {
|
||||
storageService.removeCoinbaseRefreshToken(network, function() {
|
||||
root.updatePendingTransactions = lodash.throttle(function() {
|
||||
$log.debug('Updating pending transactions...');
|
||||
root.setCredentials();
|
||||
var pendingTransactions = { data: {} };
|
||||
root.getPendingTransactions(pendingTransactions);
|
||||
}, 20000);
|
||||
|
||||
var _updateTxs = function(coinbasePendingTransactions) {
|
||||
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;
|
||||
data['payment_method'] = tx.payment_method || null;
|
||||
data['commit'] = true;
|
||||
root.sellRequest(accessToken, accountId, data, function(err, res) {
|
||||
if (err) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
} else {
|
||||
if (res.data && !res.data.transaction) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
_savePendingTransaction(tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
root.getTransaction(accessToken, accountId, res.data.transaction.id, function(err, updatedTx) {
|
||||
_savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _sendToWallet = function(tx, accessToken, accountId, coinbasePendingTransactions) {
|
||||
if (!tx) return;
|
||||
var desc = appConfigService.nameCase + ' Wallet';
|
||||
var data = {
|
||||
to: tx.toAddr,
|
||||
amount: tx.amount.amount,
|
||||
currency: tx.amount.currency,
|
||||
description: desc
|
||||
};
|
||||
root.sendTo(accessToken, accountId, data, function(err, res) {
|
||||
if (err) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
} else {
|
||||
if (res.data && !res.data.id) {
|
||||
_savePendingTransaction(tx, {
|
||||
status: 'error',
|
||||
error: err
|
||||
}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
return;
|
||||
}
|
||||
root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) {
|
||||
_savePendingTransaction(tx, {
|
||||
remove: true
|
||||
}, function(err) {
|
||||
_savePendingTransaction(sendTx.data, {}, function(err) {
|
||||
if (err) $log.debug(err);
|
||||
_updateTxs(coinbasePendingTransactions);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _updateCoinbasePendingTransactions = function(obj /*, …*/ ) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
for (var prop in arguments[i]) {
|
||||
var val = arguments[i][prop];
|
||||
if (typeof val == "object")
|
||||
_updateCoinbasePendingTransactions(obj[prop], val);
|
||||
else
|
||||
obj[prop] = val ? val : obj[prop];
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
root.logout = function(cb) {
|
||||
storageService.removeCoinbaseToken(credentials.NETWORK, function() {
|
||||
storageService.removeCoinbaseRefreshToken(credentials.NETWORK, function() {
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
|||
},
|
||||
|
||||
coinbase: {
|
||||
enabled: false, //disable coinbase for this release
|
||||
enabled: true,
|
||||
testnet: false
|
||||
},
|
||||
|
||||
|
|
@ -222,10 +222,6 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
|||
configCache.aliasFor = configCache.aliasFor || {};
|
||||
configCache.emailFor = configCache.emailFor || {};
|
||||
|
||||
// Coinbase
|
||||
// Disabled for testnet
|
||||
configCache.coinbase.testnet = false;
|
||||
|
||||
$log.debug('Preferences read:', configCache)
|
||||
|
||||
lodash.each(root._queue, function(x) {
|
||||
|
|
|
|||
|
|
@ -126,9 +126,16 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
url: data
|
||||
});
|
||||
} else if (data && data.indexOf(appConfigService.name + '://coinbase') === 0) {
|
||||
return $state.go('uricoinbase', {
|
||||
url: data
|
||||
var code = getParameterByName('code', data);
|
||||
$state.go('tabs.home', {}, {
|
||||
'reload': true,
|
||||
'notify': $state.current.name == 'tabs.home' ? false : true
|
||||
}).then(function() {
|
||||
$state.transitionTo('tabs.buyandsell.coinbase', {
|
||||
code: code
|
||||
});
|
||||
});
|
||||
return true;
|
||||
|
||||
// BitPayCard Authentication
|
||||
} else if (data && data.indexOf(appConfigService.name + '://') === 0) {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@
|
|||
padding: 24px 0;
|
||||
font-size: 18px;
|
||||
.title {
|
||||
float: left;
|
||||
padding-top: 10px;
|
||||
color: $dark-gray;
|
||||
font-weight: bold;
|
||||
|
|
@ -89,6 +88,9 @@
|
|||
color: $light-gray;
|
||||
font-size: 12px;
|
||||
}
|
||||
.select {
|
||||
margin: 10px 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.amount {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,129 @@
|
|||
.coinbase-preferences {
|
||||
ul {
|
||||
font-size: 14px;
|
||||
background: white;
|
||||
li {
|
||||
padding: 16px 10px 16px 16px;
|
||||
border-bottom: 1px solid #E9E9EC;
|
||||
#coinbase {
|
||||
$item-lateral-padding: 20px;
|
||||
$item-vertical-padding: 10px;
|
||||
$item-border-color: #EFEFEF;
|
||||
$item-label-color: #6C6C6E;
|
||||
@extend .deflash-blue;
|
||||
|
||||
.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.5rem;
|
||||
|
||||
img {
|
||||
margin-right: 1rem;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
}
|
||||
|
||||
span {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
.amount-label{
|
||||
line-height: 30px;
|
||||
.amount{
|
||||
font-size: 38px;
|
||||
margin-bottom: .5rem;
|
||||
|
||||
> .unit {
|
||||
font-family: "Roboto-Light";
|
||||
}
|
||||
}
|
||||
.alternative {
|
||||
font-size: 16px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coinbase-last-transactions-content {
|
||||
background: #fff;
|
||||
padding: 0.8rem 1rem;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #E4E8EC;
|
||||
}
|
||||
|
||||
.coinbase-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue