Merge pull request #5334 from cmgustavo/feat/coinbase-integration

Re-enable Coinbase integration
This commit is contained in:
Matias Alejo Garcia 2017-01-17 14:50:19 -03:00 committed by GitHub
commit 6bbb1fd442
30 changed files with 1632 additions and 1241 deletions

View file

@ -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) {

View file

@ -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');
});
};
});

View file

@ -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();
}
});
});

View file

@ -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));
}
});

View file

@ -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);

View file

@ -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();
};
});

View file

@ -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();
});
};
});

View file

@ -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;
});
});
});
});

View file

@ -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');
});
};
});

View file

@ -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);

View file

@ -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;
});
}
});
};