From af932b3e59f5a33414a92e6af1cb4e2bbe8997d3 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 8 Dec 2016 11:04:07 -0300 Subject: [PATCH 01/34] Coinbase: first step integration, connect account and main view --- src/js/controllers/coinbase.js | 148 ++++++++------ src/js/routes.js | 40 ++-- src/js/services/coinbaseService.js | 300 +++++++++++++++++++++++++---- src/js/services/configService.js | 6 +- www/views/advancedSettings.html | 7 +- www/views/buyandsell.html | 4 + www/views/coinbase.html | 225 +++++++++------------- www/views/tab-home.html | 10 +- 8 files changed, 481 insertions(+), 259 deletions(-) diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index 76b93c9c7..ef2ef3749 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -1,25 +1,53 @@ '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($rootScope, $scope, $timeout, $ionicModal, $log, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, gettextCatalog, externalLinkService) { - var isNW = platformInfo.isNW; + var isNW = platformInfo.isNW; - if (platformInfo.isCordova && StatusBar.isVisible) { - StatusBar.backgroundColorByHexString("#4B6178"); - } + var init = function() { + ongoingProcess.set('connectingCoinbase', true); + coinbaseService.init($scope.accessToken, function(err, data) { +console.log('[coinbase.js:9]',data); //TODO) + ongoingProcess.set('connectingCoinbase', false); + if (err || lodash.isEmpty(data)) { + if (err) { + popupService.showAlert(gettextCatalog.getString('Error'), err); + } + return; + } + // Updating accessToken and accountId + $timeout(function() { + $scope.accessToken = data.accessToken; + $scope.accountId = data.accountId; + $scope.updateTransactions(); + $scope.$apply(); + }, 100); + }); + }; - 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' - }); + $scope.updateTransactions = function() { + $log.debug('Checking for transactions...'); + coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) { +console.log('[coinbase.js:43]',txs); //TODO) + $scope.pendingTransactions = txs; + }); + + }; + + this.openAuthenticateWindow = function() { + var oauthUrl = this.getAuthenticateUrl(); + externalLinkService.open(oauthUrl); + /* + * Not working (NW bug) + if (!isNW) { + externalLinkService.open(oauthUrl); + } else { + var self = this; + var gui = require('nw.gui'); + gui.Window.open(oauthUrl, { + focus: true, + position: 'center' + }, function(win) { win.on('loaded', function() { var title = win.title; if (title.indexOf('Coinbase') == -1) { @@ -28,51 +56,47 @@ angular.module('copayApp.controllers').controller('coinbaseController', 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); - 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); - }); - }); - } - }); - }, 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(); }); - }; + } + */ + } + this.getAuthenticateUrl = function() { + 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(gettextCatalog.getString('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.coinbaseTxDetailsModal = modal; + $scope.coinbaseTxDetailsModal.show(); + }); + }; + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + coinbaseService.setCredentials(); + $scope.network = coinbaseService.getEnvironment(); + init(); }); +}); diff --git a/src/js/routes.js b/src/js/routes.js index f9a02560b..786d50f17 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -926,21 +926,39 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr * */ - .state('coinbase', { + .state('tabs.buyandsell.coinbase', { url: '/coinbase', - templateUrl: 'views/coinbase.html' + views: { + 'tab-home@tabs': { + controller: 'coinbaseController', + controllerAs: 'coinbase', + templateUrl: 'views/coinbase.html' + } + } }) - .state('preferencesCoinbase', { - url: '/preferencesCoinbase', - templateUrl: 'views/preferencesCoinbase.html' + .state('tabs.buyandsell.coinbase.preferences', { + url: '/preferences', + 'tab-home@tabs': { + controller: 'preferencesCoinbaseController', + controllerAs: 'coinbase', + templateUrl: 'views/preferencesCoinbase.html' + } }) - .state('buyCoinbase', { - url: '/buycoinbase', - templateUrl: 'views/buyCoinbase.html' + .state('tabs.buyandsell.coinbase.buy', { + url: '/buy', + 'tab-home@tabs': { + controller: 'buyCoinbaseController', + controllerAs: 'buy', + templateUrl: 'views/buyCoinbase.html' + } }) - .state('sellCoinbase', { - url: '/sellcoinbase', - templateUrl: 'views/sellCoinbase.html' + .state('tabs.buyandsell.coinbase.sell', { + url: '/sell', + 'tab-home@tabs': { + controller: 'sellCoinbaseController', + controllerAs: 'sell', + templateUrl: 'views/sellCoinbase.html' + } }) /* diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index ce5b984cd..4acb37c60 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -1,11 +1,26 @@ '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, platformInfo, lodash, storageService, configService) { var root = {}; var credentials = {}; var isCordova = platformInfo.isCordova; + var isNW = platformInfo.isNW; - root.setCredentials = function(network) { + 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,27 +35,45 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ + 'wallet:transactions:send,' + 'wallet:payment-methods:read'; - if (isCordova) { - credentials.REDIRECT_URI = 'copay://coinbase'; + // NW has a bug with Window Object + if (isCordova && isNW) { + 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.getEnvironment = function() { + return credentials.NETWORK; + }; + root.getOauthCodeUrl = function() { + // TODO CHANGE LIMIT BACK TO 1000 ************************************************* return credentials.HOST + '/oauth/authorize?response_type=code&client_id=' + credentials.CLIENT_ID @@ -48,13 +81,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ + credentials.REDIRECT_URI + '&state=SECURE_RANDOM&scope=' + credentials.SCOPE - + '&meta[send_limit_amount]=1000&meta[send_limit_currency]=USD&meta[send_limit_period]=day'; + + '&meta[send_limit_amount]=1&meta[send_limit_currency]=USD&meta[send_limit_period]=day'; }; 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 +104,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 +131,63 @@ 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); + } + } + coinbaseService.logout(function() {}); + return cb('Your primary account should be a WALLET. Set your wallet account as primary and try again'); + }); + }; + + root.init = function(accessToken, cb) { + if (lodash.isEmpty(credentials.CLIENT_ID)) { + return cb('Coinbase is Disabled'); + } + $log.debug('Init Token...'); + + var getToken = function(cb) { + if (accessToken) { + cb(null, accessToken); + } else { + storageService.getCoinbaseToken(credentials.NETWORK, cb); + } + }; + + getToken(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); + return cb(null, {accessToken: newToken, accountId: accountId}); + }); + }); + } else { + return cb(err); + } + } else { + return cb(null, {accessToken: accessToken, accountId: accountId}); + } + }); + } }); }; @@ -124,7 +210,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'); }); }; @@ -331,9 +417,8 @@ 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) { + var _savePendingTransaction = function(ctx, opts, cb) { + storageService.getCoinbaseTxs(credentials.NETWORK, function(err, oldTxs) { if (lodash.isString(oldTxs)) { oldTxs = JSON.parse(oldTxs); } @@ -350,23 +435,166 @@ 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(accessToken, accountId, cb) { + var coinbasePendingTransactions; + storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { + txs = txs ? JSON.parse(txs) : {}; + coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs; + lodash.forEach(txs, 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) { + _savePendingTransaction(dataFromStorage, { + status: 'error', + error: err + }, function(err) { + if (err) $log.debug(err); + }); + return; + } + _updateCoinbasePendingTransactions(dataFromStorage, tx.data); + coinbasePendingTransactions[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); + }); + return cb(); + } + 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(tx.data, accessToken, accountId); + } else { + var error = { + errors: [{ + message: 'Price falls over the selected percentage' + }] + }; + _savePendingTransaction(dataFromStorage, { + status: 'error', + error: error + }, function(err) { + if (err) $log.debug(err); + }); + } + }); + } else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) { + _sendToCopay(dataFromStorage, accessToken, accountId); + } else { + _savePendingTransaction(dataFromStorage, {}, function(err) { + if (err) $log.debug(err); + }); + } + return cb(null, coinbasePendingTransactions); + }); + }); }); }; - root.logout = function(network, cb) { - storageService.removeCoinbaseToken(network, function() { - storageService.removeCoinbaseRefreshToken(network, function() { + var _sellPending = function(tx, accessToken, accountId) { + if (!tx) return; + var data = tx.amount; + 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); + }); + } else { + if (!res.data.transaction) { + _savePendingTransaction(tx, { + status: 'error', + error: err + }, function(err) { + if (err) $log.debug(err); + }); + 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); + }); + }); + }); + } + }); + }; + + var _sendToCopay = function(tx, accessToken, accountId) { + if (!tx) return; + var data = { + to: tx.toAddr, + amount: tx.amount.amount, + currency: tx.amount.currency, + description: 'To Copay Wallet' + }; + root.sendTo(accessToken, accountId, data, function(err, res) { + if (err) { + _savePendingTransaction(tx, { + status: 'error', + error: err + }, function(err) { + if (err) $log.debug(err); + }); + } else { + if (!res.data.id) { + _savePendingTransaction(tx, { + status: 'error', + error: err + }, function(err) { + if (err) $log.debug(err); + }); + return; + } + root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) { + _savePendingTransaction(tx, { + remove: true + }, function(err) { + _savePendingTransaction(sendTx.data, {}, function(err) { + // TODO + }); + }); + }); + } + }); + }; + + 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(); }); }); diff --git a/src/js/services/configService.js b/src/js/services/configService.js index 5bd6c3cc6..83783c98c 100644 --- a/src/js/services/configService.js +++ b/src/js/services/configService.js @@ -49,7 +49,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer }, coinbase: { - enabled: false, //disable coinbase for this release + enabled: true, testnet: false }, @@ -230,10 +230,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) { diff --git a/www/views/advancedSettings.html b/www/views/advancedSettings.html index f6ee8f328..7623121e7 100644 --- a/www/views/advancedSettings.html +++ b/www/views/advancedSettings.html @@ -21,12 +21,9 @@ Enable Glidera Service - - - - +
Wallet Operation
diff --git a/www/views/buyandsell.html b/www/views/buyandsell.html index 4f7dd4b10..47e361365 100644 --- a/www/views/buyandsell.html +++ b/www/views/buyandsell.html @@ -6,6 +6,10 @@ + + + + diff --git a/www/views/coinbase.html b/www/views/coinbase.html index 9e4457961..c1ac86f3a 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -1,59 +1,30 @@ - -
- -
- -
-
-
-
    -
  • -
-
-
-
- Your primary account should be a WALLET. Set your wallet account as primary and try again. -
-
-
- -
- Or go to Preferences and log out manually. -
-
-
+ + -
-
- + + +
Testnet wallets only work with Coinbase Sandbox Accounts
-
+ +
+
+
    +
  • +
+
+
+ +
@@ -61,111 +32,95 @@

Connect your Coinbase account to get started

- Connect to Coinbase - - -
+
- - - -
- -
-
-
- -
- -
- -
- -
    -
  • - buy bitcoin - Buy Bitcoin - - - -
  • -
  • - sell bitcoin - Sell Bitcoin - - - -
  • -
- -
-

Activity

-
-
    -
  • -
-
-
-
- bought - bought - sold - sold -
- -
-
- Sold - Bought - - -{{tx.amount.amount.replace('-','')}} - {{tx.amount.currency}} - +
+
-
-
-
+ + +
+
+ +
- -
-
+ + diff --git a/www/views/tab-home.html b/www/views/tab-home.html index a0b17d7e6..04bedc98d 100644 --- a/www/views/tab-home.html +++ b/www/views/tab-home.html @@ -124,10 +124,10 @@ - - + + + +
@@ -156,7 +156,7 @@ Add BitPay Visa® Card - +
From 1533428fb08b1254fb603189e38d72bf49195392 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 8 Dec 2016 11:13:33 -0300 Subject: [PATCH 02/34] Coinbase logo --- www/views/coinbase.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/www/views/coinbase.html b/www/views/coinbase.html index c1ac86f3a..c92d2c080 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -66,6 +66,10 @@
+
+ +
+
Date: Thu, 8 Dec 2016 13:06:01 -0300 Subject: [PATCH 03/34] Coinbase connection issues and logout from tab-setting --- .../modals/coinbaseConfirmation.js | 20 --- src/js/controllers/preferencesCoinbase.js | 48 ++++++-- src/js/controllers/tab-settings.js | 11 +- src/js/routes.js | 29 ++--- src/js/services/coinbaseService.js | 5 +- www/views/coinbase.html | 5 - www/views/modals/coinbase-confirmation.html | 18 --- www/views/preferencesCoinbase.html | 114 +++++++++--------- www/views/tab-settings.html | 7 ++ 9 files changed, 130 insertions(+), 127 deletions(-) delete mode 100644 src/js/controllers/modals/coinbaseConfirmation.js delete mode 100644 www/views/modals/coinbase-confirmation.html diff --git a/src/js/controllers/modals/coinbaseConfirmation.js b/src/js/controllers/modals/coinbaseConfirmation.js deleted file mode 100644 index 729b81945..000000000 --- a/src/js/controllers/modals/coinbaseConfirmation.js +++ /dev/null @@ -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(); - }; - -}); diff --git a/src/js/controllers/preferencesCoinbase.js b/src/js/controllers/preferencesCoinbase.js index 6bca19de6..b9113cbcd 100644 --- a/src/js/controllers/preferencesCoinbase.js +++ b/src/js/controllers/preferencesCoinbase.js @@ -1,18 +1,42 @@ '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(); + $scope.network = coinbaseService.getEnvironment(); + ongoingProcess.set('connectingCoinbase', true); + coinbaseService.init($scope.accessToken, function(err, data) { + ongoingProcess.set('connectingCoinbase', false); + 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) { + $scope.coinbaseAccount = account.data; }); - }; - + coinbaseService.getCurrentUser(accessToken, function(err, user) { + $scope.coinbaseUser = user.data; + }); + }); }); + +}); diff --git a/src/js/controllers/tab-settings.js b/src/js/controllers/tab-settings.js index b5573155e..ab2bf1cff 100644 --- a/src/js/controllers/tab-settings.js +++ b/src/js/controllers/tab-settings.js @@ -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, $window, $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,14 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct }); } + if ($scope.coinbaseEnabled) { + coinbaseService.setCredentials(); + storageService.getCoinbaseToken(coinbaseService.getEnvironment(), function(err, token) { + if (err) $log.error(err); + $scope.coinbaseToken = token; + }); + } + }); }; diff --git a/src/js/routes.js b/src/js/routes.js index 786d50f17..4561bc68a 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -926,24 +926,25 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr * */ - .state('tabs.buyandsell.coinbase', { - url: '/coinbase', - views: { - 'tab-home@tabs': { - controller: 'coinbaseController', - controllerAs: 'coinbase', - templateUrl: 'views/coinbase.html' - } - } - }) - .state('tabs.buyandsell.coinbase.preferences', { - url: '/preferences', + .state('tabs.buyandsell.coinbase', { + url: '/coinbase', + views: { 'tab-home@tabs': { - controller: 'preferencesCoinbaseController', + 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.buy', { url: '/buy', 'tab-home@tabs': { diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 4acb37c60..842d1dc50 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -177,7 +177,10 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ if (err) return cb(err); _refreshToken(refreshToken, function(err, newToken) { if (err) return cb(err); - return cb(null, {accessToken: newToken, accountId: accountId}); + _getMainAccountId(newToken, function(err, accountId) { + if (err) return cb(err); + return cb(null, {accessToken: newToken, accountId: accountId}); + }); }); }); } else { diff --git a/www/views/coinbase.html b/www/views/coinbase.html index c92d2c080..82ba63d8c 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -3,11 +3,6 @@ Coinbase - - - diff --git a/www/views/modals/coinbase-confirmation.html b/www/views/modals/coinbase-confirmation.html deleted file mode 100644 index c243c0f28..000000000 --- a/www/views/modals/coinbase-confirmation.html +++ /dev/null @@ -1,18 +0,0 @@ - -
-
-

Are you sure you would like to log out of your Coinbase account?

-

You will need to log back in to buy or sell bitcoin in Copay.

-
- -
-
- -
-
-
-
diff --git a/www/views/preferencesCoinbase.html b/www/views/preferencesCoinbase.html index fee9ab66f..3b0e0599c 100644 --- a/www/views/preferencesCoinbase.html +++ b/www/views/preferencesCoinbase.html @@ -1,60 +1,62 @@ -
-
+ + + + + Preferences + -
+ -
    -

    Account

    -
  • - ID - - {{index.coinbaseAccount.id}} - -
  • -
  • - Name - - {{index.coinbaseAccount.name}} - -
  • -
  • - Balance - - {{index.coinbaseAccount.balance.amount}} {{index.coinbaseAccount.balance.currency}} - -
  • -
  • - Native Balance - - {{index.coinbaseAccount.native_balance.amount}} {{index.coinbaseAccount.native_balance.currency}} - -
  • +
      +
      + Account +
      +
    • + ID + + {{coinbaseAccount.id}} + +
    • +
    • + Name + + {{coinbaseAccount.name}} + +
    • +
    • + Balance + + {{coinbaseAccount.balance.amount}} {{coinbaseAccount.balance.currency}} + +
    • +
    • + Native Balance + + {{coinbaseAccount.native_balance.amount}} {{coinbaseAccount.native_balance.currency}} + +
    • -

      User Information

      -
    • - ID - - {{index.coinbaseUser.id}} - -
    • -
    • - Email - - {{index.coinbaseUser.email}} - -
    • -
    -
      -

      -
    • - - Log out -
    • -
    -

    +
    + User Information +
    +
  • + ID + + {{coinbaseUser.id}} + +
  • +
  • + Email + + {{coinbaseUser.email}} + +
  • -
-
+
+
  • + Log out +
  • + + +
    + diff --git a/www/views/tab-settings.html b/www/views/tab-settings.html index 9ee416332..868388c56 100644 --- a/www/views/tab-settings.html +++ b/www/views/tab-settings.html @@ -133,6 +133,13 @@
    + + + + +
    From ec2801ef37fee9de831c6b67d0c92919452937d9 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 9 Dec 2016 12:46:27 -0300 Subject: [PATCH 04/34] Buy, enter amount --- src/js/controllers/amount.js | 47 +++++++++++++++++++++++++++++++++--- src/js/routes.js | 20 +++++++++++++++ www/views/amount.html | 25 ++++++++++++++++--- www/views/coinbase.html | 4 +-- 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 2a07c3ba5..81d819e94 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService) { +angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService, coinbaseService) { var unitToSatoshi; var satToUnit; var unitDecimals; @@ -20,6 +20,9 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.isGlidera = data.stateParams.isGlidera; $scope.glideraAccessToken = data.stateParams.glideraAccessToken; + // Coinbase parameters + $scope.isCoinbase = data.stateParams.isCoinbase; + $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.isCoinbase; $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.isCoinbase && !data.stateParams.toAddress) { $log.error('Bad params at amount') throw ('bad params'); } @@ -47,6 +50,33 @@ angular.module('copayApp.controllers').controller('amountController', function($ }); } + if ($scope.isCoinbase) { + var currency = 'USD'; + coinbaseService.init($scope.coinbaseAccessToken, function(err, data) { + if ($scope.isCoinbase == 'buy') { + coinbaseService.buyPrice(data.accessToken, currency, function(err, b) { + $scope.coinbaseBuyPrice = b.data || null; + }); + } else { + coinbaseService.sellPrice(data.accessToken, currency, function(err, b) { + $scope.coinbaseSellPrice = b.data || null; + }); + } + + $scope.coinbasePaymentMethods = []; + coinbaseService.getPaymentMethods(data.accessToken, function(err, p) { + lodash.each(p.data, function(pm) { + if (pm.allow_buy) { + $scope.coinbasePaymentMethods.push(pm); + } + if (pm.allow_buy && pm.primary_buy) { + $scope.coinbaseSelectedPaymentMethod = pm; + } + }); + }); + }); + } + var reNr = /^[1234567890\.]$/; var reOp = /^[\*\+\-\/]$/; @@ -350,6 +380,17 @@ angular.module('copayApp.controllers').controller('amountController', function($ isGlidera: $scope.isGlidera, glideraAccessToken: $scope.glideraAccessToken }); + } else if ($scope.isCoinbase) { + if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethod)) { + popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected'); + return; + } + var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; + $state.transitionTo('tabs.buyandsell.glidera.confirm', { + toAmount: (amount * unitToSatoshi).toFixed(0), + isCoinbase: $scope.isCoinbase, + coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id + }); } else { var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; if ($scope.customAmount) { diff --git a/src/js/routes.js b/src/js/routes.js index 4561bc68a..d466a17eb 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -945,6 +945,25 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } } }) + .state('tabs.buyandsell.coinbase.amount', { + url: '/amount/:isCoinbase', + views: { + 'tab-home@tabs': { + controller: 'amountController', + templateUrl: 'views/amount.html' + } + } + }) + .state('tabs.buyandsell.coinbase.confirm', { + url: '/confirm/:toAmount/:isCoinbase/:coinbasePaymentMethodId', + views: { + 'tab-home@tabs': { + controller: 'confirmController', + templateUrl: 'views/confirm.html' + } + } + }) + /* .state('tabs.buyandsell.coinbase.buy', { url: '/buy', 'tab-home@tabs': { @@ -961,6 +980,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr templateUrl: 'views/sellCoinbase.html' } }) + */ /* * diff --git a/www/views/amount.html b/www/views/amount.html index 68499b244..a730d0525 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -14,14 +14,14 @@ -
    +
    Recipient
    - + @@ -39,7 +39,7 @@
    -
    +
    @@ -66,6 +66,25 @@ (remaining {{limits.monthlySellRemaining|currency:'':2}} {{limits.currency}})
    + +
    +
    + 1 BTC ~ {{coinbaseBuyPrice.amount}} {{coinbaseBuyPrice.currency}} +
    +
    + 1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}} +
    +
    + +
    +
    diff --git a/www/views/coinbase.html b/www/views/coinbase.html index 82ba63d8c..f531b2e07 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -68,13 +68,13 @@
    + href ui-sref="tabs.buyandsell.coinbase.amount({isCoinbase: 'buy'})"> buy bitcoin Buy Bitcoin + href ui-sref="tabs.buyandsell.coinbase.amount({isCoinbase: 'sell'})"> buy bitcoin Sell Bitcoin From 964318d57f85073bbd7fe85f62fb89b41dbd321f Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 12 Dec 2016 09:16:58 -0300 Subject: [PATCH 05/34] Select payment method --- src/sass/views/amount.scss | 4 +++- www/views/amount.html | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sass/views/amount.scss b/src/sass/views/amount.scss index cc4bf24e3..175e230f4 100644 --- a/src/sass/views/amount.scss +++ b/src/sass/views/amount.scss @@ -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 { diff --git a/www/views/amount.html b/www/views/amount.html index a730d0525..312d50943 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -74,7 +74,7 @@
    1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}}
    -
    +
    Payment Method From 3b0dceccf5854426e39708f052ef4bce41563cd4 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 12 Dec 2016 14:45:12 -0300 Subject: [PATCH 06/34] Fix amount process --- src/js/controllers/amount.js | 21 +++++--- src/js/controllers/confirm.js | 11 ++-- src/js/services/coinbaseService.js | 83 ++++++++++++++++++++++++++++++ www/views/amount.html | 17 +++--- www/views/confirm.html | 29 ++++++----- 5 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 81d819e94..f9c5a554e 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -66,11 +66,20 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.coinbasePaymentMethods = []; coinbaseService.getPaymentMethods(data.accessToken, function(err, p) { lodash.each(p.data, function(pm) { - if (pm.allow_buy) { - $scope.coinbasePaymentMethods.push(pm); - } - if (pm.allow_buy && pm.primary_buy) { - $scope.coinbaseSelectedPaymentMethod = pm; + if ($scope.isCoinbase == 'sell') { + if (pm.allow_sell) { + $scope.coinbasePaymentMethods.push(pm); + } + if (pm.allow_sell && pm.primary_sell) { + $scope.coinbaseSelectedPaymentMethod = pm; + } + } else { + if (pm.allow_buy) { + $scope.coinbasePaymentMethods.push(pm); + } + if (pm.allow_buy && pm.primary_buy) { + $scope.coinbaseSelectedPaymentMethod = pm; + } } }); }); @@ -386,7 +395,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ return; } var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; - $state.transitionTo('tabs.buyandsell.glidera.confirm', { + $state.transitionTo('tabs.buyandsell.coinbase.confirm', { toAmount: (amount * unitToSatoshi).toFixed(0), isCoinbase: $scope.isCoinbase, coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 9ebcf507f..96cb1429a 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, bitpayCardService) { +angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, coinbaseService, bitpayCardService) { var cachedTxp = {}; var toAmount; var isChromeApp = platformInfo.isChromeApp; @@ -25,6 +25,10 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.isGlidera = data.stateParams.isGlidera; $scope.glideraAccessToken = data.stateParams.glideraAccessToken; + // Coinbase parameters + $scope.isCoinbase = data.stateParams.isCoinbase; + $scope.coinbasePaymentMethodId = data.stateParams.coinbasePaymentMethodId; + toAmount = data.stateParams.toAmount; cachedSendMax = {}; $scope.useSendMax = data.stateParams.useSendMax == 'true' ? true : false; @@ -50,6 +54,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( var feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal'; $scope.feeLevel = feeService.feeOpts[feeLevel]; if ($scope.isGlidera) $scope.network = glideraService.getEnvironment(); + else if ($scope.isCoinbase) $scope.network = coinbaseService.getEnvironment(); else $scope.network = (new bitcore.Address($scope.toAddress)).network.name; resetValues(); setwallets(); @@ -283,7 +288,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); $scope.showWalletSelector = function() { - $scope.walletSelectorTitle = $scope.isGlidera == 'buy' ? 'Receive in' : $scope.isGlidera == 'sell' ? 'Sell From' : gettextCatalog.getString('Send from'); + $scope.walletSelectorTitle = ($scope.isGlidera || $scope.isCoinbase) == 'buy' ? 'Receive in' : ($scope.isGlidera || $scope.isCoinbase) == 'sell' ? 'Sell From' : gettextCatalog.getString('Send from'); if (!$scope.useSendMax && ($scope.insufficientFunds || $scope.noMatchingWallet)) return; $scope.showWallets = true; }; @@ -362,7 +367,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.wallet = wallet; $scope.fee = $scope.txp = null; - if ($scope.isGlidera) return; + if ($scope.isGlidera || $scope.isCoinbase) return; if (stop) { $timeout.cancel(stop); stop = null; diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 842d1dc50..6a05b99cb 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -6,6 +6,34 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ var isCordova = platformInfo.isCordova; var isNW = platformInfo.isNW; + // FAKE DATA + var isFake = true; + + 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) { @@ -304,6 +332,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getPaymentMethods = function(token, cb) { + if (isFake) return cb(null, payment_methods); $http(_get('/payment-methods', token)).then(function(data) { $log.info('Coinbase Get Payment Methods: SUCCESS'); return cb(null, data.data); @@ -603,6 +632,60 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; + var payment_methods = { + "pagination": { + "ending_before": null, + "starting_after": null, + "limit": 25, + "order": "desc", + "previous_uri": null, + "next_uri": null + }, + "data": [ + { + "id": "127b4d76-a1a0-5de7-8185-3657d7b526ec", + "type": "fiat_account", + "name": "USD Wallet", + "currency": "USD", + "primary_buy": false, + "primary_sell": false, + "allow_buy": true, + "allow_sell": true, + "allow_deposit": true, + "allow_withdraw": true, + "instant_buy": true, + "instant_sell": true, + "created_at": "2015-02-24T14:30:30-08:00", + "updated_at": "2015-02-24T14:30:30-08:00", + "resource": "payment_method", + "resource_path": "/v2/payment-methods/127b4d76-a1a0-5de7-8185-3657d7b526ec", + "fiat_account": { + "id": "a077fff9-312b-559b-af98-146c33e27388", + "resource": "account", + "resource_path": "/v2/accounts/a077fff9-312b-559b-af98-146c33e27388" + } + }, + { + "id": "83562370-3e5c-51db-87da-752af5ab9559", + "type": "ach_bank_account", + "name": "International Bank *****1111", + "currency": "USD", + "primary_buy": true, + "primary_sell": true, + "allow_buy": true, + "allow_sell": true, + "allow_deposit": true, + "allow_withdraw": true, + "instant_buy": false, + "instant_sell": false, + "created_at": "2015-01-31T20:49:02Z", + "updated_at": "2015-02-11T16:53:57-08:00", + "resource": "payment_method", + "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + } + ] + }; + return root; }); diff --git a/www/views/amount.html b/www/views/amount.html index 312d50943..c5366bdc4 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -43,7 +43,7 @@
    - Amount + Amount
    Purchase Amount is limited to USD 1000 per day
    {{exchangeRate}}
    @@ -68,22 +68,23 @@
    -
    - 1 BTC ~ {{coinbaseBuyPrice.amount}} {{coinbaseBuyPrice.currency}} -
    -
    - 1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}} -
    +
    + 1 BTC ~ {{coinbaseBuyPrice.amount}} {{coinbaseBuyPrice.currency}} +
    +
    + 1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}} +
    diff --git a/www/views/confirm.html b/www/views/confirm.html index 9612e42dc..9bfdcdeeb 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -12,10 +12,10 @@
    - Sending + Sending Sending maximum amount - Buying - Selling + Buying + Selling
    {{displayAmount || '...'}} {{displayUnit}}
    @@ -30,16 +30,17 @@ Expired
    - To - From - To + To + From + To - +
    +
    @@ -57,9 +58,9 @@
    - From - To - From + From + To + From
    @@ -74,14 +75,14 @@
    - + Add Memo {{description}} -
    +
    {{'Fee' | translate}}: {{feeLevel | translate}} {{fee || '...'}} @@ -153,6 +154,10 @@ A transfer has been initiated from your bank account. Your bitcoins should arrive to your wallet in 2-4 business day A transfer has been initiated to your bank account. Should arrive in 4-6 business days
    +
    + Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet + Sale initiated +
    Date: Mon, 12 Dec 2016 16:05:57 -0300 Subject: [PATCH 07/34] Fix buy --- src/js/controllers/confirm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 96cb1429a..11e2f7444 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -90,7 +90,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( return; } - if ($scope.isGlidera == 'buy') { + if (($scope.isGlidera || $scope.isCoinbase) == 'buy') { initConfirm(); return; } From 612d7067799e52605d3c847321578e7bf979b414 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 19 Dec 2016 11:50:49 -0300 Subject: [PATCH 08/34] Complete buying flow --- src/js/controllers/amount.js | 7 +- src/js/controllers/coinbase.js | 6 +- src/js/controllers/confirm.js | 126 +++++++++++- .../controllers/modals/coinbaseTxDetails.js | 4 +- src/js/routes.js | 2 +- src/js/services/coinbaseService.js | 177 +++++++++++++++-- www/views/confirm.html | 29 ++- www/views/modals/coinbase-tx-details.html | 180 +++++++++--------- 8 files changed, 406 insertions(+), 125 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index f9c5a554e..55da74976 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -52,7 +52,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ if ($scope.isCoinbase) { var currency = 'USD'; - coinbaseService.init($scope.coinbaseAccessToken, function(err, data) { + coinbaseService.init(function(err, data) { if ($scope.isCoinbase == 'buy') { coinbaseService.buyPrice(data.accessToken, currency, function(err, b) { $scope.coinbaseBuyPrice = b.data || null; @@ -394,11 +394,14 @@ angular.module('copayApp.controllers').controller('amountController', function($ popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected'); return; } + var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; $state.transitionTo('tabs.buyandsell.coinbase.confirm', { toAmount: (amount * unitToSatoshi).toFixed(0), isCoinbase: $scope.isCoinbase, - coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id + coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id, + coinbaseAmount: amountUSD, + coinbaseAmountCurrency: 'USD' }); } else { var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index ef2ef3749..333ab1ee6 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -6,8 +6,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function var init = function() { ongoingProcess.set('connectingCoinbase', true); - coinbaseService.init($scope.accessToken, function(err, data) { -console.log('[coinbase.js:9]',data); //TODO) + coinbaseService.init(function(err, data) { +console.log('[coinbase.js:9]',err, data); //TODO) ongoingProcess.set('connectingCoinbase', false); if (err || lodash.isEmpty(data)) { if (err) { @@ -28,7 +28,7 @@ console.log('[coinbase.js:9]',data); //TODO) $scope.updateTransactions = function() { $log.debug('Checking for transactions...'); coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) { -console.log('[coinbase.js:43]',txs); //TODO) +console.log('[coinbase.js:43]',txs); //TODO $scope.pendingTransactions = txs; }); diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 11e2f7444..9cc0e8ec1 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -28,6 +28,8 @@ angular.module('copayApp.controllers').controller('confirmController', function( // Coinbase parameters $scope.isCoinbase = data.stateParams.isCoinbase; $scope.coinbasePaymentMethodId = data.stateParams.coinbasePaymentMethodId; + $scope.coinbaseAmount = data.stateParams.coinbaseAmount; + $scope.coinbaseAmountCurrency = data.stateParams.coinbaseAmountCurrency; toAmount = data.stateParams.toAmount; cachedSendMax = {}; @@ -180,6 +182,10 @@ angular.module('copayApp.controllers').controller('confirmController', function( } if ($scope.isGlidera == 'buy') $scope.getBuyPrice(); if ($scope.isGlidera == 'sell') $scope.getSellPrice(); + + if ($scope.isCoinbase == 'buy') { + coinbaseBuyRequest($scope.coinbaseAmount, $scope.coinbaseAmountCurrency, $scope.coinbasePaymentMethodId); + } }; function resetValues() { @@ -532,6 +538,50 @@ angular.module('copayApp.controllers').controller('confirmController', function( } }); return; + } + + if ($scope.isCoinbase) { + + ongoingProcess.set('buyingBitcoin', true, onSendStatusChange); + coinbaseService.init(function(err, res) { + if (err) { + $log.error(err); + return; + } + var token = res.accessToken; + var accountId = res.accountId; + coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) { +console.log('[confirm.js:508] BUY COMMIT',b); //TODO + if (err) { + $log.error(err); + return; + } + var tx = b.data.transaction; + if (!tx) return; + + coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { +console.log('[confirm.js:517] GET TRANSACTION',updatedTx); //TODO + if (err) $log.debug(err); + walletService.getAddress($scope.wallet, false, function(err, walletAddr) { +console.log('[confirm.js:521] GET ADDRESS',walletAddr); //TODO + if (err) { + return; + } + updatedTx.data['toAddr'] = walletAddr; + coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) { + if (err) $log.debug(err); + if (updatedTx.data.status == 'completed') { + coinbaseSendToCopay(token, accountId, updatedTx.data, onSendStatusChange); + } else { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + $scope.coinbaseBuySuccess = updatedTx.data; + } + }); + }); + }); + }); + }); + return; } ongoingProcess.set('creatingTx', true, onSendStatusChange); @@ -582,12 +632,15 @@ angular.module('copayApp.controllers').controller('confirmController', function( $log.debug('statusChangeHandler: ', processName, showName, isOn); if ( ( - processName === 'broadcastingTx' || - ((processName === 'signingTx') && $scope.wallet.m > 1) || - (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) + processName === 'broadcastingTx' || + ((processName === 'signingTx') && $scope.wallet.m > 1) || + (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) || + (processName == 'buyingBitcoin') ) && !isOn) { $scope.sendStatus = 'success'; - $scope.$digest(); + $timeout(function() { + $scope.$digest(); + }, 100) } else if (showName) { $scope.sendStatus = showName; } @@ -604,6 +657,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( var fromBitPayCard = previousView.match(/tabs.bitpayCard/) ? true : false; var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false; var fromGlidera = previousView.match(/tabs.buyandsell.glidera/) ? true : false; + var fromCoinbase = previousView.match(/tabs.buyandsell.coinbase/) ? true : false; $ionicHistory.nextViewOptions({ disableAnimate: true @@ -637,6 +691,15 @@ angular.module('copayApp.controllers').controller('confirmController', function( $state.go('tabs.home').then(function() { $state.transitionTo('tabs.buyandsell.glidera'); }); + } else if (fromCoinbase) { + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $ionicHistory.clearHistory(); + $state.go('tabs.home').then(function() { + $state.transitionTo('tabs.buyandsell.coinbase'); + }); } else { $ionicHistory.nextViewOptions({ disableAnimate: true, @@ -897,10 +960,61 @@ angular.module('copayApp.controllers').controller('confirmController', function( } if (lodash.isEmpty(res)) return; if (unitName == 'bits') { - $scope.exchangeRate = '1,000,000 bits ~ ' + res.rate + ' ' + alternativeIsoCode; + $scope.exchangeRate = '1,000,000 bits ~ ' + res.data.rate + ' ' + alternativeIsoCode; } else { - $scope.exchangeRate = '1 BTC ~ ' + res.rate + ' ' + alternativeIsoCode; + $scope.exchangeRate = '1 BTC ~ ' + res.data.rate + ' ' + alternativeIsoCode; } }); }; + + var coinbaseBuyRequest = function(amount, currency, paymentMethodId) { + var dataSrc = { + amount: amount, + currency: currency, + payment_method: paymentMethodId + }; + coinbaseService.init(function(err, res) { + if (err) { + $log.error(err); + return; + } + coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) { +console.log('[confirm.js:931] BUY REQUEST',data); //TODO + if (err) { + $log.error(err); + return; + } + $scope.coinbaseBuyRequest = data.data; + }); + }); + }; + + var coinbaseSendToCopay = function(token, accountId, tx, onSendStatusChange) { + var data = { + to: tx.toAddr, + amount: tx.amount.amount, + currency: tx.amount.currency, + description: 'Copay Wallet: ' + $scope.wallet.name + }; + coinbaseService.sendTo(token, accountId, data, function(err, res) { +console.log('[confirm.js:938] SEND TO',res); //TODO + if (err) { + return; + } + $scope.coinbaseReceiveInfo = res.data; + if (!res.data.id) return; + coinbaseService.getTransaction(token, accountId, res.data.id, function(err, sendTx) { +console.log('[confirm.js:945] GET TRANSACTION',sendTx); //TODO + coinbaseService.savePendingTransaction(tx, { + remove: true + }, function(err) { + coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); +console.log('[confirm.js:948] LISTO',err); //TODO + // TODO + }); + }); + }); + }); + }; }); diff --git a/src/js/controllers/modals/coinbaseTxDetails.js b/src/js/controllers/modals/coinbaseTxDetails.js index feebfd47d..3d952b168 100644 --- a/src/js/controllers/modals/coinbaseTxDetails.js +++ b/src/js/controllers/modals/coinbaseTxDetails.js @@ -7,11 +7,11 @@ angular.module('copayApp.controllers').controller('coinbaseTxDetailsController', remove: true }, function(err) { $rootScope.$emit('Local/CoinbaseTx'); - $scope.cancel(); + $scope.close(); }); }; - $scope.cancel = function() { + $scope.close = function() { $scope.coinbaseTxDetailsModal.hide(); }; diff --git a/src/js/routes.js b/src/js/routes.js index d466a17eb..ad0bcca17 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -955,7 +955,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.buyandsell.coinbase.confirm', { - url: '/confirm/:toAmount/:isCoinbase/:coinbasePaymentMethodId', + url: '/confirm/:toAmount/:isCoinbase/:coinbasePaymentMethodId/:coinbaseAmount/:coinbaseAmountCurrency', views: { 'tab-home@tabs': { controller: 'confirmController', diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 6a05b99cb..6bccb318d 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -180,21 +180,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; - root.init = function(accessToken, cb) { + root.init = function(cb) { if (lodash.isEmpty(credentials.CLIENT_ID)) { return cb('Coinbase is Disabled'); } $log.debug('Init Token...'); - var getToken = function(cb) { - if (accessToken) { - cb(null, accessToken); - } else { - storageService.getCoinbaseToken(credentials.NETWORK, cb); - } - }; - - getToken(function(err, accessToken) { + storageService.getCoinbaseToken(credentials.NETWORK, function(err, accessToken) { if (err || !accessToken) return cb(); else { _getMainAccountId(accessToken, function(err, accountId) { @@ -279,6 +271,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getTransaction = function(token, accountId, transactionId, cb) { + if (isFake) return cb(null, get_transaction); if (!token) return cb('Invalid Token'); $http(_get('/accounts/' + accountId + '/transactions/' + transactionId, token)).then(function(data) { $log.info('Coinbase Transaction: SUCCESS'); @@ -392,6 +385,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.buyRequest = function(token, accountId, data, cb) { + if (isFake) return cb(null, buy_request); var data = { amount: data.amount, currency: data.currency, @@ -408,6 +402,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.buyCommit = function(token, accountId, buyId, cb) { + if (isFake) return cb(null, buy_commit); $http(_post('/accounts/' + accountId + '/buys/' + buyId + '/commit', token)).then(function(data) { $log.info('Coinbase Buy Commit: SUCCESS'); return cb(null, data.data); @@ -431,6 +426,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.sendTo = function(token, accountId, data, cb) { + if (isFake) return cb(null, send_to_copay); var data = { type: 'send', to: data.to, @@ -448,6 +444,10 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; // Pending transactions + + root.savePendingTransaction = function(ctx, opts, cb) { + _savePendingTransaction(ctx, opts, cb); + }; var _savePendingTransaction = function(ctx, opts, cb) { storageService.getCoinbaseTxs(credentials.NETWORK, function(err, oldTxs) { @@ -478,11 +478,12 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { txs = txs ? JSON.parse(txs) : {}; coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs; - lodash.forEach(txs, function(dataFromStorage, txId) { + lodash.forEach(coinbasePendingTransactions, 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; + (dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) + return cb(null, coinbasePendingTransactions); root.getTransaction(accessToken, accountId, txId, function(err, tx) { if (err) { _savePendingTransaction(dataFromStorage, { @@ -491,7 +492,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }, function(err) { if (err) $log.debug(err); }); - return; + return cb(err); } _updateCoinbasePendingTransactions(dataFromStorage, tx.data); coinbasePendingTransactions[txId] = dataFromStorage; @@ -504,7 +505,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }, function(err) { if (err) $log.debug(err); }); - return cb(); + return cb(err); } var newSellPrice = s.data.amount; var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100); @@ -632,6 +633,46 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; + var buy_request = { + "data" : { + "id": "a333743d-184a-5b5b-abe8-11612fc44ab5", + "status": "created", + "payment_method": { + "id": "83562370-3e5c-51db-87da-752af5ab9559", + "resource": "payment_method", + "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + }, + "transaction": { + "id": "763d1401-fd17-5a18-852a-9cca5ac2f9c0", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" + }, + "amount": { + "amount": "10.00000000", + "currency": "BTC" + }, + "total": { + "amount": "102.01", + "currency": "USD" + }, + "subtotal": { + "amount": "101.00", + "currency": "USD" + }, + "created_at": "2015-04-01T18:43:37-07:00", + "updated_at": "2015-04-01T18:43:37-07:00", + "resource": "buy", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/a333743d-184a-5b5b-abe8-11612fc44ab5", + "committed": false, + "instant": false, + "fee": { + "amount": "1.01", + "currency": "USD" + }, + "payout_at": "2015-04-07T18:43:37-07:00" + } + }; + var payment_methods = { "pagination": { "ending_before": null, @@ -686,6 +727,114 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ ] }; + var get_transaction = { + "data" : { + "id": "57ffb4ae-0c59-5430-bcd3-3f98f797a66c", + "type": "send", + "status": "completed", + "amount": { + "amount": "-0.00100000", + "currency": "BTC" + }, + "native_amount": { + "amount": "-0.01", + "currency": "USD" + }, + "description": null, + "created_at": "2015-03-11T13:13:35-07:00", + "updated_at": "2015-03-26T15:55:43-07:00", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/57ffb4ae-0c59-5430-bcd3-3f98f797a66c", + "network": { + "status": "off_blockchain", + "name": "bitcoin" + }, + "to": { + "id": "a6b4c2df-a62c-5d68-822a-dd4e2102e703", + "resource": "user", + "resource_path": "/v2/users/a6b4c2df-a62c-5d68-822a-dd4e2102e703" + }, + "details": { + "title": "Send bitcoin", + "subtitle": "to User 2" + } + } + }; + + var buy_commit = { + "data" : { + "id": "a333743d-184a-5b5b-abe8-11612fc44ab5", + "status": "created", + "payment_method": { + "id": "83562370-3e5c-51db-87da-752af5ab9559", + "resource": "payment_method", + "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" + }, + "transaction": { + "id": "763d1401-fd17-5a18-852a-9cca5ac2f9c0", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" + }, + "amount": { + "amount": "10.00000000", + "currency": "BTC" + }, + "total": { + "amount": "102.01", + "currency": "USD" + }, + "subtotal": { + "amount": "101.00", + "currency": "USD" + }, + "created_at": "2015-04-01T18:43:37-07:00", + "updated_at": "2015-04-01T18:43:37-07:00", + "resource": "buy", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/a333743d-184a-5b5b-abe8-11612fc44ab5", + "committed": true, + "instant": false, + "fee": { + "amount": "1.01", + "currency": "USD" + }, + "payout_at": "2015-04-07T18:43:37-07:00" + } + }; + + var send_to_copay = { + "data" : { + "id": "3c04e35e-8e5a-5ff1-9155-00675db4ac02", + "type": "send", + "status": "pending", + "amount": { + "amount": "-0.10000000", + "currency": "BTC" + }, + "native_amount": { + "amount": "-1.00", + "currency": "USD" + }, + "description": null, + "created_at": "2015-01-31T20:49:02Z", + "updated_at": "2015-03-31T17:25:29-07:00", + "resource": "transaction", + "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/3c04e35e-8e5a-5ff1-9155-00675db4ac02", + "network": { + "status": "unconfirmed", + "hash": "463397c87beddd9a61ade61359a13adc9efea26062191fe07147037bce7f33ed", + "name": "bitcoin" + }, + "to": { + "resource": "bitcoin_address", + "address": "1AUJ8z5RuHRTqD1eikyfUUetzGmdWLGkpT" + }, + "details": { + "title": "Send bitcoin", + "subtitle": "to User 2" + } + } + }; + return root; }); diff --git a/www/views/confirm.html b/www/views/confirm.html index 9bfdcdeeb..4cf9e823f 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -116,6 +116,17 @@
    +
    +
    + Subtotal: {{coinbaseBuyRequest.subtotal.amount}} {{coinbaseBuyRequest.subtotal.currency}} +
    +
    + Total: {{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}} +
    +
    + Payout at: {{coinbaseBuyRequest.payout_at | amCalendar}} +
    +
    No wallets available
    @@ -147,15 +158,25 @@ slide-success-show="sendStatus === 'success'" slide-success-on-confirm="onSuccessConfirm()" slide-success-hide-on-confirm="true"> - Payment Sent - Proposal Created - Transaction created + Bought +
    + Payment Sent + Proposal Created + Transaction created +
    A transfer has been initiated from your bank account. Your bitcoins should arrive to your wallet in 2-4 business day A transfer has been initiated to your bank account. Should arrive in 4-6 business days
    - Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet + + + Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet + + + Buy confirmed. Funds will be send soon to your selected wallet + + Sale initiated
    diff --git a/www/views/modals/coinbase-tx-details.html b/www/views/modals/coinbase-tx-details.html index 181312a7f..1ff0969ef 100644 --- a/www/views/modals/coinbase-tx-details.html +++ b/www/views/modals/coinbase-tx-details.html @@ -1,104 +1,98 @@ - -
    - - - Back - + + +
    + Details
    -

    Details

    -
    -
    -
    - bought - bought - bought - bought -
    -
    - Bought - Sold -
    -
    - Receiving purchased bitcoin - Sending bitcoin to sell -
    -
    - Buying bitcoin - Selling bitcoin -
    -
    - -{{tx.amount.amount.replace('-','')}} - {{tx.amount.currency}} -
    -
    - -{{tx.native_amount.amount.replace('-','')}} - {{tx.native_amount.currency}} -
    +
    +
    + bought + bought + bought + bought
    - - -
    -
      -
    • -
    +
    + Bought + Sold
    - -
      - -
    • - {{tx.details.title}} - {{tx.details.subtitle}} -
    • - -
    • - Status - Completed - Pending - Error -
    • - -
    • - Date - {{tx.created_at | amCalendar}} -
    • - -
    • - Price Sensitivity - {{tx.price_sensitivity.name}} -
    • - -
    • - Sell Price - {{tx.sell_price_amount}} {{tx.sell_price_currency}} -
    • - -
    • - Sent bitcoin from - Receive bitcoin in - {{tx.description}} -
    • -
    - -
    -
    -

    - This action will remove the transaction. -

    - -
    +
    + Receiving purchased bitcoin + Sending bitcoin to sell +
    +
    + Buying bitcoin + Selling bitcoin +
    +
    + -{{tx.amount.amount.replace('-','')}} + {{tx.amount.currency}} +
    +
    + -{{tx.native_amount.amount.replace('-','')}} + {{tx.native_amount.currency}}
    - -
    + +
    +
      +
    • +
    +
    + +
      +
    • + {{tx.details.title}} + {{tx.details.subtitle}} +
    • + +
    • + Status + + Completed + Pending + Error + +
    • + +
    • + Date + {{tx.created_at | amCalendar}} +
    • + +
    • + Price Sensitivity + {{tx.price_sensitivity.name}} +
    • + +
    • + Sell Price + {{tx.sell_price_amount}} {{tx.sell_price_currency}} +
    • + +
    • + Sent bitcoin from + Receive bitcoin in + {{tx.description}} +
    • +
    + +
    +
    + This action will remove the transaction. +
    + +
    + From 4febe719b063c21a144114f3752818e41ad791f1 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 22 Dec 2016 20:37:10 -0300 Subject: [PATCH 09/34] Selling process --- src/js/controllers/amount.js | 19 ++- src/js/controllers/coinbase.js | 4 +- src/js/controllers/confirm.js | 177 ++++++++++++++++++----- src/js/routes.js | 2 +- src/js/services/coinbaseService.js | 223 ++--------------------------- www/views/amount.html | 2 +- www/views/confirm.html | 46 +++++- 7 files changed, 216 insertions(+), 257 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 55da74976..7247d3cb7 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -51,15 +51,23 @@ angular.module('copayApp.controllers').controller('amountController', function($ } if ($scope.isCoinbase) { - var currency = 'USD'; + var currency = 'USD'; + coinbaseService.init(function(err, data) { if ($scope.isCoinbase == 'buy') { coinbaseService.buyPrice(data.accessToken, currency, function(err, b) { $scope.coinbaseBuyPrice = b.data || null; }); } else { - coinbaseService.sellPrice(data.accessToken, currency, function(err, b) { - $scope.coinbaseSellPrice = b.data || null; + coinbaseService.sellPrice(data.accessToken, currency, function(err, s) { + $scope.coinbaseSellPrice = s.data || null; + }); + + var dataSrc = { + name: 'Received from ' + $window.appConfig.nameCase + }; + coinbaseService.createAddress(data.accessToken, data.accountId, dataSrc, function(err, data) { + $scope.toAddress = data.data.address; }); } @@ -394,10 +402,15 @@ angular.module('copayApp.controllers').controller('amountController', function($ popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected'); return; } + if ($scope.isCoinbase == 'sell' && lodash.isEmpty($scope.toAddress)) { + popupService.showAlert(gettextCatalog.getString('Error'), 'No Destination Address'); + return; + } var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; $state.transitionTo('tabs.buyandsell.coinbase.confirm', { toAmount: (amount * unitToSatoshi).toFixed(0), + toAddress: $scope.toAddress, isCoinbase: $scope.isCoinbase, coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id, coinbaseAmount: amountUSD, diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index 333ab1ee6..c23bd3faf 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -7,7 +7,6 @@ angular.module('copayApp.controllers').controller('coinbaseController', function var init = function() { ongoingProcess.set('connectingCoinbase', true); coinbaseService.init(function(err, data) { -console.log('[coinbase.js:9]',err, data); //TODO) ongoingProcess.set('connectingCoinbase', false); if (err || lodash.isEmpty(data)) { if (err) { @@ -26,9 +25,8 @@ console.log('[coinbase.js:9]',err, data); //TODO) }; $scope.updateTransactions = function() { - $log.debug('Checking for transactions...'); + $log.debug('Getting transactions...'); coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) { -console.log('[coinbase.js:43]',txs); //TODO $scope.pendingTransactions = txs; }); diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 9cc0e8ec1..a8a9e5a77 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -186,6 +186,19 @@ angular.module('copayApp.controllers').controller('confirmController', function( if ($scope.isCoinbase == 'buy') { coinbaseBuyRequest($scope.coinbaseAmount, $scope.coinbaseAmountCurrency, $scope.coinbasePaymentMethodId); } + + if ($scope.isCoinbase == 'sell') { + var satToBtc = 1 / 100000000; + $scope.coinbaseAmountBTC = (toAmount * satToBtc).toFixed(8); + $scope.priceSensitivity = coinbaseService.priceSensitivity; + $scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity }; + + coinbaseService.init(function(err, data) { + coinbaseService.sellPrice(data.accessToken, $scope.coinbaseAmountCurrency, function(err, sell) { + $scope.coinbaseSellPrice = sell.data; + }); + }); + } }; function resetValues() { @@ -373,7 +386,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.wallet = wallet; $scope.fee = $scope.txp = null; - if ($scope.isGlidera || $scope.isCoinbase) return; + if ($scope.isGlidera || $scope.isCoinbase == 'buy') return; if (stop) { $timeout.cancel(stop); stop = null; @@ -542,45 +555,123 @@ angular.module('copayApp.controllers').controller('confirmController', function( if ($scope.isCoinbase) { - ongoingProcess.set('buyingBitcoin', true, onSendStatusChange); - coinbaseService.init(function(err, res) { - if (err) { - $log.error(err); - return; - } - var token = res.accessToken; - var accountId = res.accountId; - coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) { -console.log('[confirm.js:508] BUY COMMIT',b); //TODO + // BUY + if ($scope.isCoinbase == 'buy') { + ongoingProcess.set('buyingBitcoin', true, onSendStatusChange); + coinbaseService.init(function(err, res) { if (err) { $log.error(err); return; } - var tx = b.data.transaction; - if (!tx) return; + var token = res.accessToken; + var accountId = res.accountId; + coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) { + if (err) { + $log.error(err); + return; + } + var tx = b.data.transaction; + if (!tx) return; - coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { -console.log('[confirm.js:517] GET TRANSACTION',updatedTx); //TODO - if (err) $log.debug(err); - walletService.getAddress($scope.wallet, false, function(err, walletAddr) { -console.log('[confirm.js:521] GET ADDRESS',walletAddr); //TODO - if (err) { - return; - } - updatedTx.data['toAddr'] = walletAddr; - coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) { - if (err) $log.debug(err); - if (updatedTx.data.status == 'completed') { - coinbaseSendToCopay(token, accountId, updatedTx.data, onSendStatusChange); - } else { - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - $scope.coinbaseBuySuccess = updatedTx.data; + coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { + if (err) $log.debug(err); + walletService.getAddress($scope.wallet, false, function(err, walletAddr) { + if (err) { + return; } + updatedTx.data['toAddr'] = walletAddr; + coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) { + if (err) $log.debug(err); + if (updatedTx.data.status == 'completed') { + coinbaseSendToCopay(token, accountId, updatedTx.data, onSendStatusChange); + } else { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + $scope.coinbaseBuySuccess = updatedTx.data; + } + }); }); }); }); }); - }); + } + + + // SELL + if ($scope.isCoinbase == 'sell') { + + ongoingProcess.set('sellingBitcoin', true, onSendStatusChange); + createTx($scope.wallet, false, function(err, txp) { + var message = gettextCatalog.getString('Selling {{amountStr}} from {{name}}', { + amountStr: $scope.coinbaseAmount + ' ' + $scope.coinbaseAmountCurrency, + name: $scope.wallet.name + }); + var okText = gettextCatalog.getString('Confirm'); + var cancelText = gettextCatalog.getString('Cancel'); + + popupService.showConfirm(null, message, okText, cancelText, function(ok) { + if (!ok) { + $scope.sendStatus = ''; + $timeout(function() { + $scope.$apply(); + }); + return; + } + // SIMULATE publishAndSign + //$log.warn('Simulating publishAndSign...'); + publishAndSign(wallet, txp, onSendStatusChange); + + $timeout(function() { + + coinbaseService.init(function(err, res) { + if (err) { + $log.error(err); + return; + } + var token = res.accessToken; + var accountId = res.accountId; + + coinbaseService.getAddressTransactions(token, accountId, $scope.toAddress, function(err, ctxs) { + if (err) { + ongoingProcess.clear(); + $log.debug(err); + return; + } + var coinbaseTransactions = ctxs.data; + var txFound = false; + var ctx; + for(var i = 0; i < coinbaseTransactions.length; i++) { + ctx = coinbaseTransactions[i]; + if (ctx.type == 'send' && ctx.from) { + txFound = true; + if (ctx.status == 'completed') { + coinbaseSellRequest(token, accountId, ctx, onSendStatusChange); + } else { + $log.debug('Saving transaction to process later'); + ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; + ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : ''; + ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD'; + ctx['description'] = $window.appConfig.nameCase + ' Wallet: ' + $scope.wallet.name; + $scope.coinbaseSendInfo = ctx; + coinbaseService.savePendingTransaction(ctx, null, function(err) { + ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); + if (err) $log.debug(err); + }); + } + } + } + if (!txFound) { + // Transaction sent, but can not verify it on Coinbase.com + // TODO: improve error message + ongoingProcess.clear(); + return setSendError('Transaction not found. Please, verify if transaction exists on Coinbase.com'); + } + }); + }); + }, 5000); + }); + }); + } + return; } @@ -635,7 +726,8 @@ console.log('[confirm.js:521] GET ADDRESS',walletAddr); //TODO processName === 'broadcastingTx' || ((processName === 'signingTx') && $scope.wallet.m > 1) || (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) || - (processName == 'buyingBitcoin') + (processName == 'buyingBitcoin') || + (processName == 'sellingBitcoin' && ($scope.coinbaseSellRequest || $scope.coinbaseSendInfo)) ) && !isOn) { $scope.sendStatus = 'success'; $timeout(function() { @@ -979,7 +1071,6 @@ console.log('[confirm.js:521] GET ADDRESS',walletAddr); //TODO return; } coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) { -console.log('[confirm.js:931] BUY REQUEST',data); //TODO if (err) { $log.error(err); return; @@ -989,6 +1080,25 @@ console.log('[confirm.js:931] BUY REQUEST',data); //TODO }); }; + var coinbaseSellRequest = function(accessToken, accountId, ctx, onSendStatusChange) { + var dataSrc = ctx.amount; + dataSrc['payment_method'] = $scope.coinbasePaymentMethodId || null; + dataSrc['commit'] = true; + + coinbaseService.sellRequest(accessToken, accountId, dataSrc, function(err, data) { + if (err) { + ongoingProcess.clear(); + $log.error(err); + return; + } + $scope.coinbaseSellRequest = data.data; + coinbaseService.savePendingTransaction($scope.coinbaseSellRequest, null, function(err) { + ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); + if (err) $log.debug(err); + }); + }); + }; + var coinbaseSendToCopay = function(token, accountId, tx, onSendStatusChange) { var data = { to: tx.toAddr, @@ -997,20 +1107,17 @@ console.log('[confirm.js:931] BUY REQUEST',data); //TODO description: 'Copay Wallet: ' + $scope.wallet.name }; coinbaseService.sendTo(token, accountId, data, function(err, res) { -console.log('[confirm.js:938] SEND TO',res); //TODO if (err) { return; } $scope.coinbaseReceiveInfo = res.data; if (!res.data.id) return; coinbaseService.getTransaction(token, accountId, res.data.id, function(err, sendTx) { -console.log('[confirm.js:945] GET TRANSACTION',sendTx); //TODO coinbaseService.savePendingTransaction(tx, { remove: true }, function(err) { coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) { ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); -console.log('[confirm.js:948] LISTO',err); //TODO // TODO }); }); diff --git a/src/js/routes.js b/src/js/routes.js index ad0bcca17..57d5b57d8 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -955,7 +955,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.buyandsell.coinbase.confirm', { - url: '/confirm/:toAmount/:isCoinbase/:coinbasePaymentMethodId/:coinbaseAmount/:coinbaseAmountCurrency', + url: '/confirm/:toAmount/:toAddress/:isCoinbase/:coinbasePaymentMethodId/:coinbaseAmount/:coinbaseAmountCurrency', views: { 'tab-home@tabs': { controller: 'confirmController', diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 6bccb318d..248dc0e52 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -6,9 +6,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ var isCordova = platformInfo.isCordova; var isNW = platformInfo.isNW; - // FAKE DATA - var isFake = true; - root.priceSensitivity = [ { value: 0.5, @@ -271,7 +268,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getTransaction = function(token, accountId, transactionId, cb) { - if (isFake) return cb(null, get_transaction); if (!token) return cb('Invalid Token'); $http(_get('/accounts/' + accountId + '/transactions/' + transactionId, token)).then(function(data) { $log.info('Coinbase Transaction: SUCCESS'); @@ -282,6 +278,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) { @@ -325,7 +332,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getPaymentMethods = function(token, cb) { - if (isFake) return cb(null, payment_methods); $http(_get('/payment-methods', token)).then(function(data) { $log.info('Coinbase Get Payment Methods: SUCCESS'); return cb(null, data.data); @@ -385,7 +391,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.buyRequest = function(token, accountId, data, cb) { - if (isFake) return cb(null, buy_request); var data = { amount: data.amount, currency: data.currency, @@ -402,7 +407,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.buyCommit = function(token, accountId, buyId, cb) { - if (isFake) return cb(null, buy_commit); $http(_post('/accounts/' + accountId + '/buys/' + buyId + '/commit', token)).then(function(data) { $log.info('Coinbase Buy Commit: SUCCESS'); return cb(null, data.data); @@ -426,7 +430,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.sendTo = function(token, accountId, data, cb) { - if (isFake) return cb(null, send_to_copay); var data = { type: 'send', to: data.to, @@ -485,7 +488,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ (dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return cb(null, coinbasePendingTransactions); root.getTransaction(accessToken, accountId, txId, function(err, tx) { - if (err) { + if (err || lodash.isEmpty(tx)) { _savePendingTransaction(dataFromStorage, { status: 'error', error: err @@ -633,208 +636,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; - var buy_request = { - "data" : { - "id": "a333743d-184a-5b5b-abe8-11612fc44ab5", - "status": "created", - "payment_method": { - "id": "83562370-3e5c-51db-87da-752af5ab9559", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" - }, - "transaction": { - "id": "763d1401-fd17-5a18-852a-9cca5ac2f9c0", - "resource": "transaction", - "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" - }, - "amount": { - "amount": "10.00000000", - "currency": "BTC" - }, - "total": { - "amount": "102.01", - "currency": "USD" - }, - "subtotal": { - "amount": "101.00", - "currency": "USD" - }, - "created_at": "2015-04-01T18:43:37-07:00", - "updated_at": "2015-04-01T18:43:37-07:00", - "resource": "buy", - "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/a333743d-184a-5b5b-abe8-11612fc44ab5", - "committed": false, - "instant": false, - "fee": { - "amount": "1.01", - "currency": "USD" - }, - "payout_at": "2015-04-07T18:43:37-07:00" - } - }; - - var payment_methods = { - "pagination": { - "ending_before": null, - "starting_after": null, - "limit": 25, - "order": "desc", - "previous_uri": null, - "next_uri": null - }, - "data": [ - { - "id": "127b4d76-a1a0-5de7-8185-3657d7b526ec", - "type": "fiat_account", - "name": "USD Wallet", - "currency": "USD", - "primary_buy": false, - "primary_sell": false, - "allow_buy": true, - "allow_sell": true, - "allow_deposit": true, - "allow_withdraw": true, - "instant_buy": true, - "instant_sell": true, - "created_at": "2015-02-24T14:30:30-08:00", - "updated_at": "2015-02-24T14:30:30-08:00", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/127b4d76-a1a0-5de7-8185-3657d7b526ec", - "fiat_account": { - "id": "a077fff9-312b-559b-af98-146c33e27388", - "resource": "account", - "resource_path": "/v2/accounts/a077fff9-312b-559b-af98-146c33e27388" - } - }, - { - "id": "83562370-3e5c-51db-87da-752af5ab9559", - "type": "ach_bank_account", - "name": "International Bank *****1111", - "currency": "USD", - "primary_buy": true, - "primary_sell": true, - "allow_buy": true, - "allow_sell": true, - "allow_deposit": true, - "allow_withdraw": true, - "instant_buy": false, - "instant_sell": false, - "created_at": "2015-01-31T20:49:02Z", - "updated_at": "2015-02-11T16:53:57-08:00", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" - } - ] - }; - - var get_transaction = { - "data" : { - "id": "57ffb4ae-0c59-5430-bcd3-3f98f797a66c", - "type": "send", - "status": "completed", - "amount": { - "amount": "-0.00100000", - "currency": "BTC" - }, - "native_amount": { - "amount": "-0.01", - "currency": "USD" - }, - "description": null, - "created_at": "2015-03-11T13:13:35-07:00", - "updated_at": "2015-03-26T15:55:43-07:00", - "resource": "transaction", - "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/57ffb4ae-0c59-5430-bcd3-3f98f797a66c", - "network": { - "status": "off_blockchain", - "name": "bitcoin" - }, - "to": { - "id": "a6b4c2df-a62c-5d68-822a-dd4e2102e703", - "resource": "user", - "resource_path": "/v2/users/a6b4c2df-a62c-5d68-822a-dd4e2102e703" - }, - "details": { - "title": "Send bitcoin", - "subtitle": "to User 2" - } - } - }; - - var buy_commit = { - "data" : { - "id": "a333743d-184a-5b5b-abe8-11612fc44ab5", - "status": "created", - "payment_method": { - "id": "83562370-3e5c-51db-87da-752af5ab9559", - "resource": "payment_method", - "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559" - }, - "transaction": { - "id": "763d1401-fd17-5a18-852a-9cca5ac2f9c0", - "resource": "transaction", - "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a" - }, - "amount": { - "amount": "10.00000000", - "currency": "BTC" - }, - "total": { - "amount": "102.01", - "currency": "USD" - }, - "subtotal": { - "amount": "101.00", - "currency": "USD" - }, - "created_at": "2015-04-01T18:43:37-07:00", - "updated_at": "2015-04-01T18:43:37-07:00", - "resource": "buy", - "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/a333743d-184a-5b5b-abe8-11612fc44ab5", - "committed": true, - "instant": false, - "fee": { - "amount": "1.01", - "currency": "USD" - }, - "payout_at": "2015-04-07T18:43:37-07:00" - } - }; - - var send_to_copay = { - "data" : { - "id": "3c04e35e-8e5a-5ff1-9155-00675db4ac02", - "type": "send", - "status": "pending", - "amount": { - "amount": "-0.10000000", - "currency": "BTC" - }, - "native_amount": { - "amount": "-1.00", - "currency": "USD" - }, - "description": null, - "created_at": "2015-01-31T20:49:02Z", - "updated_at": "2015-03-31T17:25:29-07:00", - "resource": "transaction", - "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/3c04e35e-8e5a-5ff1-9155-00675db4ac02", - "network": { - "status": "unconfirmed", - "hash": "463397c87beddd9a61ade61359a13adc9efea26062191fe07147037bce7f33ed", - "name": "bitcoin" - }, - "to": { - "resource": "bitcoin_address", - "address": "1AUJ8z5RuHRTqD1eikyfUUetzGmdWLGkpT" - }, - "details": { - "title": "Send bitcoin", - "subtitle": "to User 2" - } - } - }; - return root; }); diff --git a/www/views/amount.html b/www/views/amount.html index c5366bdc4..867258a99 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -84,7 +84,7 @@
    1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}} -
    +
    diff --git a/www/views/confirm.html b/www/views/confirm.html index 4cf9e823f..c2c2a9471 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -29,6 +29,37 @@ {{remainingTimeStr.value}} Expired
    + +
    + At what percentage lower price would you accept to sell? +
    + +
    +
    + Coinbase has not yet implemented an immediate method to sell bitcoin from a wallet. To make this sale, funds + will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one + hour). +
    +
    +
    + Estimated sale value: + + {{coinbaseSellPrice.amount * coinbaseAmountBTC | currency : 'USD ' : 2}} + +
    + Still sell if price fall until: + + {{(coinbaseSellPrice.amount - (selectedPriceSensitivity.data.value / 100) * + coinbaseSellPrice.amount) * coinbaseAmountBTC | currency : 'USD ' : 2}} + +
    +
    To From @@ -75,14 +106,14 @@
    - + Add Memo {{description}} -
    +
    {{'Fee' | translate}}: {{feeLevel | translate}} {{fee || '...'}} @@ -159,6 +190,8 @@ slide-success-on-confirm="onSuccessConfirm()" slide-success-hide-on-confirm="true"> Bought + Funds sent to Coinbase Account + Sale initiated
    Payment Sent Proposal Created @@ -177,7 +210,14 @@ Buy confirmed. Funds will be send soon to your selected wallet - Sale initiated + + + The transaction is not yet confirmed, and will show as "Processing" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase. + + + A transfer has been initiated to your bank account and should arrive at {{coinbaseSellRequest.payout_at | amCalendar}}. + +
    From 1a586f6fcc10e54143f21a671f8cd68842249931 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 30 Dec 2016 19:08:51 -0300 Subject: [PATCH 10/34] Fix buy and sell --- src/js/controllers/amount.js | 14 +- src/js/controllers/coinbase.js | 6 +- src/js/controllers/confirm.js | 250 ++++++++++++---------- src/js/services/coinbaseService.js | 158 ++++++++------ www/views/amount.html | 2 +- www/views/coinbase.html | 4 +- www/views/confirm.html | 54 +++-- www/views/modals/coinbase-tx-details.html | 8 +- 8 files changed, 268 insertions(+), 228 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 7247d3cb7..ad9f81f54 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -72,6 +72,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ } $scope.coinbasePaymentMethods = []; + $scope.coinbaseSelectedPaymentMethodId = { value : null }; coinbaseService.getPaymentMethods(data.accessToken, function(err, p) { lodash.each(p.data, function(pm) { if ($scope.isCoinbase == 'sell') { @@ -79,14 +80,14 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.coinbasePaymentMethods.push(pm); } if (pm.allow_sell && pm.primary_sell) { - $scope.coinbaseSelectedPaymentMethod = pm; + $scope.coinbaseSelectedPaymentMethodId.value = pm.id; } } else { if (pm.allow_buy) { $scope.coinbasePaymentMethods.push(pm); } if (pm.allow_buy && pm.primary_buy) { - $scope.coinbaseSelectedPaymentMethod = pm; + $scope.coinbaseSelectedPaymentMethodId.value = pm.id; } } }); @@ -398,7 +399,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ glideraAccessToken: $scope.glideraAccessToken }); } else if ($scope.isCoinbase) { - if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethod)) { + if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethodId.value)) { popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected'); return; } @@ -407,12 +408,17 @@ angular.module('copayApp.controllers').controller('amountController', function($ return; } var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); + if (amountUSD < 1) { + popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 USD'); + return; + } + var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; $state.transitionTo('tabs.buyandsell.coinbase.confirm', { toAmount: (amount * unitToSatoshi).toFixed(0), toAddress: $scope.toAddress, isCoinbase: $scope.isCoinbase, - coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethod.id, + coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethodId.value, coinbaseAmount: amountUSD, coinbaseAmountCurrency: 'USD' }); diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index c23bd3faf..e1bc21e17 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -26,10 +26,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function $scope.updateTransactions = function() { $log.debug('Getting transactions...'); - coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) { - $scope.pendingTransactions = txs; - }); - + $scope.pendingTransactions = { data: {} }; + coinbaseService.getPendingTransactions($scope.pendingTransactions); }; this.openAuthenticateWindow = function() { diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index a8a9e5a77..66416f1a7 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -159,7 +159,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( if ($scope.paypro) _paymentTimeControl($scope.paypro.expires); displayValues(); - if ($scope.wallets.length > 1) $scope.showWalletSelector(); + if ($scope.wallets.length > 1 && $scope.isCoinbase != 'buy') $scope.showWalletSelector(); else setWallet($scope.wallets[0]); $timeout(function() { $scope.$apply(); @@ -175,6 +175,8 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.cardAmountUSD) + ' USD'; } else if ($scope.giftCardAmountUSD) { $scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.giftCardAmountUSD) + ' USD'; + } else if ($scope.coinbaseAmount) { + $scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.coinbaseAmount) + ' ' + $scope.coinbaseAmountCurrency; } else { txFormatService.formatAlternativeStr(toAmount, function(v) { $scope.alternativeAmountStr = v; @@ -197,6 +199,10 @@ angular.module('copayApp.controllers').controller('confirmController', function( coinbaseService.sellPrice(data.accessToken, $scope.coinbaseAmountCurrency, function(err, sell) { $scope.coinbaseSellPrice = sell.data; }); + coinbaseService.getPaymentMethod(data.accessToken, $scope.coinbasePaymentMethodId, function(err, data) { + if (err) $log.error(err); + $scope.coinbasePaymentMethodInfo = data.data; + }); }); } }; @@ -567,30 +573,45 @@ angular.module('copayApp.controllers').controller('confirmController', function( var accountId = res.accountId; coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) { if (err) { - $log.error(err); + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + popupService.showAlert(gettextCatalog.getString('Error'), 'Could not complete purchase'); return; } var tx = b.data.transaction; - if (!tx) return; + if (!tx) { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + popupService.showAlert(gettextCatalog.getString('Error'), 'Transaction not found'); + return; + } - coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { - if (err) $log.debug(err); - walletService.getAddress($scope.wallet, false, function(err, walletAddr) { + $timeout(function() { + coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { if (err) { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + popupService.showAlert(gettextCatalog.getString('Error'), 'Transaction error'); return; } - updatedTx.data['toAddr'] = walletAddr; - coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) { - if (err) $log.debug(err); - if (updatedTx.data.status == 'completed') { - coinbaseSendToCopay(token, accountId, updatedTx.data, onSendStatusChange); - } else { + walletService.getAddress($scope.wallet, false, function(err, walletAddr) { + if (err) { + ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); + popupService.showAlert(gettextCatalog.getString('Error'), 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) { + if (err) $log.debug(err); ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); $scope.coinbaseBuySuccess = updatedTx.data; - } + $timeout(function() { + $scope.$apply(); + }); + }); }); }); - }); + }, 8000); }); }); } @@ -610,7 +631,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( popupService.showConfirm(null, message, okText, cancelText, function(ok) { if (!ok) { - $scope.sendStatus = ''; $timeout(function() { $scope.$apply(); }); @@ -618,56 +638,13 @@ angular.module('copayApp.controllers').controller('confirmController', function( } // SIMULATE publishAndSign //$log.warn('Simulating publishAndSign...'); - publishAndSign(wallet, txp, onSendStatusChange); - - $timeout(function() { - - coinbaseService.init(function(err, res) { - if (err) { - $log.error(err); - return; - } - var token = res.accessToken; - var accountId = res.accountId; - - coinbaseService.getAddressTransactions(token, accountId, $scope.toAddress, function(err, ctxs) { - if (err) { - ongoingProcess.clear(); - $log.debug(err); - return; - } - var coinbaseTransactions = ctxs.data; - var txFound = false; - var ctx; - for(var i = 0; i < coinbaseTransactions.length; i++) { - ctx = coinbaseTransactions[i]; - if (ctx.type == 'send' && ctx.from) { - txFound = true; - if (ctx.status == 'completed') { - coinbaseSellRequest(token, accountId, ctx, onSendStatusChange); - } else { - $log.debug('Saving transaction to process later'); - ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; - ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : ''; - ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD'; - ctx['description'] = $window.appConfig.nameCase + ' Wallet: ' + $scope.wallet.name; - $scope.coinbaseSendInfo = ctx; - coinbaseService.savePendingTransaction(ctx, null, function(err) { - ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); - if (err) $log.debug(err); - }); - } - } - } - if (!txFound) { - // Transaction sent, but can not verify it on Coinbase.com - // TODO: improve error message - ongoingProcess.clear(); - return setSendError('Transaction not found. Please, verify if transaction exists on Coinbase.com'); - } - }); - }); - }, 5000); + $log.debug('Plublish and Sign...'); + publishAndSign(wallet, txp, function() {}, function(err) { + if (err) return setSendError(err); + $log.debug('Finished publish and sign. Trying to sell...'); + var count = 0; + checkTransaction(count, txp, onSendStatusChange); + }); }); }); } @@ -725,9 +702,9 @@ angular.module('copayApp.controllers').controller('confirmController', function( ( processName === 'broadcastingTx' || ((processName === 'signingTx') && $scope.wallet.m > 1) || - (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) || + (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) || (processName == 'buyingBitcoin') || - (processName == 'sellingBitcoin' && ($scope.coinbaseSellRequest || $scope.coinbaseSendInfo)) + (processName == 'sellingBitcoin') ) && !isOn) { $scope.sendStatus = 'success'; $timeout(function() { @@ -960,18 +937,26 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); }; - function publishAndSign(wallet, txp, onSendStatusChange) { + function publishAndSign(wallet, txp, onSendStatusChange, cb) { if (!wallet.canSign() && !wallet.isPrivKeyExternal()) { $log.info('No signing proposal: No private key'); - return walletService.onlyPublish(wallet, txp, function(err) { - if (err) setSendError(err); + walletService.onlyPublish(wallet, txp, function(err) { + if (err) { + if (cb) return cb(err); + else return setSendError(err); + } + if (cb) return cb(); + else return; }, onSendStatusChange); } walletService.publishAndSign(wallet, txp, function(err, txp) { - if (err) return setSendError(err); + if (err) { + if (cb) return cb(err); + else return setSendError(err); + } var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName; var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false; @@ -990,8 +975,72 @@ angular.module('copayApp.controllers').controller('confirmController', function( ongoingProcess.set('creatingGiftCard', true); debounceCreate(count, dataSrc, onSendStatusChange); } + if (cb) return cb(); }, onSendStatusChange); - } + }; + + var checkTransaction = lodash.throttle(function(count, txp, onSendStatusChange) { + $log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5'); + coinbaseService.init(function(err, res) { + if (err) { + ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); + $log.error(err); + checkTransaction(count, txp, onSendStatusChange); + return; + } + var token = res.accessToken; + var accountId = res.accountId; + + coinbaseService.getTransactions(token, accountId, function(err, ctxs) { + if (err) { + ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); + $log.debug(err); + checkTransaction(count, txp, onSendStatusChange); + return; + } + + // TX amount in BTC + var satToBtc = 1 / 100000000; + var amountBTC = (txp.amount * satToBtc).toFixed(8); + + var coinbaseTransactions = ctxs.data; + var txFound = false; + var ctx; + for(var i = 0; i < coinbaseTransactions.length; i++) { + ctx = coinbaseTransactions[i]; + if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) { + $log.warn('Transaction found!', ctx); + txFound = true; + $log.debug('Saving transaction to process later...'); + ctx['payment_method'] = $scope.coinbasePaymentMethodId; + ctx['status'] = 'pending'; // Forcing "pending" status to process later + ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; + ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : ''; + ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD'; + ctx['description'] = $window.appConfig.nameCase + ' Wallet: ' + $scope.wallet.name; + coinbaseService.savePendingTransaction(ctx, null, function(err) { + ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); + if (err) $log.debug(err); + $scope.coinbaseSendInfo = ctx; + $timeout(function() { + $scope.$apply(); + }); + }); + return; + } + } + if (!txFound) { + // Transaction sent, but could not be verified by Coinbase.com + $log.warn('Transaction not found in Coinbase.'); + if (count < 5) { + checkTransaction(count + 1, txp, onSendStatusChange); + } + } + }); + }); + }, 8000, { + 'leading': true + }); var debounceCreate = lodash.throttle(function(count, dataSrc) { debounceCreateGiftCard(count, dataSrc); @@ -1067,12 +1116,25 @@ angular.module('copayApp.controllers').controller('confirmController', function( }; coinbaseService.init(function(err, res) { if (err) { + $scope.showWallets = null; $log.error(err); + popupService.showAlert(gettextCatalog.getString('Error'), 'Could not connect to Coinbase', function() { + $ionicHistory.goBack(); + }); return; } + coinbaseService.getPaymentMethod(res.accessToken, paymentMethodId, function(err, data) { + if (err) $log.error(err); + $scope.coinbasePaymentMethodInfo = data.data; + }); + coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) { if (err) { + $scope.showWallets = null; $log.error(err); + popupService.showAlert(gettextCatalog.getString('Error'), 'Could not create a buy request', function() { + $ionicHistory.goBack(); + }); return; } $scope.coinbaseBuyRequest = data.data; @@ -1080,48 +1142,4 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); }; - var coinbaseSellRequest = function(accessToken, accountId, ctx, onSendStatusChange) { - var dataSrc = ctx.amount; - dataSrc['payment_method'] = $scope.coinbasePaymentMethodId || null; - dataSrc['commit'] = true; - - coinbaseService.sellRequest(accessToken, accountId, dataSrc, function(err, data) { - if (err) { - ongoingProcess.clear(); - $log.error(err); - return; - } - $scope.coinbaseSellRequest = data.data; - coinbaseService.savePendingTransaction($scope.coinbaseSellRequest, null, function(err) { - ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); - if (err) $log.debug(err); - }); - }); - }; - - var coinbaseSendToCopay = function(token, accountId, tx, onSendStatusChange) { - var data = { - to: tx.toAddr, - amount: tx.amount.amount, - currency: tx.amount.currency, - description: 'Copay Wallet: ' + $scope.wallet.name - }; - coinbaseService.sendTo(token, accountId, data, function(err, res) { - if (err) { - return; - } - $scope.coinbaseReceiveInfo = res.data; - if (!res.data.id) return; - coinbaseService.getTransaction(token, accountId, res.data.id, function(err, sendTx) { - coinbaseService.savePendingTransaction(tx, { - remove: true - }, function(err) { - coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) { - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - // TODO - }); - }); - }); - }); - }; }); diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 248dc0e52..f0267eb1d 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -476,74 +476,90 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; - root.getPendingTransactions = function(accessToken, accountId, cb) { - var coinbasePendingTransactions; - storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { - txs = txs ? JSON.parse(txs) : {}; - coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs; - lodash.forEach(coinbasePendingTransactions, 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 cb(null, coinbasePendingTransactions); - root.getTransaction(accessToken, accountId, txId, function(err, tx) { - if (err || lodash.isEmpty(tx)) { - _savePendingTransaction(dataFromStorage, { - status: 'error', - error: err - }, function(err) { - if (err) $log.debug(err); - }); - return cb(err); - } - _updateCoinbasePendingTransactions(dataFromStorage, tx.data); - coinbasePendingTransactions[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); - }); - return cb(err); - } - 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(tx.data, accessToken, accountId); - } else { - var error = { - errors: [{ - message: 'Price falls over the selected percentage' - }] - }; - _savePendingTransaction(dataFromStorage, { - status: 'error', - error: error - }, function(err) { - if (err) $log.debug(err); - }); - } - }); - } else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) { - _sendToCopay(dataFromStorage, accessToken, accountId); - } else { - _savePendingTransaction(dataFromStorage, {}, function(err) { - if (err) $log.debug(err); - }); - } - return cb(null, coinbasePendingTransactions); + root.getPendingTransactions = function(coinbasePendingTransactions) { + root.init(function(err, data) { + var accessToken = data.accessToken; + var accountId = data.accountId; + storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { + txs = txs ? JSON.parse(txs) : {}; + console.log('[coinbaseService.js:484] getCoinbaseTxs',err, txs); //TODO + coinbasePendingTransactions.data = lodash.isEmpty(txs) ? null : txs; + + 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) { + console.log('[coinbaseService.js:494] getTransaction to UPDATE from Coinbase',err, tx); //TODO + 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); + }); + } + }); }); }); }); }; - var _sellPending = function(tx, accessToken, accountId) { - if (!tx) return; + 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) { @@ -552,14 +568,16 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ error: err }, function(err) { if (err) $log.debug(err); + _updateTxs(coinbasePendingTransactions); }); } else { - if (!res.data.transaction) { + if (res.data && !res.data.transaction) { _savePendingTransaction(tx, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); + _updateTxs(coinbasePendingTransactions); }); return; } @@ -569,6 +587,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ root.getTransaction(accessToken, accountId, res.data.transaction.id, function(err, updatedTx) { _savePendingTransaction(updatedTx.data, {}, function(err) { if (err) $log.debug(err); + _updateTxs(coinbasePendingTransactions); }); }); }); @@ -576,38 +595,43 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; - var _sendToCopay = function(tx, accessToken, accountId) { + var _sendToWallet = function(tx, accessToken, accountId, coinbasePendingTransactions) { if (!tx) return; var data = { to: tx.toAddr, amount: tx.amount.amount, currency: tx.amount.currency, - description: 'To Copay Wallet' + description: 'To Wallet' }; root.sendTo(accessToken, accountId, data, function(err, res) { +console.log('[coinbaseService.js:591] SEND TO COPAY',err, res); //TODO if (err) { _savePendingTransaction(tx, { status: 'error', error: err }, function(err) { if (err) $log.debug(err); + _updateTxs(coinbasePendingTransactions); }); } else { - if (!res.data.id) { + 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) { +console.log('[coinbaseService.js:633] GET UPDATED TX', err, sendTx); //TODO _savePendingTransaction(tx, { remove: true }, function(err) { _savePendingTransaction(sendTx.data, {}, function(err) { - // TODO + if (err) $log.debug(err); + _updateTxs(coinbasePendingTransactions); }); }); }); diff --git a/www/views/amount.html b/www/views/amount.html index 867258a99..023e2125e 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -74,7 +74,7 @@ Payment Method Deposit into
    - diff --git a/www/views/coinbase.html b/www/views/coinbase.html index f531b2e07..a8cae44a4 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -86,8 +86,8 @@ Activity
    diff --git a/www/views/confirm.html b/www/views/confirm.html index c2c2a9471..2010d13c8 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -46,19 +46,18 @@ will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one hour).
    -
    -
    - Estimated sale value: - - {{coinbaseSellPrice.amount * coinbaseAmountBTC | currency : 'USD ' : 2}} - -
    - Still sell if price fall until: - - {{(coinbaseSellPrice.amount - (selectedPriceSensitivity.data.value / 100) * - coinbaseSellPrice.amount) * coinbaseAmountBTC | currency : 'USD ' : 2}} - -
    + Estimated sale value: + {{coinbaseSellPrice.amount * coinbaseAmountBTC | currency : 'USD ' : 2}} + + Still sell if price fall until: + {{(coinbaseSellPrice.amount - (selectedPriceSensitivity.data.value / 100) * + coinbaseSellPrice.amount) * coinbaseAmountBTC | currency : 'USD ' : 2}} + +
    +
    + Payment Method + Deposit into + {{coinbasePaymentMethodInfo.name}}
    To @@ -147,15 +146,19 @@
    -
    -
    - Subtotal: {{coinbaseBuyRequest.subtotal.amount}} {{coinbaseBuyRequest.subtotal.currency}} +
    +
    + Subtotal + {{coinbaseBuyRequest.subtotal.amount}} + {{coinbaseBuyRequest.subtotal.currency}}
    -
    - Total: {{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}} +
    + Total + {{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}}
    -
    - Payout at: {{coinbaseBuyRequest.payout_at | amCalendar}} +
    + Payout at + {{coinbaseBuyRequest.payout_at | amCalendar}}
    @@ -191,7 +194,6 @@ slide-success-hide-on-confirm="true"> Bought Funds sent to Coinbase Account - Sale initiated
    Payment Sent Proposal Created @@ -204,18 +206,12 @@
    - Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet - - - Buy confirmed. Funds will be send soon to your selected wallet + Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet - The transaction is not yet confirmed, and will show as "Processing" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase. - - - A transfer has been initiated to your bank account and should arrive at {{coinbaseSellRequest.payout_at | amCalendar}}. + The transaction is not yet confirmed, and will show as "Pending" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase.
    diff --git a/www/views/modals/coinbase-tx-details.html b/www/views/modals/coinbase-tx-details.html index 1ff0969ef..6d6dce8b4 100644 --- a/www/views/modals/coinbase-tx-details.html +++ b/www/views/modals/coinbase-tx-details.html @@ -40,11 +40,9 @@
    -
    -
      -
    • -
    -
    +
      +
    • +
    • From 5301d1e9a95cbbb748c4f9192f1b7de8895caa1e Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Tue, 3 Jan 2017 12:43:08 -0300 Subject: [PATCH 11/34] Fix uri handler for coinbase --- src/js/controllers/coinbase.js | 10 ++++++++-- src/js/controllers/preferencesCoinbase.js | 4 ++-- src/js/routes.js | 6 +----- src/js/services/coinbaseService.js | 2 +- src/js/services/incomingData.js | 11 +++++++++-- www/views/coinbase.html | 16 +++++----------- www/views/preferencesCoinbase.html | 2 +- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index e1bc21e17..f5ab4368e 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -3,6 +3,7 @@ angular.module('copayApp.controllers').controller('coinbaseController', function($rootScope, $scope, $timeout, $ionicModal, $log, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, gettextCatalog, externalLinkService) { var isNW = platformInfo.isNW; + var isCordova = platformInfo.isCordova; var init = function() { ongoingProcess.set('connectingCoinbase', true); @@ -58,6 +59,7 @@ angular.module('copayApp.controllers').controller('coinbaseController', function } this.getAuthenticateUrl = function() { + $scope.showOauthForm = isCordova || isNW ? false : true; return coinbaseService.getOauthCodeUrl(); }; @@ -90,9 +92,13 @@ angular.module('copayApp.controllers').controller('coinbaseController', function }); }; + var self = this; $scope.$on("$ionicView.beforeEnter", function(event, data) { coinbaseService.setCredentials(); - $scope.network = coinbaseService.getEnvironment(); - init(); + if (data.stateParams && data.stateParams.code) { + self.submitOauthCode(data.stateParams.code); + } else { + init(); + } }); }); diff --git a/src/js/controllers/preferencesCoinbase.js b/src/js/controllers/preferencesCoinbase.js index b9113cbcd..beb39dc26 100644 --- a/src/js/controllers/preferencesCoinbase.js +++ b/src/js/controllers/preferencesCoinbase.js @@ -19,8 +19,7 @@ angular.module('copayApp.controllers').controller('preferencesCoinbaseController coinbaseService.setCredentials(); $scope.network = coinbaseService.getEnvironment(); ongoingProcess.set('connectingCoinbase', true); - coinbaseService.init($scope.accessToken, function(err, data) { - ongoingProcess.set('connectingCoinbase', false); + coinbaseService.init(function(err, data) { if (err || lodash.isEmpty(data)) { ongoingProcess.set('connectingCoinbase', false); if (err) { @@ -31,6 +30,7 @@ angular.module('copayApp.controllers').controller('preferencesCoinbaseController 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) { diff --git a/src/js/routes.js b/src/js/routes.js index 57d5b57d8..38ae9331a 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -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' - }) /* * @@ -927,7 +923,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr */ .state('tabs.buyandsell.coinbase', { - url: '/coinbase', + url: '/coinbase/:code', views: { 'tab-home@tabs': { controller: 'coinbaseController', diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index f0267eb1d..90e0c5622 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -61,7 +61,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ + 'wallet:payment-methods:read'; // NW has a bug with Window Object - if (isCordova && isNW) { + if (isCordova || isNW) { credentials.REDIRECT_URI = coinbase.redirect_uri.mobile; } else { credentials.REDIRECT_URI = coinbase.redirect_uri.desktop; diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index 4b7d4ea34..78e856af1 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -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) { diff --git a/www/views/coinbase.html b/www/views/coinbase.html index a8cae44a4..1ec941348 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -7,16 +7,10 @@ -
      - Testnet wallets only work with Coinbase Sandbox Accounts -
      - -
      -
      -
        -
      • -
      -
      +
      +
        +
      • +
      @@ -28,7 +22,7 @@

      Connect your Coinbase account to get started

      diff --git a/www/views/preferencesCoinbase.html b/www/views/preferencesCoinbase.html index 3b0e0599c..68050e9af 100644 --- a/www/views/preferencesCoinbase.html +++ b/www/views/preferencesCoinbase.html @@ -54,7 +54,7 @@
    • - Log out + Log out
    From 49c01ca015eb44769fe27f4af4cd24cab5995537 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Tue, 3 Jan 2017 12:44:50 -0300 Subject: [PATCH 12/34] Clean code --- src/js/controllers/buyCoinbase.js | 175 ------------------- src/js/controllers/coinbaseUri.js | 44 ----- src/js/controllers/sellCoinbase.js | 261 ----------------------------- www/views/buyCoinbase.html | 169 ------------------- www/views/coinbaseUri.html | 22 --- www/views/sellCoinbase.html | 202 ---------------------- 6 files changed, 873 deletions(-) delete mode 100644 src/js/controllers/buyCoinbase.js delete mode 100644 src/js/controllers/coinbaseUri.js delete mode 100644 src/js/controllers/sellCoinbase.js delete mode 100644 www/views/buyCoinbase.html delete mode 100644 www/views/coinbaseUri.html delete mode 100644 www/views/sellCoinbase.html diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js deleted file mode 100644 index e48dea1c7..000000000 --- a/src/js/controllers/buyCoinbase.js +++ /dev/null @@ -1,175 +0,0 @@ -'use strict'; - -angular.module('copayApp.controllers').controller('buyCoinbaseController', - function($scope, $log, $ionicModal, $timeout, lodash, profileService, coinbaseService, addressService, ongoingProcess) { - var self = this; - - this.init = function(testnet) { - self.allWallets = profileService.getWallets(testnet ? 'testnet' : 'livenet'); - - var client = profileService.focusedClient; - if (client) { - $timeout(function() { - self.selectedWalletId = client.credentials.walletId; - self.selectedWalletName = client.credentials.walletName; - $scope.$apply(); - }, 100); - } - }; - - 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_buy) { - self.paymentMethods.push(pm); - } - if (pm.allow_buy && pm.primary_buy) { - $scope.selectedPaymentMethod = pm; - } - }); - }); - }; - - 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; - var dataSrc = { - amount: amount, - currency: currency, - payment_method: $scope.selectedPaymentMethod.id || null - }; - ongoingProcess.set('Sending request...', true); - coinbaseService.buyRequest(token, accountId, dataSrc, function(err, data) { - ongoingProcess.set('Sending request...', false); - if (err) { - self.error = err; - return; - } - self.buyInfo = data.data; - }); - }; - - 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); - if (err) { - self.error = err; - return; - } else { - var tx = b.data.transaction; - if (!tx) 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) { - if (err) { - self.error = { - errors: [{ - message: 'Could not create address' - }] - }; - 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); - } - }); - }); - }); - } - }); - }; - - this.sendToCopay = function(token, account, tx) { - self.error = null; - var accountId = account.id; - - 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); - }); - }); - }); - } - - }); - }; - - - }); diff --git a/src/js/controllers/coinbaseUri.js b/src/js/controllers/coinbaseUri.js deleted file mode 100644 index b92c1b355..000000000 --- a/src/js/controllers/coinbaseUri.js +++ /dev/null @@ -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)); - } - }); diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js deleted file mode 100644 index 6ce9b28b3..000000000 --- a/src/js/controllers/sellCoinbase.js +++ /dev/null @@ -1,261 +0,0 @@ -'use strict'; - -angular.module('copayApp.controllers').controller('sellCoinbaseController', - function($rootScope, $scope, $log, $timeout, $ionicModal, lodash, profileService, coinbaseService, configService, walletService, fingerprintService, ongoingProcess, go) { - - var self = this; - var client; - - $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]; - - this.init = function(testnet) { - self.allWallets = profileService.getWallets(testnet ? 'testnet' : 'livenet', 1); - - client = profileService.focusedClient; - if (client && client.credentials.m == 1) { - $timeout(function() { - self.selectedWalletId = client.credentials.walletId; - self.selectedWalletName = client.credentials.walletName; - $scope.$apply(); - }, 100); - } - }; - - 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; - } - }); - }); - }; - - 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'; - return; - } - - 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) { - if (err) { - ongoingProcess.set('Creating Transaction...', false); - self.error = err; - return; - } - - var address, comment; - - address = data.data.address; - amount = parseInt((amount * 100000000).toFixed(0)); - comment = 'Send funds to Coinbase Account: ' + account.name; - - outputs.push({ - 'toAddress': address, - 'amount': amount, - 'message': comment - }); - - var txp = { - toAddress: address, - amount: amount, - outputs: outputs, - message: comment, - payProUrl: null, - excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true, - feeLevel: walletSettings.feeLevel || 'normal' - }; - - walletService.createTx(client, txp, function(err, createdTxp) { - if (err) { - $log.debug(err); - ongoingProcess.set('Creating Transaction...', false); - self.error = { - errors: [{ - message: 'Could not create transaction: ' + err.message - }] - }; - $scope.$apply(); - 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'); - } - }); - }); - }); - }, 100); - }; - - this.confirmTx = function(txp, cb) { - - // TODO see walletService createAndPublish - }; - - }); diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html deleted file mode 100644 index 62684c5a1..000000000 --- a/www/views/buyCoinbase.html +++ /dev/null @@ -1,169 +0,0 @@ -
    -
    - - -
    - -
    -
    -
    -
      -
    • -
    -
    -
    -
    - -
    -
    - -
    - -
    - - -
    - - - -
    - -
    - - Enter the amount to get the exchange rate - - - Not available - - - ~ {{buy.buyPrice.amount * amount | currency : 'USD ' : 2}} - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    -

    Funds sent to Copay Wallet

    -

    - Buy confirmed. Funds will be send soon to your selected Copay Wallet -

    - -
    -
    - -
    -

    Confirm transaction

    - -
      -
    • - Amount - {{buy.buyInfo.amount.amount}} {{buy.buyInfo.amount.currency}} -
    • -
    • - Fees - -
      - {{fee.type}} {{fee.amount.amount}} {{fee.amount.currency}} -
      -
      -
    • -
    • - Subtotal - {{buy.buyInfo.subtotal.amount}} {{buy.buyInfo.subtotal.currency}} -
    • -
    • - Total - {{buy.buyInfo.total.amount}} {{buy.buyInfo.total.currency}} -
    • -
    • - Payout at - {{buy.buyInfo.payout_at | amCalendar}} -
    • -
    • - Deposit into Copay Wallet - {{buy.selectedWalletName}} -
    • -
    -
    -
    - -
    -
    -
    -
    -
    -

    Purchase initiated

    -

    - Bitcoin purchase completed. Coinbase has queued the transfer to your selected Copay wallet. -

    - - -
    -
    - -
    -
    diff --git a/www/views/coinbaseUri.html b/www/views/coinbaseUri.html deleted file mode 100644 index 5752c0873..000000000 --- a/www/views/coinbaseUri.html +++ /dev/null @@ -1,22 +0,0 @@ -
    -
    - -
    - -
    -
    -
    - -
    - -
    -
    {{coinbase.error}}
    - -
    -
    -
    -
    diff --git a/www/views/sellCoinbase.html b/www/views/sellCoinbase.html deleted file mode 100644 index 6de014946..000000000 --- a/www/views/sellCoinbase.html +++ /dev/null @@ -1,202 +0,0 @@ -
    -
    - - -
    - -
    -
    -
    -
      -
    • -
    -
    -
    -
    - -
    -
    -
    - - -
    - -
    - -
    - - - - -
    -
    - - -
    - - - - - BTC - USD -
    - -
    - - Enter the amount to get the exchange rate - - - Not available - - - ~ {{sell.sellPrice.amount * amount | currency : 'USD ' : 2}} - -
    - -
    - -
    - -
    - - -
    -
    - Continue -
    -
    - -
    -

    Price Sensitivity

    -

    - Coinbase has not yet implemented an immediate method to sell bitcoin from a wallet. To make this sale, funds - will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one - hour). -

    - - -

    - Estimated sale value: {{sell.sellPrice.amount * amount | currency : 'USD ' : 2}}
    - Still sell if price fall until: - {{(sell.sellPrice.amount - (selectedPriceSensitivity.value / 100) * sell.sellPrice.amount) * amount | currency : 'USD ' : 2}} -

    - -
    -
    - Back -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -

    Funds sent to Coinbase Account

    -

    - The transaction is not yet confirmed, and will show as "Processing" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase. -

    - -
    -
    - -
    -

    Confirm transaction

    - -
      -
    • - Amount - {{sell.sellInfo.amount.amount}} {{sell.sellInfo.amount.currency}} -
    • -
    • - Fees - -
      - {{fee.type}} {{fee.amount.amount}} {{fee.amount.currency}} -
      -
      -
    • -
    • - Subtotal - {{sell.sellInfo.subtotal.amount}} {{sell.sellInfo.subtotal.currency}} -
    • -
    • - Total - {{sell.sellInfo.total.amount}} {{sell.sellInfo.total.currency}} -
    • -
    • - Payout at - {{sell.sellInfo.payout_at | amCalendar}} -
    • -
    -
    -
    - -
    -
    -
    -
    -
    -

    Sale initiated

    -

    - A transfer has been initiated to your bank account and should arrive at {{sell.success.payout_at | amCalendar}}. -

    - - -
    -
    - -
    -
    From 163f1bf9132a8aa250bb600175249a9feb6d953e Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Tue, 3 Jan 2017 15:33:35 -0300 Subject: [PATCH 13/34] Fix coinbase oauth for desktop. Fix NWjs window --- src/js/controllers/coinbase.js | 22 ++++++++++------------ src/js/services/coinbaseService.js | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index f5ab4368e..3e03cefd3 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -33,9 +33,6 @@ angular.module('copayApp.controllers').controller('coinbaseController', function this.openAuthenticateWindow = function() { var oauthUrl = this.getAuthenticateUrl(); - externalLinkService.open(oauthUrl); - /* - * Not working (NW bug) if (!isNW) { externalLinkService.open(oauthUrl); } else { @@ -44,18 +41,19 @@ angular.module('copayApp.controllers').controller('coinbaseController', function gui.Window.open(oauthUrl, { focus: true, position: 'center' - }, function(win) { - win.on('loaded', function() { - var title = win.title; - if (title.indexOf('Coinbase') == -1) { - $scope.code = title; - self.submitOauthCode(title); - win.close(); - } + }, 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() { diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 90e0c5622..2c83ccb98 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -61,7 +61,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ + 'wallet:payment-methods:read'; // NW has a bug with Window Object - if (isCordova || isNW) { + if (isCordova) { credentials.REDIRECT_URI = coinbase.redirect_uri.mobile; } else { credentials.REDIRECT_URI = coinbase.redirect_uri.desktop; From 1a7f66273e2f9321bd7f311be31b17155bdf607a Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 4 Jan 2017 12:14:36 -0300 Subject: [PATCH 14/34] Update Coinbase from tab-home --- src/js/controllers/tab-home.js | 6 +++++- src/js/services/coinbaseService.js | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/js/controllers/tab-home.js b/src/js/controllers/tab-home.js index 9ef5ce4a7..ac2a6b471 100644 --- a/src/js/controllers/tab-home.js +++ b/src/js/controllers/tab-home.js @@ -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); diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 2c83ccb98..ccb6401b1 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -181,7 +181,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ if (lodash.isEmpty(credentials.CLIENT_ID)) { return cb('Coinbase is Disabled'); } - $log.debug('Init Token...'); + $log.debug('Trying to initialise Coinbase...'); storageService.getCoinbaseToken(credentials.NETWORK, function(err, accessToken) { if (err || !accessToken) return cb(); @@ -478,6 +478,10 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ root.getPendingTransactions = function(coinbasePendingTransactions) { root.init(function(err, data) { + if (err || lodash.isEmpty(data)) { + if (err) $log.error(err); + return; + } var accessToken = data.accessToken; var accountId = data.accountId; storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { @@ -550,6 +554,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; + 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) : {}; From 5495f603069bd4f4ed623eb3de2ba6c02706fbb8 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 5 Jan 2017 16:22:52 -0300 Subject: [PATCH 15/34] Wrong function call --- src/js/services/coinbaseService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index ccb6401b1..384b48083 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -172,7 +172,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ return cb(null, data[i].id); } } - coinbaseService.logout(function() {}); + root.logout(function() {}); return cb('Your primary account should be a WALLET. Set your wallet account as primary and try again'); }); }; From cb1ff562bc64797610e682410034c8fe90a337ae Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 5 Jan 2017 16:45:52 -0300 Subject: [PATCH 16/34] Rename variables --- src/js/controllers/preferencesCoinbase.js | 1 - src/js/controllers/tab-settings.js | 2 +- src/js/services/coinbaseService.js | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js/controllers/preferencesCoinbase.js b/src/js/controllers/preferencesCoinbase.js index beb39dc26..c55e996ef 100644 --- a/src/js/controllers/preferencesCoinbase.js +++ b/src/js/controllers/preferencesCoinbase.js @@ -17,7 +17,6 @@ angular.module('copayApp.controllers').controller('preferencesCoinbaseController $scope.$on("$ionicView.enter", function(event, data){ coinbaseService.setCredentials(); - $scope.network = coinbaseService.getEnvironment(); ongoingProcess.set('connectingCoinbase', true); coinbaseService.init(function(err, data) { if (err || lodash.isEmpty(data)) { diff --git a/src/js/controllers/tab-settings.js b/src/js/controllers/tab-settings.js index ab2bf1cff..b78fc0e3d 100644 --- a/src/js/controllers/tab-settings.js +++ b/src/js/controllers/tab-settings.js @@ -44,7 +44,7 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct if ($scope.coinbaseEnabled) { coinbaseService.setCredentials(); - storageService.getCoinbaseToken(coinbaseService.getEnvironment(), function(err, token) { + storageService.getCoinbaseToken(coinbaseService.getNetwork(), function(err, token) { if (err) $log.error(err); $scope.coinbaseToken = token; }); diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 384b48083..3c608a3ca 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -93,7 +93,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ } }; - root.getEnvironment = function() { + root.getNetwork = function() { return credentials.NETWORK; }; From 46d123a755f7f9873d891843fd2e78519bb558f9 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 5 Jan 2017 17:37:59 -0300 Subject: [PATCH 17/34] Uses appConfigService --- src/js/controllers/amount.js | 4 ++-- src/js/controllers/confirm.js | 4 ++-- src/js/services/coinbaseService.js | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index ad9f81f54..77bd6d0ec 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService, coinbaseService) { +angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService, coinbaseService, appConfigService) { var unitToSatoshi; var satToUnit; var unitDecimals; @@ -64,7 +64,7 @@ angular.module('copayApp.controllers').controller('amountController', function($ }); var dataSrc = { - name: 'Received from ' + $window.appConfig.nameCase + name: 'Received from ' + appConfigService.nameCase }; coinbaseService.createAddress(data.accessToken, data.accountId, dataSrc, function(err, data) { $scope.toAddress = data.data.address; diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 66416f1a7..89de29963 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, coinbaseService, bitpayCardService) { +angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, coinbaseService, bitpayCardService, appConfigService) { var cachedTxp = {}; var toAmount; var isChromeApp = platformInfo.isChromeApp; @@ -1017,7 +1017,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : ''; ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD'; - ctx['description'] = $window.appConfig.nameCase + ' Wallet: ' + $scope.wallet.name; + ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name; coinbaseService.savePendingTransaction(ctx, null, function(err) { ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); if (err) $log.debug(err); diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 3c608a3ca..e8c6be185 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').factory('coinbaseService', function($http, $log, $window, platformInfo, lodash, storageService, configService) { +angular.module('copayApp.services').factory('coinbaseService', function($http, $log, $window, platformInfo, lodash, storageService, configService, appConfigService) { var root = {}; var credentials = {}; var isCordova = platformInfo.isCordova; @@ -608,11 +608,12 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ var _sendToWallet = function(tx, accessToken, accountId, coinbasePendingTransactions) { if (!tx) return; + var desc = 'To ' + appConfigService.nameCase + ' Wallet'; var data = { to: tx.toAddr, amount: tx.amount.amount, currency: tx.amount.currency, - description: 'To Wallet' + description: desc }; root.sendTo(accessToken, accountId, data, function(err, res) { console.log('[coinbaseService.js:591] SEND TO COPAY',err, res); //TODO From 420a063017e2733396d89577f499616567a40bd8 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 5 Jan 2017 18:10:57 -0300 Subject: [PATCH 18/34] Clean code --- src/js/services/coinbaseService.js | 9 ++------- www/views/coinbase.html | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index e8c6be185..81dce1731 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -98,7 +98,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getOauthCodeUrl = function() { - // TODO CHANGE LIMIT BACK TO 1000 ************************************************* return credentials.HOST + '/oauth/authorize?response_type=code&client_id=' + credentials.CLIENT_ID @@ -106,7 +105,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ + credentials.REDIRECT_URI + '&state=SECURE_RANDOM&scope=' + credentials.SCOPE - + '&meta[send_limit_amount]=1&meta[send_limit_currency]=USD&meta[send_limit_period]=day'; + + '&meta[send_limit_amount]=1000&meta[send_limit_currency]=USD&meta[send_limit_period]=day'; }; root.getToken = function(code, cb) { @@ -486,7 +485,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ var accountId = data.accountId; storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { txs = txs ? JSON.parse(txs) : {}; - console.log('[coinbaseService.js:484] getCoinbaseTxs',err, txs); //TODO coinbasePendingTransactions.data = lodash.isEmpty(txs) ? null : txs; lodash.forEach(coinbasePendingTransactions.data, function(dataFromStorage, txId) { @@ -496,7 +494,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ (dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return; root.getTransaction(accessToken, accountId, txId, function(err, tx) { - console.log('[coinbaseService.js:494] getTransaction to UPDATE from Coinbase',err, tx); //TODO if (err || lodash.isEmpty(tx) || (tx.data && tx.data.error)) { _savePendingTransaction(dataFromStorage, { status: 'error', @@ -608,7 +605,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ var _sendToWallet = function(tx, accessToken, accountId, coinbasePendingTransactions) { if (!tx) return; - var desc = 'To ' + appConfigService.nameCase + ' Wallet'; + var desc = appConfigService.nameCase + ' Wallet'; var data = { to: tx.toAddr, amount: tx.amount.amount, @@ -616,7 +613,6 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ description: desc }; root.sendTo(accessToken, accountId, data, function(err, res) { -console.log('[coinbaseService.js:591] SEND TO COPAY',err, res); //TODO if (err) { _savePendingTransaction(tx, { status: 'error', @@ -637,7 +633,6 @@ console.log('[coinbaseService.js:591] SEND TO COPAY',err, res); //TODO return; } root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) { -console.log('[coinbaseService.js:633] GET UPDATED TX', err, sendTx); //TODO _savePendingTransaction(tx, { remove: true }, function(err) { diff --git a/www/views/coinbase.html b/www/views/coinbase.html index 1ec941348..054fb89ab 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -105,7 +105,7 @@

    Sold - Bought + Bought

    -{{tx.amount.amount.replace('-','')}} From e42d09574bd0fb6aacb4213c9986e40014ad1cd4 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Wed, 11 Jan 2017 19:38:05 -0300 Subject: [PATCH 19/34] Refactor buying process --- src/js/controllers/amount.js | 87 +++------------ src/js/controllers/buyCoinbase.js | 166 +++++++++++++++++++++++++++++ src/js/controllers/coinbase.js | 19 ++++ src/js/routes.js | 43 +++----- src/js/services/coinbaseService.js | 2 +- www/views/amount.html | 29 +---- www/views/buyCoinbase.html | 96 +++++++++++++++++ www/views/coinbase.html | 10 +- 8 files changed, 329 insertions(+), 123 deletions(-) create mode 100644 src/js/controllers/buyCoinbase.js create mode 100644 www/views/buyCoinbase.html diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 77bd6d0ec..c291e7c13 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -20,8 +20,8 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.isGlidera = data.stateParams.isGlidera; $scope.glideraAccessToken = data.stateParams.glideraAccessToken; - // Coinbase parameters - $scope.isCoinbase = data.stateParams.isCoinbase; + // Coinbase + $scope.coinbase = data.stateParams.coinbase; $scope.cardId = data.stateParams.cardId; $scope.showMenu = $ionicHistory.backView() && $ionicHistory.backView().stateName == 'tabs.send'; @@ -30,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.isCoinbase; + $scope.showAlternativeAmount = !!$scope.cardId || !!$scope.isGiftCard || !!$scope.isGlidera || !!$scope.coinbase; $scope.toColor = data.stateParams.toColor; $scope.showSendMax = false; $scope.customAmount = data.stateParams.customAmount; - if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !$scope.isCoinbase && !data.stateParams.toAddress) { + if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !$scope.coinbase && !data.stateParams.toAddress) { $log.error('Bad params at amount') throw ('bad params'); } @@ -50,51 +50,6 @@ angular.module('copayApp.controllers').controller('amountController', function($ }); } - if ($scope.isCoinbase) { - var currency = 'USD'; - - coinbaseService.init(function(err, data) { - if ($scope.isCoinbase == 'buy') { - coinbaseService.buyPrice(data.accessToken, currency, function(err, b) { - $scope.coinbaseBuyPrice = b.data || null; - }); - } else { - coinbaseService.sellPrice(data.accessToken, currency, function(err, s) { - $scope.coinbaseSellPrice = s.data || null; - }); - - var dataSrc = { - name: 'Received from ' + appConfigService.nameCase - }; - coinbaseService.createAddress(data.accessToken, data.accountId, dataSrc, function(err, data) { - $scope.toAddress = data.data.address; - }); - } - - $scope.coinbasePaymentMethods = []; - $scope.coinbaseSelectedPaymentMethodId = { value : null }; - coinbaseService.getPaymentMethods(data.accessToken, function(err, p) { - lodash.each(p.data, function(pm) { - if ($scope.isCoinbase == 'sell') { - if (pm.allow_sell) { - $scope.coinbasePaymentMethods.push(pm); - } - if (pm.allow_sell && pm.primary_sell) { - $scope.coinbaseSelectedPaymentMethodId.value = pm.id; - } - } else { - if (pm.allow_buy) { - $scope.coinbasePaymentMethods.push(pm); - } - if (pm.allow_buy && pm.primary_buy) { - $scope.coinbaseSelectedPaymentMethodId.value = pm.id; - } - } - }); - }); - }); - } - var reNr = /^[1234567890\.]$/; var reOp = /^[\*\+\-\/]$/; @@ -120,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; @@ -398,29 +357,17 @@ angular.module('copayApp.controllers').controller('amountController', function($ isGlidera: $scope.isGlidera, glideraAccessToken: $scope.glideraAccessToken }); - } else if ($scope.isCoinbase) { - if (lodash.isEmpty($scope.coinbaseSelectedPaymentMethodId.value)) { - popupService.showAlert(gettextCatalog.getString('Error'), 'No Payment Method Selected'); - return; - } - if ($scope.isCoinbase == 'sell' && lodash.isEmpty($scope.toAddress)) { - popupService.showAlert(gettextCatalog.getString('Error'), 'No Destination Address'); - return; - } - var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); - if (amountUSD < 1) { - popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 USD'); + } else if ($scope.coinbase) { + var amountAlternative = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); + if (amountAlternative < 1) { + popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 ' + $scope.alternativeIsoCode); return; } - var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; - $state.transitionTo('tabs.buyandsell.coinbase.confirm', { - toAmount: (amount * unitToSatoshi).toFixed(0), - toAddress: $scope.toAddress, - isCoinbase: $scope.isCoinbase, - coinbasePaymentMethodId: $scope.coinbaseSelectedPaymentMethodId.value, - coinbaseAmount: amountUSD, - coinbaseAmountCurrency: 'USD' + var goTo = 'tabs.buyandsell.coinbase.' + $scope.coinbase; + $state.transitionTo(goTo, { + amount: amountAlternative, + currency: $scope.alternativeIsoCode }); } else { var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js new file mode 100644 index 000000000..6454b6cbe --- /dev/null +++ b/src/js/controllers/buyCoinbase.js @@ -0,0 +1,166 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService) { + + var amount; + var currency; + + var initError = function(err) { + $log.error(err); + popupService.showAlert('Error', 'Could not connect to Coinbase', function() { + $ionicHistory.goBack(); + }); + }; + + $scope.$on("$ionicView.beforeEnter", function(event, data) { + amount = data.stateParams.amount; + currency = 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); + initError(err); + return; + } + var accessToken = res.accessToken; + + $scope.paymentMethods = []; + $scope.selectedPaymentMethodId = { value : null }; + coinbaseService.getPaymentMethods(accessToken, function(err, p) { + if (err) { + ongoingProcess.set('connectingCoinbase', false); + initError(err); + return; + } + lodash.each(p.data, function(pm) { + if (pm.allow_buy) { + $scope.paymentMethods.push(pm); + } + if (pm.allow_buy && pm.primary_buy) { + $scope.selectedPaymentMethodId.value = pm.id; + $scope.buyRequest(); + } + }); + }); + }); + }); + + $scope.buyRequest = function() { + ongoingProcess.set('connectingCoinbase', true); + coinbaseService.init(function(err, res) { + if (err) { + ongoingProcess.set('connectingCoinbase', false); + initError(err); + return; + } + var accessToken = res.accessToken; + var accountId = res.accountId; + var dataSrc = { + amount: amount, + currency: currency, + payment_method: $scope.selectedPaymentMethodId.value + }; + coinbaseService.buyRequest(accessToken, accountId, dataSrc, function(err, data) { + ongoingProcess.set('connectingCoinbase', false); + if (err) { + $log.error(err); + popupService.showAlert('Error', 'Could not create a buy request', function() { + $ionicHistory.goBack(); + }); + return; + } + $scope.buyRequestInfo = data.data; + }); + }); + }; + + $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); + coinbaseService.init(function(err, res) { + if (err) { + ongoingProcess.set('buyingBitcoin', false); + initError(err); + return; + } + var accessToken = res.accessToken; + var accountId = res.accountId; + coinbaseService.buyCommit(accessToken, accountId, $scope.buyRequestInfo.id, function(err, b) { + if (err) { + ongoingProcess.set('buyingBitcoin', false); + popupService.showAlert('Error', 'Could not complete purchase'); + return; + } + var tx = b.data.transaction; + if (!tx) { + ongoingProcess.set('buyingBitcoin', false); + popupService.showAlert('Error', 'Transaction not found'); + return; + } + + $timeout(function() { + coinbaseService.getTransaction(accessToken, accountId, tx.id, function(err, updatedTx) { + if (err) { + ongoingProcess.set('buyingBitcoin', false); + popupService.showAlert('Error', 'Transaction error'); + return; + } + walletService.getAddress($scope.wallet, false, function(err, walletAddr) { + if (err) { + ongoingProcess.set('buyingBitcoin', false); + popupService.showAlert('Error', 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) { + if (err) $log.debug(err); + ongoingProcess.set('buyingBitcoin', false); + $scope.buySuccess = updatedTx.data; + $timeout(function() { + $scope.$apply(); + }); + }); + }); + }); + }, 8000); + }); + }); + }); + }; + + $scope.showWalletSelector = function() { + $scope.walletSelectorTitle = ($scope.action) == 'buy' ? 'Receive in' : 'Sell From'; + $scope.showWallets = true; + }; + + $scope.onWalletSelect = function(wallet) { + $scope.wallet = wallet; + }; + + $scope.goBackHome = function() { + $ionicHistory.nextViewOptions({ + disableAnimate: true, + historyRoot: true + }); + $ionicHistory.clearHistory(); + $state.go('tabs.home').then(function() { + $state.transitionTo('tabs.buyandsell.coinbase'); + }); + }; +}); diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index 3e03cefd3..5f3c9684c 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -6,6 +6,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function var isCordova = platformInfo.isCordova; var init = function() { + var config = configService.getSync().wallet.settings; + $scope.currency = getCurrency(config.alternativeIsoCode); ongoingProcess.set('connectingCoinbase', true); coinbaseService.init(function(err, data) { ongoingProcess.set('connectingCoinbase', false); @@ -15,6 +17,15 @@ angular.module('copayApp.controllers').controller('coinbaseController', function } 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; + }); + // Updating accessToken and accountId $timeout(function() { $scope.accessToken = data.accessToken; @@ -25,6 +36,14 @@ angular.module('copayApp.controllers').controller('coinbaseController', function }); }; + var getCurrency = function(code) { + // ONLY "USD" and "EUR" + switch(code) { + case 'EUR' : return 'EUR'; + default : return 'USD' + }; + }; + $scope.updateTransactions = function() { $log.debug('Getting transactions...'); $scope.pendingTransactions = { data: {} }; diff --git a/src/js/routes.js b/src/js/routes.js index 38ae9331a..c458a689b 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -942,41 +942,32 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.buyandsell.coinbase.amount', { - url: '/amount/:isCoinbase', - views: { - 'tab-home@tabs': { - controller: 'amountController', - templateUrl: 'views/amount.html' - } + url: '/amount/:coinbase/:currency', + views: { + 'tab-home@tabs': { + controller: 'amountController', + templateUrl: 'views/amount.html' } - }) - .state('tabs.buyandsell.coinbase.confirm', { - url: '/confirm/:toAmount/:toAddress/:isCoinbase/:coinbasePaymentMethodId/:coinbaseAmount/:coinbaseAmountCurrency', - views: { - 'tab-home@tabs': { - controller: 'confirmController', - templateUrl: 'views/confirm.html' - } - } - }) - /* - .state('tabs.buyandsell.coinbase.buy', { - url: '/buy', + } + }) + .state('tabs.buyandsell.coinbase.buy', { + url: '/buy/:amount/:currency', + views: { 'tab-home@tabs': { controller: 'buyCoinbaseController', - controllerAs: 'buy', templateUrl: 'views/buyCoinbase.html' } - }) - .state('tabs.buyandsell.coinbase.sell', { - url: '/sell', + } + }) + .state('tabs.buyandsell.coinbase.sell', { + url: '/sell/:amount/:currency', + views: { 'tab-home@tabs': { controller: 'sellCoinbaseController', - controllerAs: 'sell', templateUrl: 'views/sellCoinbase.html' } - }) - */ + } + }) /* * diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 81dce1731..1e3a3cd33 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -394,7 +394,7 @@ 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 }; $http(_post('/accounts/' + accountId + '/buys', token, data)).then(function(data) { $log.info('Coinbase Buy Request: SUCCESS'); diff --git a/www/views/amount.html b/www/views/amount.html index 023e2125e..f42c024bb 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -1,7 +1,7 @@ - {{'Enter Amount' | translate}} + {{coinbase ? (coinbase == 'buy' ? 'Buy bitcoin' : 'Sell bitcoin') : ('Enter Amount' | translate)}} @@ -14,7 +14,7 @@ -

    +
    Recipient
    @@ -39,11 +39,12 @@
    -
    +
    - Amount + Amount
    Purchase Amount is limited to USD 1000 per day
    {{exchangeRate}}
    @@ -66,26 +67,6 @@ (remaining {{limits.monthlySellRemaining|currency:'':2}} {{limits.currency}})
    - -
    -
    - -
    -
    - 1 BTC ~ {{coinbaseBuyPrice.amount}} {{coinbaseBuyPrice.currency}} -
    -
    - 1 BTC ~ {{coinbaseSellPrice.amount}} {{coinbaseSellPrice.currency}} -
    -
    diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html new file mode 100644 index 000000000..df6e43ee6 --- /dev/null +++ b/www/views/buyCoinbase.html @@ -0,0 +1,96 @@ + + + + + Buy bitcoin + + + + +
    +
    + Purchase Info +
    + + +
    +
    + Amount + + {{buyRequestInfo.amount.amount}} {{buyRequestInfo.amount.currency}} + +
    +
    + Payout at + + {{buyRequestInfo.payout_at | amCalendar}} + +
    +
    + Receive in + {{wallet ? wallet.name : '...'}} +
    + +
    + Fees +
    +
    + {{fee.type}} + + {{fee.amount.amount}} {{fee.amount.currency}} + +
    + +
    + Total +
    +
    + Subtotal + + {{buyRequestInfo.subtotal.amount}} {{buyRequestInfo.subtotal.currency}} + +
    +
    + Total + + {{buyRequestInfo.total.amount}} {{buyRequestInfo.total.currency}} + +
    +
    +
    + + + +
    +
    + Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet +
    + +
    +
    + + +
    diff --git a/www/views/coinbase.html b/www/views/coinbase.html index 054fb89ab..87ebe2397 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -59,16 +59,22 @@
    +
    + {{buyPrice.amount}} {{buyPrice.currency}} + | + {{sellPrice.amount}} {{sellPrice.currency}} +
    +
    + href ui-sref="tabs.buyandsell.coinbase.amount({coinbase: 'buy', currency: currency})"> buy bitcoin Buy Bitcoin + href ui-sref="tabs.buyandsell.coinbase.amount({coinbase: 'sell', currency: currency})"> buy bitcoin Sell Bitcoin From c3131838ef2362074c5aee960857f44b3f25ab69 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 12 Jan 2017 09:58:12 -0300 Subject: [PATCH 20/34] Removes all Coinbase dependencies from amount view --- src/js/controllers/amount.js | 20 +++++++------------- src/js/controllers/buyCoinbase.js | 8 ++++++++ src/js/routes.js | 2 +- www/views/amount.html | 8 ++++---- www/views/coinbase.html | 4 ++-- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index c291e7c13..28542f3c1 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService, coinbaseService, appConfigService) { +angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService, appConfigService) { var unitToSatoshi; var satToUnit; var unitDecimals; @@ -20,8 +20,8 @@ angular.module('copayApp.controllers').controller('amountController', function($ $scope.isGlidera = data.stateParams.isGlidera; $scope.glideraAccessToken = data.stateParams.glideraAccessToken; - // Coinbase - $scope.coinbase = data.stateParams.coinbase; + // Go to... + $scope.nextStep = data.stateParams.nextStep; $scope.cardId = data.stateParams.cardId; $scope.showMenu = $ionicHistory.backView() && $ionicHistory.backView().stateName == 'tabs.send'; @@ -30,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.coinbase; + $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 && !$scope.coinbase && !data.stateParams.toAddress) { + if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !$scope.nextStep && !data.stateParams.toAddress) { $log.error('Bad params at amount') throw ('bad params'); } @@ -357,15 +357,9 @@ angular.module('copayApp.controllers').controller('amountController', function($ isGlidera: $scope.isGlidera, glideraAccessToken: $scope.glideraAccessToken }); - } else if ($scope.coinbase) { + } else if ($scope.nextStep) { var amountAlternative = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); - if (amountAlternative < 1) { - popupService.showAlert(gettextCatalog.getString('Error'), 'Amount must be at least 1.00 ' + $scope.alternativeIsoCode); - return; - } - - var goTo = 'tabs.buyandsell.coinbase.' + $scope.coinbase; - $state.transitionTo(goTo, { + $state.transitionTo($scope.nextStep, { amount: amountAlternative, currency: $scope.alternativeIsoCode }); diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js index 6454b6cbe..8425d1960 100644 --- a/src/js/controllers/buyCoinbase.js +++ b/src/js/controllers/buyCoinbase.js @@ -15,6 +15,14 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct $scope.$on("$ionicView.beforeEnter", function(event, data) { amount = data.stateParams.amount; currency = data.stateParams.currency; +console.log('[buyCoinbase.js:17]',amount, currency); //TODO + + if (amount < 1) { + popupService.showAlert('Error', 'Amount must be at least 1.00 ' + currency, function() { + $ionicHistory.goBack(); + }); + return; + } $scope.network = coinbaseService.getNetwork(); $scope.wallets = profileService.getWallets({ diff --git a/src/js/routes.js b/src/js/routes.js index c458a689b..e88c15275 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -942,7 +942,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } }) .state('tabs.buyandsell.coinbase.amount', { - url: '/amount/:coinbase/:currency', + url: '/amount/:nextStep/:currency', views: { 'tab-home@tabs': { controller: 'amountController', diff --git a/www/views/amount.html b/www/views/amount.html index f42c024bb..7335118b2 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -1,7 +1,7 @@ - {{coinbase ? (coinbase == 'buy' ? 'Buy bitcoin' : 'Sell bitcoin') : ('Enter Amount' | translate)}} + {{'Enter Amount' | translate}} @@ -14,7 +14,7 @@ -
    +
    Recipient
    @@ -39,8 +39,8 @@
    -
    +
    diff --git a/www/views/coinbase.html b/www/views/coinbase.html index 87ebe2397..1309c4778 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -68,13 +68,13 @@
    + href ui-sref="tabs.buyandsell.coinbase.amount({nextStep: 'tabs.buyandsell.coinbase.buy', currency: currency})"> buy bitcoin Buy Bitcoin + href ui-sref="tabs.buyandsell.coinbase.amount({nextStep: 'tabs.buyandsell.coinbase.sell', currency: currency})"> buy bitcoin Sell Bitcoin From 9b793c7668cdfdce81e1c796bbb2f0d5f92d448d Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 12 Jan 2017 10:01:11 -0300 Subject: [PATCH 21/34] Removes all Coinbase dependencies from amount view --- src/js/controllers/buyCoinbase.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js index 8425d1960..b48af5961 100644 --- a/src/js/controllers/buyCoinbase.js +++ b/src/js/controllers/buyCoinbase.js @@ -15,7 +15,6 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct $scope.$on("$ionicView.beforeEnter", function(event, data) { amount = data.stateParams.amount; currency = data.stateParams.currency; -console.log('[buyCoinbase.js:17]',amount, currency); //TODO if (amount < 1) { popupService.showAlert('Error', 'Amount must be at least 1.00 ' + currency, function() { From 021e9301d3d65ef8980f0b56ae5a54608b780eec Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Thu, 12 Jan 2017 20:29:34 -0300 Subject: [PATCH 22/34] Refactor selling bitcoin --- src/js/controllers/buyCoinbase.js | 7 +- src/js/controllers/sellCoinbase.js | 308 ++++++++++++++++++++++ src/js/services/coinbaseService.js | 3 +- src/sass/views/integrations/coinbase.scss | 24 +- www/views/buyCoinbase.html | 1 + www/views/coinbase.html | 2 +- www/views/sellCoinbase.html | 137 ++++++++++ 7 files changed, 459 insertions(+), 23 deletions(-) create mode 100644 src/js/controllers/sellCoinbase.js create mode 100644 www/views/sellCoinbase.html diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js index b48af5961..c336ee75f 100644 --- a/src/js/controllers/buyCoinbase.js +++ b/src/js/controllers/buyCoinbase.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService) { +angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService) { var amount; var currency; @@ -13,6 +13,8 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct }; $scope.$on("$ionicView.beforeEnter", function(event, data) { + coinbaseService.setCredentials(); + amount = data.stateParams.amount; currency = data.stateParams.currency; @@ -140,6 +142,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct ongoingProcess.set('buyingBitcoin', false); $scope.buySuccess = updatedTx.data; $timeout(function() { + $ionicScrollDelegate.resize(); $scope.$apply(); }); }); @@ -152,7 +155,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct }; $scope.showWalletSelector = function() { - $scope.walletSelectorTitle = ($scope.action) == 'buy' ? 'Receive in' : 'Sell From'; + $scope.walletSelectorTitle = 'Receive in'; $scope.showWallets = true; }; diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js new file mode 100644 index 000000000..58466ad7f --- /dev/null +++ b/src/js/controllers/sellCoinbase.js @@ -0,0 +1,308 @@ +'use strict'; + +angular.module('copayApp.controllers').controller('sellCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, appConfigService, configService) { + + var amount; + var currency; + + 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 publishAndSign = function (wallet, txp, onSendStatusChange, cb) { + if (!wallet.canSign() && !wallet.isPrivKeyExternal()) { + var err = 'No signing proposal: No private key'; + $log.info(err); + return cb(err); + } + + walletService.publishAndSign(wallet, txp, function(err, txp) { + if (err) return cb(err); + return cb(null, txp); + }, onSendStatusChange); + }; + + var 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, currency, function(err, sell) { + if (err) { + $log.debug(err); + checkTransaction(count, txp); + return; + } + sellPrice = sell.data; + + coinbaseService.getTransactions(accessToken, accountId, function(err, ctxs) { + if (err) { + $log.debug(err); + checkTransaction(count, txp); + return; + } + + var coinbaseTransactions = ctxs.data; + var txFound = false; + var ctx; + for(var i = 0; i < coinbaseTransactions.length; i++) { + ctx = coinbaseTransactions[i]; + if (ctx.type == 'send' && ctx.from && 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 + }); + + 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(); + + amount = data.stateParams.amount; + currency = data.stateParams.currency; + + if (amount < 1) { + showErrorAndBack('Amount must be at least 1.00 ' + currency); + return; + } + + $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; + + $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({ quote: true }); + }); + }); + }); + + $scope.sellRequest = function(opts) { + opts = opts || {}; + 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, + commit: opts.commit, + quote: opts.quote + }; + coinbaseService.sellRequest(accessToken, accountId, dataSrc, function(err, data) { + ongoingProcess.set('connectingCoinbase', false); + if (err) { + ongoingProcess.set('connectingCoinbase', false); + 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': toAddress, + 'amount': amountSat, + 'message': comment + }); + + var txp = { + toAddress: toAddress, + amount: amountSat, + outputs: outputs, + message: comment, + payProUrl: null, + excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true, + feeLevel: walletSettings.feeLevel || 'normal' + }; + + walletService.createTx($scope.wallet, txp, function(err, ctxp) { + if (err) { + ongoingProcess.set('sellingBitcoin', false, statusChangeHandler); + showError(err); + return; + } + $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); + }); + }); + }); + }); + }); + }; + + $scope.showWalletSelector = function() { + $scope.walletSelectorTitle = 'Sell From'; + $scope.showWallets = true; + }; + + $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'); + }); + }; + +}); diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 1e3a3cd33..8d1e9815a 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -368,7 +368,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'); diff --git a/src/sass/views/integrations/coinbase.scss b/src/sass/views/integrations/coinbase.scss index 0896abffa..5a5531a7f 100644 --- a/src/sass/views/integrations/coinbase.scss +++ b/src/sass/views/integrations/coinbase.scss @@ -1,21 +1,7 @@ -.coinbase-preferences { - ul { - font-size: 14px; - background: white; - li { - padding: 16px 10px 16px 16px; - border-bottom: 1px solid #E9E9EC; - } +#coinbase { + @extend .deflash-blue; + + .add-bottom-for-cta { + bottom: 92px; } } - -.coinbase-last-transactions-content { - background: #fff; - padding: 0.8rem 1rem; - cursor: pointer; - border-bottom: 1px solid #E4E8EC; -} - -.coinbase-pointer { - cursor: pointer; -} diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html index df6e43ee6..708b55c76 100644 --- a/www/views/buyCoinbase.html +++ b/www/views/buyCoinbase.html @@ -77,6 +77,7 @@
    +

    Bought

    Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet
    - -
    -
    -

    Bought

    - Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet -
    - -
    + + + Confirm purchase + + + Slide to buy + + + Bought +
    + Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet +
    +
    + Date: Thu, 12 Jan 2017 23:53:23 -0300 Subject: [PATCH 24/34] Refactor sell --- src/js/controllers/sellCoinbase.js | 8 +++----- www/views/sellCoinbase.html | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js index b7d2dc4aa..c90c48ce6 100644 --- a/src/js/controllers/sellCoinbase.js +++ b/src/js/controllers/sellCoinbase.js @@ -173,13 +173,12 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func return; } if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id; - $scope.sellRequest({ quote: true }); + $scope.sellRequest(); }); }); }); - $scope.sellRequest = function(opts) { - opts = opts || {}; + $scope.sellRequest = function() { ongoingProcess.set('connectingCoinbase', true); coinbaseService.init(function(err, res) { if (err) { @@ -193,8 +192,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func amount: amount, currency: currency, payment_method: $scope.selectedPaymentMethodId.value, - commit: opts.commit, - quote: opts.quote + quote: true }; coinbaseService.sellRequest(accessToken, accountId, dataSrc, function(err, data) { ongoingProcess.set('connectingCoinbase', false); diff --git a/www/views/sellCoinbase.html b/www/views/sellCoinbase.html index 2fcc8d771..b74b8783f 100644 --- a/www/views/sellCoinbase.html +++ b/www/views/sellCoinbase.html @@ -17,7 +17,7 @@
    From 0d1ff3e86d560681938b9d417f47e0d8e242c72c Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 13 Jan 2017 00:02:16 -0300 Subject: [PATCH 25/34] Clean code --- www/views/confirm.html | 91 +++++++----------------------------------- 1 file changed, 15 insertions(+), 76 deletions(-) diff --git a/www/views/confirm.html b/www/views/confirm.html index 2010d13c8..54d56c87b 100644 --- a/www/views/confirm.html +++ b/www/views/confirm.html @@ -12,10 +12,10 @@
    - Sending + Sending Sending maximum amount - Buying - Selling + Buying + Selling
    {{displayAmount || '...'}} {{displayUnit}}
    @@ -30,47 +30,17 @@ Expired
    -
    - At what percentage lower price would you accept to sell? -
    - -
    -
    - Coinbase has not yet implemented an immediate method to sell bitcoin from a wallet. To make this sale, funds - will be sent to your Coinbase account, and sold when Coinbase accepts the transaction (usually one - hour). -
    - Estimated sale value: - {{coinbaseSellPrice.amount * coinbaseAmountBTC | currency : 'USD ' : 2}} - - Still sell if price fall until: - {{(coinbaseSellPrice.amount - (selectedPriceSensitivity.data.value / 100) * - coinbaseSellPrice.amount) * coinbaseAmountBTC | currency : 'USD ' : 2}} - -
    -
    - Payment Method - Deposit into - {{coinbasePaymentMethodInfo.name}} -
    -
    -
    - Subtotal - {{coinbaseBuyRequest.subtotal.amount}} - {{coinbaseBuyRequest.subtotal.currency}} -
    -
    - Total - {{coinbaseBuyRequest.total.amount}} {{coinbaseBuyRequest.total.currency}} -
    -
    - Payout at - {{coinbaseBuyRequest.payout_at | amCalendar}} -
    -
    No wallets available
    @@ -192,29 +147,13 @@ slide-success-show="sendStatus === 'success'" slide-success-on-confirm="onSuccessConfirm()" slide-success-hide-on-confirm="true"> - Bought - Funds sent to Coinbase Account -
    - Payment Sent - Proposal Created - Transaction created -
    + Payment Sent + Proposal Created + Transaction created
    A transfer has been initiated from your bank account. Your bitcoins should arrive to your wallet in 2-4 business day A transfer has been initiated to your bank account. Should arrive in 4-6 business days
    -
    - - - Bitcoin purchase completed. Coinbase has queued the transfer to your selected wallet - - - - - The transaction is not yet confirmed, and will show as "Pending" in your Activity. The bitcoin sale will be completed automatically once it is confirmed by Coinbase. - - -
    Date: Fri, 13 Jan 2017 00:06:52 -0300 Subject: [PATCH 26/34] Clean code part 2 --- src/js/controllers/confirm.js | 241 +--------------------------------- 1 file changed, 5 insertions(+), 236 deletions(-) diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 89de29963..72cc12910 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, coinbaseService, bitpayCardService, appConfigService) { +angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, bitpayCardService, appConfigService) { var cachedTxp = {}; var toAmount; var isChromeApp = platformInfo.isChromeApp; @@ -25,12 +25,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.isGlidera = data.stateParams.isGlidera; $scope.glideraAccessToken = data.stateParams.glideraAccessToken; - // Coinbase parameters - $scope.isCoinbase = data.stateParams.isCoinbase; - $scope.coinbasePaymentMethodId = data.stateParams.coinbasePaymentMethodId; - $scope.coinbaseAmount = data.stateParams.coinbaseAmount; - $scope.coinbaseAmountCurrency = data.stateParams.coinbaseAmountCurrency; - toAmount = data.stateParams.toAmount; cachedSendMax = {}; $scope.useSendMax = data.stateParams.useSendMax == 'true' ? true : false; @@ -56,7 +50,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( var feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal'; $scope.feeLevel = feeService.feeOpts[feeLevel]; if ($scope.isGlidera) $scope.network = glideraService.getEnvironment(); - else if ($scope.isCoinbase) $scope.network = coinbaseService.getEnvironment(); else $scope.network = (new bitcore.Address($scope.toAddress)).network.name; resetValues(); setwallets(); @@ -92,7 +85,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( return; } - if (($scope.isGlidera || $scope.isCoinbase) == 'buy') { + if ($scope.isGlidera == 'buy') { initConfirm(); return; } @@ -159,7 +152,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( if ($scope.paypro) _paymentTimeControl($scope.paypro.expires); displayValues(); - if ($scope.wallets.length > 1 && $scope.isCoinbase != 'buy') $scope.showWalletSelector(); + if ($scope.wallets.length > 1) $scope.showWalletSelector(); else setWallet($scope.wallets[0]); $timeout(function() { $scope.$apply(); @@ -175,8 +168,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.cardAmountUSD) + ' USD'; } else if ($scope.giftCardAmountUSD) { $scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.giftCardAmountUSD) + ' USD'; - } else if ($scope.coinbaseAmount) { - $scope.alternativeAmountStr = $filter('formatFiatAmount')($scope.coinbaseAmount) + ' ' + $scope.coinbaseAmountCurrency; } else { txFormatService.formatAlternativeStr(toAmount, function(v) { $scope.alternativeAmountStr = v; @@ -184,27 +175,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( } if ($scope.isGlidera == 'buy') $scope.getBuyPrice(); if ($scope.isGlidera == 'sell') $scope.getSellPrice(); - - if ($scope.isCoinbase == 'buy') { - coinbaseBuyRequest($scope.coinbaseAmount, $scope.coinbaseAmountCurrency, $scope.coinbasePaymentMethodId); - } - - if ($scope.isCoinbase == 'sell') { - var satToBtc = 1 / 100000000; - $scope.coinbaseAmountBTC = (toAmount * satToBtc).toFixed(8); - $scope.priceSensitivity = coinbaseService.priceSensitivity; - $scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity }; - - coinbaseService.init(function(err, data) { - coinbaseService.sellPrice(data.accessToken, $scope.coinbaseAmountCurrency, function(err, sell) { - $scope.coinbaseSellPrice = sell.data; - }); - coinbaseService.getPaymentMethod(data.accessToken, $scope.coinbasePaymentMethodId, function(err, data) { - if (err) $log.error(err); - $scope.coinbasePaymentMethodInfo = data.data; - }); - }); - } }; function resetValues() { @@ -313,7 +283,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); $scope.showWalletSelector = function() { - $scope.walletSelectorTitle = ($scope.isGlidera || $scope.isCoinbase) == 'buy' ? 'Receive in' : ($scope.isGlidera || $scope.isCoinbase) == 'sell' ? 'Sell From' : gettextCatalog.getString('Send from'); + $scope.walletSelectorTitle = $scope.isGlidera == 'buy' ? 'Receive in' : $scope.isGlidera == 'sell' ? 'Sell From' : gettextCatalog.getString('Send from'); if (!$scope.useSendMax && ($scope.insufficientFunds || $scope.noMatchingWallet)) return; $scope.showWallets = true; }; @@ -392,7 +362,7 @@ angular.module('copayApp.controllers').controller('confirmController', function( $scope.wallet = wallet; $scope.fee = $scope.txp = null; - if ($scope.isGlidera || $scope.isCoinbase == 'buy') return; + if ($scope.isGlidera) return; if (stop) { $timeout.cancel(stop); stop = null; @@ -559,99 +529,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( return; } - if ($scope.isCoinbase) { - - // BUY - if ($scope.isCoinbase == 'buy') { - ongoingProcess.set('buyingBitcoin', true, onSendStatusChange); - coinbaseService.init(function(err, res) { - if (err) { - $log.error(err); - return; - } - var token = res.accessToken; - var accountId = res.accountId; - coinbaseService.buyCommit(token, accountId, $scope.coinbaseBuyRequest.id, function(err, b) { - if (err) { - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - popupService.showAlert(gettextCatalog.getString('Error'), 'Could not complete purchase'); - return; - } - var tx = b.data.transaction; - if (!tx) { - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - popupService.showAlert(gettextCatalog.getString('Error'), 'Transaction not found'); - return; - } - - $timeout(function() { - coinbaseService.getTransaction(token, accountId, tx.id, function(err, updatedTx) { - if (err) { - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - popupService.showAlert(gettextCatalog.getString('Error'), 'Transaction error'); - return; - } - walletService.getAddress($scope.wallet, false, function(err, walletAddr) { - if (err) { - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - popupService.showAlert(gettextCatalog.getString('Error'), 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) { - if (err) $log.debug(err); - ongoingProcess.set('buyingBitcoin', false, onSendStatusChange); - $scope.coinbaseBuySuccess = updatedTx.data; - $timeout(function() { - $scope.$apply(); - }); - }); - }); - }); - }, 8000); - }); - }); - } - - - // SELL - if ($scope.isCoinbase == 'sell') { - - ongoingProcess.set('sellingBitcoin', true, onSendStatusChange); - createTx($scope.wallet, false, function(err, txp) { - var message = gettextCatalog.getString('Selling {{amountStr}} from {{name}}', { - amountStr: $scope.coinbaseAmount + ' ' + $scope.coinbaseAmountCurrency, - name: $scope.wallet.name - }); - var okText = gettextCatalog.getString('Confirm'); - var cancelText = gettextCatalog.getString('Cancel'); - - popupService.showConfirm(null, message, okText, cancelText, function(ok) { - if (!ok) { - $timeout(function() { - $scope.$apply(); - }); - return; - } - // SIMULATE publishAndSign - //$log.warn('Simulating publishAndSign...'); - $log.debug('Plublish and Sign...'); - publishAndSign(wallet, txp, function() {}, function(err) { - if (err) return setSendError(err); - $log.debug('Finished publish and sign. Trying to sell...'); - var count = 0; - checkTransaction(count, txp, onSendStatusChange); - }); - }); - }); - } - - return; - } - ongoingProcess.set('creatingTx', true, onSendStatusChange); createTx(wallet, false, function(err, txp) { ongoingProcess.set('creatingTx', false, onSendStatusChange); @@ -726,7 +603,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( var fromBitPayCard = previousView.match(/tabs.bitpayCard/) ? true : false; var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false; var fromGlidera = previousView.match(/tabs.buyandsell.glidera/) ? true : false; - var fromCoinbase = previousView.match(/tabs.buyandsell.coinbase/) ? true : false; $ionicHistory.nextViewOptions({ disableAnimate: true @@ -760,15 +636,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( $state.go('tabs.home').then(function() { $state.transitionTo('tabs.buyandsell.glidera'); }); - } else if (fromCoinbase) { - $ionicHistory.nextViewOptions({ - disableAnimate: true, - historyRoot: true - }); - $ionicHistory.clearHistory(); - $state.go('tabs.home').then(function() { - $state.transitionTo('tabs.buyandsell.coinbase'); - }); } else { $ionicHistory.nextViewOptions({ disableAnimate: true, @@ -979,69 +846,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( }, onSendStatusChange); }; - var checkTransaction = lodash.throttle(function(count, txp, onSendStatusChange) { - $log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5'); - coinbaseService.init(function(err, res) { - if (err) { - ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); - $log.error(err); - checkTransaction(count, txp, onSendStatusChange); - return; - } - var token = res.accessToken; - var accountId = res.accountId; - - coinbaseService.getTransactions(token, accountId, function(err, ctxs) { - if (err) { - ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); - $log.debug(err); - checkTransaction(count, txp, onSendStatusChange); - return; - } - - // TX amount in BTC - var satToBtc = 1 / 100000000; - var amountBTC = (txp.amount * satToBtc).toFixed(8); - - var coinbaseTransactions = ctxs.data; - var txFound = false; - var ctx; - for(var i = 0; i < coinbaseTransactions.length; i++) { - ctx = coinbaseTransactions[i]; - if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) { - $log.warn('Transaction found!', ctx); - txFound = true; - $log.debug('Saving transaction to process later...'); - ctx['payment_method'] = $scope.coinbasePaymentMethodId; - ctx['status'] = 'pending'; // Forcing "pending" status to process later - ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data; - ctx['sell_price_amount'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.amount : ''; - ctx['sell_price_currency'] = $scope.coinbaseSellPrice ? $scope.coinbaseSellPrice.currency : 'USD'; - ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name; - coinbaseService.savePendingTransaction(ctx, null, function(err) { - ongoingProcess.set('sellingBitcoin', false, onSendStatusChange); - if (err) $log.debug(err); - $scope.coinbaseSendInfo = ctx; - $timeout(function() { - $scope.$apply(); - }); - }); - return; - } - } - if (!txFound) { - // Transaction sent, but could not be verified by Coinbase.com - $log.warn('Transaction not found in Coinbase.'); - if (count < 5) { - checkTransaction(count + 1, txp, onSendStatusChange); - } - } - }); - }); - }, 8000, { - 'leading': true - }); - var debounceCreate = lodash.throttle(function(count, dataSrc) { debounceCreateGiftCard(count, dataSrc); }, 8000, { @@ -1107,39 +911,4 @@ angular.module('copayApp.controllers').controller('confirmController', function( } }); }; - - var coinbaseBuyRequest = function(amount, currency, paymentMethodId) { - var dataSrc = { - amount: amount, - currency: currency, - payment_method: paymentMethodId - }; - coinbaseService.init(function(err, res) { - if (err) { - $scope.showWallets = null; - $log.error(err); - popupService.showAlert(gettextCatalog.getString('Error'), 'Could not connect to Coinbase', function() { - $ionicHistory.goBack(); - }); - return; - } - coinbaseService.getPaymentMethod(res.accessToken, paymentMethodId, function(err, data) { - if (err) $log.error(err); - $scope.coinbasePaymentMethodInfo = data.data; - }); - - coinbaseService.buyRequest(res.accessToken, res.accountId, dataSrc, function(err, data) { - if (err) { - $scope.showWallets = null; - $log.error(err); - popupService.showAlert(gettextCatalog.getString('Error'), 'Could not create a buy request', function() { - $ionicHistory.goBack(); - }); - return; - } - $scope.coinbaseBuyRequest = data.data; - }); - }); - }; - }); From 9ac1e565ac249f1bbc789d6c96cffafb9fb4cfba Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 13 Jan 2017 01:05:52 -0300 Subject: [PATCH 27/34] Clean code. Fix coinbase tx modal --- src/js/controllers/coinbase.js | 51 ++++++++++--------- .../controllers/modals/coinbaseTxDetails.js | 24 ++++++--- src/js/services/coinbaseService.js | 28 ++++++---- www/views/coinbase.html | 20 ++++---- www/views/modals/coinbase-tx-details.html | 16 ++---- 5 files changed, 77 insertions(+), 62 deletions(-) diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index 5f3c9684c..3492245c6 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -8,31 +8,34 @@ angular.module('copayApp.controllers').controller('coinbaseController', function var init = function() { var config = configService.getSync().wallet.settings; $scope.currency = getCurrency(config.alternativeIsoCode); - ongoingProcess.set('connectingCoinbase', true); - coinbaseService.init(function(err, data) { - ongoingProcess.set('connectingCoinbase', false); - if (err || lodash.isEmpty(data)) { - if (err) { - popupService.showAlert(gettextCatalog.getString('Error'), err); + coinbaseService.getStoredToken(function(at) { + $scope.accessToken = at; + + // Update Access Token if necessary + coinbaseService.init(function(err, data) { + if (err || lodash.isEmpty(data)) { + if (err) { + popupService.showAlert(gettextCatalog.getString('Error'), err); + } + return; } - 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; - }); + // 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; + }); - // Updating accessToken and accountId - $timeout(function() { - $scope.accessToken = data.accessToken; - $scope.accountId = data.accountId; - $scope.updateTransactions(); - $scope.$apply(); - }, 100); + // Updating accessToken and accountId + $timeout(function() { + $scope.accessToken = data.accessToken; + $scope.accountId = data.accountId; + $scope.updateTransactions(); + $scope.$apply(); + }, 100); + }); }); }; @@ -104,8 +107,8 @@ angular.module('copayApp.controllers').controller('coinbaseController', function scope: $scope, animation: 'slide-in-up' }).then(function(modal) { - $scope.coinbaseTxDetailsModal = modal; - $scope.coinbaseTxDetailsModal.show(); + $scope.modal = modal; + $scope.modal.show(); }); }; diff --git a/src/js/controllers/modals/coinbaseTxDetails.js b/src/js/controllers/modals/coinbaseTxDetails.js index 3d952b168..bb9cd3020 100644 --- a/src/js/controllers/modals/coinbaseTxDetails.js +++ b/src/js/controllers/modals/coinbaseTxDetails.js @@ -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.close(); + 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.close = function() { - $scope.coinbaseTxDetailsModal.hide(); + $scope.modal.hide().then(function() { + if ($scope.updateRequired) $scope.updateTransactions(); + }); }; }); diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 6eaa27b11..7d04d100a 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -97,6 +97,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ return credentials.NETWORK; }; + root.getStoredToken = function(cb) { + storageService.getCoinbaseToken(credentials.NETWORK, function(err, accessToken) { + if (err || !accessToken) return cb(); + return cb(accessToken); + }); + }; + root.getOauthCodeUrl = function() { return credentials.HOST + '/oauth/authorize?response_type=code&client_id=' @@ -478,16 +485,17 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }; root.getPendingTransactions = function(coinbasePendingTransactions) { - root.init(function(err, data) { - if (err || lodash.isEmpty(data)) { - if (err) $log.error(err); - return; - } - var accessToken = data.accessToken; - var accountId = data.accountId; - storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) { - txs = txs ? JSON.parse(txs) : {}; - coinbasePendingTransactions.data = lodash.isEmpty(txs) ? null : txs; + 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') || diff --git a/www/views/coinbase.html b/www/views/coinbase.html index baaa4f54a..48cc8a7c0 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -13,7 +13,7 @@
    -
    +
    @@ -53,20 +53,22 @@
    -
    +
    -
    +
    -
    - {{buyPrice.amount}} {{buyPrice.currency}} - | - {{sellPrice.amount}} {{sellPrice.currency}} +
    + ... | ... + + {{buyPrice.amount}} {{buyPrice.currency}} + | + {{sellPrice.amount}} {{sellPrice.currency}} +
    -
    +
    buy bitcoin diff --git a/www/views/modals/coinbase-tx-details.html b/www/views/modals/coinbase-tx-details.html index 6d6dce8b4..ed8142b0d 100644 --- a/www/views/modals/coinbase-tx-details.html +++ b/www/views/modals/coinbase-tx-details.html @@ -79,18 +79,10 @@ Receive bitcoin in {{tx.description}} +
    +
    + Remove transaction +
    - -
    - From 5666a66e1d0d871b227816a15d43458fd835a5da Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 13 Jan 2017 01:21:32 -0300 Subject: [PATCH 28/34] Clean code 3 --- src/js/controllers/amount.js | 2 +- src/js/controllers/confirm.js | 33 +++++++++++---------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 28542f3c1..0cb87268b 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService, appConfigService) { +angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService) { var unitToSatoshi; var satToUnit; var unitDecimals; diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js index 72cc12910..9e2432e16 100644 --- a/src/js/controllers/confirm.js +++ b/src/js/controllers/confirm.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, bitpayCardService, appConfigService) { +angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, bitpayCardService) { var cachedTxp = {}; var toAmount; var isChromeApp = platformInfo.isChromeApp; @@ -527,8 +527,8 @@ angular.module('copayApp.controllers').controller('confirmController', function( } }); return; - } - + } + ongoingProcess.set('creatingTx', true, onSendStatusChange); createTx(wallet, false, function(err, txp) { ongoingProcess.set('creatingTx', false, onSendStatusChange); @@ -579,14 +579,12 @@ angular.module('copayApp.controllers').controller('confirmController', function( ( processName === 'broadcastingTx' || ((processName === 'signingTx') && $scope.wallet.m > 1) || - (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) || - (processName == 'buyingBitcoin') || - (processName == 'sellingBitcoin') + (processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal()) ) && !isOn) { $scope.sendStatus = 'success'; $timeout(function() { $scope.$digest(); - }, 100) + }, 100); } else if (showName) { $scope.sendStatus = showName; } @@ -804,26 +802,18 @@ angular.module('copayApp.controllers').controller('confirmController', function( }); }; - function publishAndSign(wallet, txp, onSendStatusChange, cb) { + function publishAndSign(wallet, txp, onSendStatusChange) { if (!wallet.canSign() && !wallet.isPrivKeyExternal()) { $log.info('No signing proposal: No private key'); - walletService.onlyPublish(wallet, txp, function(err) { - if (err) { - if (cb) return cb(err); - else return setSendError(err); - } - if (cb) return cb(); - else return; + return walletService.onlyPublish(wallet, txp, function(err) { + if (err) setSendError(err); }, onSendStatusChange); } walletService.publishAndSign(wallet, txp, function(err, txp) { - if (err) { - if (cb) return cb(err); - else return setSendError(err); - } + if (err) return setSendError(err); var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName; var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false; @@ -842,7 +832,6 @@ angular.module('copayApp.controllers').controller('confirmController', function( ongoingProcess.set('creatingGiftCard', true); debounceCreate(count, dataSrc, onSendStatusChange); } - if (cb) return cb(); }, onSendStatusChange); }; @@ -905,9 +894,9 @@ angular.module('copayApp.controllers').controller('confirmController', function( } if (lodash.isEmpty(res)) return; if (unitName == 'bits') { - $scope.exchangeRate = '1,000,000 bits ~ ' + res.data.rate + ' ' + alternativeIsoCode; + $scope.exchangeRate = '1,000,000 bits ~ ' + res.rate + ' ' + alternativeIsoCode; } else { - $scope.exchangeRate = '1 BTC ~ ' + res.data.rate + ' ' + alternativeIsoCode; + $scope.exchangeRate = '1 BTC ~ ' + res.rate + ' ' + alternativeIsoCode; } }); }; From b5f3648aeecd83d169dad82101ecfaf0761cfa0c Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 13 Jan 2017 01:37:13 -0300 Subject: [PATCH 29/34] Fix loading --- src/js/controllers/coinbase.js | 2 ++ src/js/controllers/tab-settings.js | 7 +++---- www/views/coinbase.html | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index 3492245c6..313e35c9b 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -12,7 +12,9 @@ angular.module('copayApp.controllers').controller('coinbaseController', function $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) { popupService.showAlert(gettextCatalog.getString('Error'), err); diff --git a/src/js/controllers/tab-settings.js b/src/js/controllers/tab-settings.js index b78fc0e3d..a92d8cb6c 100644 --- a/src/js/controllers/tab-settings.js +++ b/src/js/controllers/tab-settings.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, appConfigService, $window, $log, lodash, uxLanguage, platformInfo, profileService, feeService, configService, externalLinkService, bitpayCardService, storageService, glideraService, coinbaseService, 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; @@ -44,9 +44,8 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct if ($scope.coinbaseEnabled) { coinbaseService.setCredentials(); - storageService.getCoinbaseToken(coinbaseService.getNetwork(), function(err, token) { - if (err) $log.error(err); - $scope.coinbaseToken = token; + coinbaseService.getStoredToken(function(at) { + $scope.coinbaseToken = at; }); } diff --git a/www/views/coinbase.html b/www/views/coinbase.html index 48cc8a7c0..b2a687880 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -86,7 +86,7 @@
    Activity -
    +
    +
    + +
    From 3bf8b68b3cb5f00e71216fcc4b4fa31de23e5b6d Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 13 Jan 2017 11:00:04 -0300 Subject: [PATCH 30/34] Fix UI --- src/sass/views/integrations/coinbase.scss | 118 ++++++++++++++++++++++ www/views/buyCoinbase.html | 69 ++++++------- www/views/coinbase.html | 2 +- www/views/sellCoinbase.html | 80 +++++++-------- 4 files changed, 194 insertions(+), 75 deletions(-) diff --git a/src/sass/views/integrations/coinbase.scss b/src/sass/views/integrations/coinbase.scss index 5a5531a7f..ab1c33e73 100644 --- a/src/sass/views/integrations/coinbase.scss +++ b/src/sass/views/integrations/coinbase.scss @@ -1,7 +1,125 @@ #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; + } + + &.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; + } + } + } + } } diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html index 39af86ac1..2b78e8a4d 100644 --- a/www/views/buyCoinbase.html +++ b/www/views/buyCoinbase.html @@ -7,51 +7,52 @@ -
    -
    - Purchase Info -
    - +
    -
    -
    - Amount - - {{buyRequestInfo.amount.amount}} {{buyRequestInfo.amount.currency}} - +
    +
    + buy bitcoin + Buying
    -
    - Receive in - {{wallet ? wallet.name : '...'}} +
    +
    {{buyRequestInfo.amount.amount}} {{buyRequestInfo.amount.currency}}
    +
    {{buyRequestInfo.subtotal.amount}} {{buyRequestInfo.subtotal.currency}}
    +
    +
    + +
    + + +
    +
    Receive in
    +
    + + + + {{wallet ? wallet.name : '...'}} +
    +
    - Fees + Total to pay
    - {{fee.type}} + + {{fee.type}} + {{fee.amount.amount}} {{fee.amount.currency}}
    - -
    - Total -
    -
    - Subtotal - - {{buyRequestInfo.subtotal.amount}} {{buyRequestInfo.subtotal.currency}} - -
    Total diff --git a/www/views/coinbase.html b/www/views/coinbase.html index b2a687880..b12cdf1ce 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -77,7 +77,7 @@ - buy bitcoin + sell bitcoin Sell Bitcoin diff --git a/www/views/sellCoinbase.html b/www/views/sellCoinbase.html index b74b8783f..d698f3009 100644 --- a/www/views/sellCoinbase.html +++ b/www/views/sellCoinbase.html @@ -7,21 +7,42 @@ -
    -
    - Sale Info -
    - +
    + +
    +
    + sell bitcoin + Selling +
    +
    +
    {{sellRequestInfo.amount.amount}} {{sellRequestInfo.amount.currency}}
    +
    {{sellRequestInfo.subtotal.amount}} {{sellRequestInfo.subtotal.currency}}
    +
    +
    + +
    + +
    +
    From
    +
    + + + + {{wallet ? wallet.name : '...'}} +
    + +
    + + -
    At what percentage lower price would you accept to sell?
    @@ -54,36 +75,15 @@
    - Sell from -
    -
    - Wallet - {{wallet ? wallet.name : '...'}} -
    -
    - Amount - - {{sellRequestInfo.amount.amount}} {{sellRequestInfo.amount.currency}} - -
    - -
    - Fees + Total to receive
    - {{fee.type}} - - {{fee.amount.amount}} {{fee.amount.currency}} + + {{fee.type}} -
    - -
    - Total -
    -
    - Subtotal - {{sellRequestInfo.subtotal.amount}} {{sellRequestInfo.subtotal.currency}} + - + {{fee.amount.amount}} {{fee.amount.currency}}
    From 89639a8d4854f29c253ee6fbce6ac04359d013bc Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Fri, 13 Jan 2017 11:05:33 -0300 Subject: [PATCH 31/34] Fix icon --- www/views/coinbase.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/www/views/coinbase.html b/www/views/coinbase.html index b12cdf1ce..ec6405d2d 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -71,13 +71,15 @@
    - buy bitcoin + buy bitcoin Buy Bitcoin - sell bitcoin + sell bitcoin Sell Bitcoin From 8d1d59cb3b1a916a679d0a699266eedeec728fe0 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Sat, 14 Jan 2017 19:22:33 -0300 Subject: [PATCH 32/34] Clean UI. Adds amount parser for coinbaseService --- src/js/controllers/amount.js | 5 ++-- src/js/controllers/buyCoinbase.js | 14 +++++----- src/js/controllers/coinbase.js | 17 +++---------- src/js/controllers/sellCoinbase.js | 16 ++++++------ src/js/services/coinbaseService.js | 31 ++++++++++++++++++++++- src/sass/views/integrations/coinbase.scss | 4 +++ www/views/buyCoinbase.html | 22 +++++++++++----- www/views/coinbase.html | 2 +- www/views/sellCoinbase.html | 20 ++++++++++----- 9 files changed, 85 insertions(+), 46 deletions(-) diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 0cb87268b..259c4930b 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -358,10 +358,9 @@ angular.module('copayApp.controllers').controller('amountController', function($ glideraAccessToken: $scope.glideraAccessToken }); } else if ($scope.nextStep) { - var amountAlternative = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount)); $state.transitionTo($scope.nextStep, { - amount: amountAlternative, - currency: $scope.alternativeIsoCode + amount: _amount, + currency: $scope.showAlternativeAmount ? $scope.alternativeIsoCode : '' }); } else { var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount; diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js index aa5be6a82..59bacde15 100644 --- a/src/js/controllers/buyCoinbase.js +++ b/src/js/controllers/buyCoinbase.js @@ -36,14 +36,10 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct $scope.$on("$ionicView.beforeEnter", function(event, data) { coinbaseService.setCredentials(); - amount = data.stateParams.amount; - currency = data.stateParams.currency; + [amount, currency, $scope.amountUnitStr] = coinbaseService.parseAmount( + data.stateParams.amount, + data.stateParams.currency); - if (amount < 1) { - showErrorAndBack('Amount must be at least 1.00 ' + currency); - return; - } - $scope.network = coinbaseService.getNetwork(); $scope.wallets = profileService.getWallets({ onlyComplete: true, @@ -60,6 +56,10 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct } var accessToken = res.accessToken; + 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) { diff --git a/src/js/controllers/coinbase.js b/src/js/controllers/coinbase.js index 313e35c9b..8b23d9b28 100644 --- a/src/js/controllers/coinbase.js +++ b/src/js/controllers/coinbase.js @@ -1,13 +1,12 @@ 'use strict'; -angular.module('copayApp.controllers').controller('coinbaseController', function($rootScope, $scope, $timeout, $ionicModal, $log, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, gettextCatalog, externalLinkService) { +angular.module('copayApp.controllers').controller('coinbaseController', function($scope, $timeout, $ionicModal, $log, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, externalLinkService) { var isNW = platformInfo.isNW; var isCordova = platformInfo.isCordova; var init = function() { - var config = configService.getSync().wallet.settings; - $scope.currency = getCurrency(config.alternativeIsoCode); + $scope.currency = coinbaseService.getAvailableCurrency(); coinbaseService.getStoredToken(function(at) { $scope.accessToken = at; @@ -17,7 +16,7 @@ angular.module('copayApp.controllers').controller('coinbaseController', function $scope.loading = false; if (err || lodash.isEmpty(data)) { if (err) { - popupService.showAlert(gettextCatalog.getString('Error'), err); + popupService.showAlert('Error', err); } return; } @@ -41,14 +40,6 @@ angular.module('copayApp.controllers').controller('coinbaseController', function }); }; - var getCurrency = function(code) { - // ONLY "USD" and "EUR" - switch(code) { - case 'EUR' : return 'EUR'; - default : return 'USD' - }; - }; - $scope.updateTransactions = function() { $log.debug('Getting transactions...'); $scope.pendingTransactions = { data: {} }; @@ -93,7 +84,7 @@ angular.module('copayApp.controllers').controller('coinbaseController', function coinbaseService.getToken(code, function(err, accessToken) { ongoingProcess.set('connectingCoinbase', false); if (err) { - popupService.showAlert(gettextCatalog.getString('Error'), err); + popupService.showAlert('Error', err); return; } $scope.accessToken = accessToken; diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js index c90c48ce6..af0e62992 100644 --- a/src/js/controllers/sellCoinbase.js +++ b/src/js/controllers/sellCoinbase.js @@ -49,7 +49,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func var accountId = res.accountId; var sellPrice = null; - coinbaseService.sellPrice(accessToken, currency, function(err, sell) { + coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, sell) { if (err) { $log.debug(err); checkTransaction(count, txp); @@ -119,14 +119,10 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func $scope.$on("$ionicView.beforeEnter", function(event, data) { coinbaseService.setCredentials(); - amount = data.stateParams.amount; - currency = data.stateParams.currency; + [amount, currency, $scope.amountUnitStr] = coinbaseService.parseAmount( + data.stateParams.amount, + data.stateParams.currency); - if (amount < 1) { - showErrorAndBack('Amount must be at least 1.00 ' + currency); - return; - } - $scope.priceSensitivity = coinbaseService.priceSensitivity; $scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity }; @@ -147,6 +143,10 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func } 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) { diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 7d04d100a..213e2e3e7 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').factory('coinbaseService', function($http, $log, $window, platformInfo, lodash, storageService, configService, appConfigService) { +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; @@ -104,6 +104,35 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ }); }; + root.getAvailableCurrency = function() { + var config = configService.getSync().wallet.settings; + // ONLY "USD" and "EUR" + switch(config.alternativeIsoCode) { + case 'EUR' : return 'EUR'; + 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=' diff --git a/src/sass/views/integrations/coinbase.scss b/src/sass/views/integrations/coinbase.scss index ab1c33e73..2221463f3 100644 --- a/src/sass/views/integrations/coinbase.scss +++ b/src/sass/views/integrations/coinbase.scss @@ -81,6 +81,10 @@ vertical-align: middle; } + .total-amount { + font-weight: bold; + } + &.single-line { display: flex; align-items: center; diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html index 2b78e8a4d..01f9a5c87 100644 --- a/www/views/buyCoinbase.html +++ b/www/views/buyCoinbase.html @@ -15,8 +15,10 @@ Buying
    -
    {{buyRequestInfo.amount.amount}} {{buyRequestInfo.amount.currency}}
    -
    {{buyRequestInfo.subtotal.amount}} {{buyRequestInfo.subtotal.currency}}
    +
    {{amountUnitStr}}
    +
    + @ ${{buyPrice.amount}} per BTC +
    @@ -43,19 +45,25 @@
    - Total to pay + Transaction details +
    +
    + Amount + + {{buyRequestInfo.subtotal.amount}} {{buyRequestInfo.subtotal.currency}} +
    - {{fee.type}} + {{fee.type}} fee - {{fee.amount.amount}} {{fee.amount.currency}} + {{fee.amount.amount}} {{fee.amount.currency}}
    - Total - + Total to pay + {{buyRequestInfo.total.amount}} {{buyRequestInfo.total.currency}}
    diff --git a/www/views/coinbase.html b/www/views/coinbase.html index ec6405d2d..cd953d9bc 100644 --- a/www/views/coinbase.html +++ b/www/views/coinbase.html @@ -60,7 +60,7 @@
    - ... | ... + ... {{buyPrice.amount}} {{buyPrice.currency}} | diff --git a/www/views/sellCoinbase.html b/www/views/sellCoinbase.html index d698f3009..2cef05523 100644 --- a/www/views/sellCoinbase.html +++ b/www/views/sellCoinbase.html @@ -15,8 +15,10 @@ Selling
    -
    {{sellRequestInfo.amount.amount}} {{sellRequestInfo.amount.currency}}
    -
    {{sellRequestInfo.subtotal.amount}} {{sellRequestInfo.subtotal.currency}}
    +
    {{amountUnitStr}}
    +
    + @ ${{sellPrice.amount}} per BTC +
    @@ -75,11 +77,17 @@
    - Total to receive + Transaction details +
    +
    + Amount + + {{sellRequestInfo.subtotal.amount}} {{sellRequestInfo.subtotal.currency}} +
    - {{fee.type}} + {{fee.type}} fee - @@ -87,8 +95,8 @@
    - Total - + Total to receive + {{sellRequestInfo.total.amount}} {{sellRequestInfo.total.currency}}
    From e9877234f68d22e08ceb49618fefc0f201ed9b38 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 16 Jan 2017 10:29:36 -0300 Subject: [PATCH 33/34] Updates native currency --- src/js/services/coinbaseService.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/services/coinbaseService.js b/src/js/services/coinbaseService.js index 213e2e3e7..307638c40 100644 --- a/src/js/services/coinbaseService.js +++ b/src/js/services/coinbaseService.js @@ -106,9 +106,8 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $ root.getAvailableCurrency = function() { var config = configService.getSync().wallet.settings; - // ONLY "USD" and "EUR" + // ONLY "USD" switch(config.alternativeIsoCode) { - case 'EUR' : return 'EUR'; default : return 'USD' }; }; From 55bd2355b9051fc0bea6331df160bf7b9ec8ce06 Mon Sep 17 00:00:00 2001 From: Gustavo Maximiliano Cortez Date: Mon, 16 Jan 2017 17:21:43 -0300 Subject: [PATCH 34/34] Adds btc value before confirm --- src/js/controllers/buyCoinbase.js | 1 + src/js/controllers/sellCoinbase.js | 1 + www/views/buyCoinbase.html | 1 + www/views/sellCoinbase.html | 1 + 4 files changed, 4 insertions(+) diff --git a/src/js/controllers/buyCoinbase.js b/src/js/controllers/buyCoinbase.js index 59bacde15..587ef778e 100644 --- a/src/js/controllers/buyCoinbase.js +++ b/src/js/controllers/buyCoinbase.js @@ -36,6 +36,7 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct $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); diff --git a/src/js/controllers/sellCoinbase.js b/src/js/controllers/sellCoinbase.js index af0e62992..d7b385e72 100644 --- a/src/js/controllers/sellCoinbase.js +++ b/src/js/controllers/sellCoinbase.js @@ -119,6 +119,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func $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); diff --git a/www/views/buyCoinbase.html b/www/views/buyCoinbase.html index 01f9a5c87..3521c825a 100644 --- a/www/views/buyCoinbase.html +++ b/www/views/buyCoinbase.html @@ -17,6 +17,7 @@
    {{amountUnitStr}}
    + {{buyRequestInfo.amount.amount}} {{buyRequestInfo.amount.currency}} @ ${{buyPrice.amount}} per BTC
    diff --git a/www/views/sellCoinbase.html b/www/views/sellCoinbase.html index 2cef05523..cc4befec3 100644 --- a/www/views/sellCoinbase.html +++ b/www/views/sellCoinbase.html @@ -17,6 +17,7 @@
    {{amountUnitStr}}
    + {{sellRequestInfo.amount.amount}} {{sellRequestInfo.amount.currency}} @ ${{sellPrice.amount}} per BTC