2016-08-16 18:38:18 -03:00
'use strict' ;
2018-06-25 11:07:13 +02:00
angular . module ( 'copayApp.controllers' ) . controller ( 'confirmController' , function ( $rootScope , $scope , $interval , $filter , $timeout , $ionicScrollDelegate , gettextCatalog , walletService , platformInfo , lodash , configService , $stateParams , $window , $state , $log , profileService , bitcore , bitcoreCash , txFormatService , ongoingProcess , $ionicModal , popupService , $ionicHistory , $ionicConfig , payproService , feeService , bwcError , txConfirmNotification , externalLinkService , firebaseEventsService , soundService ) {
2017-06-20 12:14:21 -03:00
2016-10-20 15:38:57 -03:00
var countDown = null ;
2017-06-22 11:38:13 -03:00
var FEE _TOO _HIGH _LIMIT _PER = 15 ;
2017-06-20 12:14:21 -03:00
var tx = { } ;
// Config Related values
var config = configService . getSync ( ) ;
var walletConfig = config . wallet ;
var unitToSatoshi = walletConfig . settings . unitToSatoshi ;
var unitDecimals = walletConfig . settings . unitDecimals ;
var satToUnit = 1 / unitToSatoshi ;
var configFeeLevel = walletConfig . settings . feeLevel ? walletConfig . settings . feeLevel : 'normal' ;
// Platform info
var isChromeApp = platformInfo . isChromeApp ;
var isCordova = platformInfo . isCordova ;
2017-06-22 15:12:42 -03:00
var isWindowsPhoneApp = platformInfo . isCordova && platformInfo . isWP ;
2017-06-20 12:14:21 -03:00
2017-07-10 14:19:38 -03:00
//custom fee flag
2018-03-01 14:19:52 +05:00
var usingCustomFee = false ;
var usingMerchantFee = false ;
2017-07-10 14:19:38 -03:00
2017-06-22 00:46:35 -03:00
function refresh ( ) {
$timeout ( function ( ) {
$scope . $apply ( ) ;
2017-09-01 14:32:49 -03:00
} , 10 ) ;
2017-06-22 00:46:35 -03:00
}
2017-06-20 12:14:21 -03:00
$scope . showWalletSelector = function ( ) {
$scope . walletSelector = true ;
2017-06-22 00:46:35 -03:00
refresh ( ) ;
2017-06-20 12:14:21 -03:00
} ;
2017-02-23 14:26:05 -05:00
$scope . $on ( "$ionicView.beforeLeave" , function ( event , data ) {
$ionicConfig . views . swipeBackEnabled ( true ) ;
} ) ;
2016-10-10 10:56:12 -04:00
2017-03-03 10:36:19 -03:00
$scope . $on ( "$ionicView.enter" , function ( event , data ) {
2017-02-23 14:26:05 -05:00
$ionicConfig . views . swipeBackEnabled ( false ) ;
2017-03-03 10:36:19 -03:00
} ) ;
2017-06-21 17:09:33 -03:00
function exitWithError ( err ) {
$log . info ( 'Error setting wallet selector:' + err ) ;
popupService . showAlert ( gettextCatalog . getString ( ) , bwcError . msg ( err ) , function ( ) {
$ionicHistory . nextViewOptions ( {
disableAnimate : true ,
historyRoot : true
} ) ;
$ionicHistory . clearHistory ( ) ;
$state . go ( 'tabs.send' ) ;
} ) ;
} ;
2017-09-15 10:22:51 -03:00
function setNoWallet ( msg , criticalError ) {
2017-06-21 17:09:33 -03:00
$scope . wallet = null ;
2017-07-11 12:28:47 -03:00
$scope . noWalletMessage = msg ;
2017-09-15 10:22:51 -03:00
$scope . criticalError = criticalError ;
2017-06-21 17:09:33 -03:00
$log . warn ( 'Not ready to make the payment:' + msg ) ;
$timeout ( function ( ) {
$scope . $apply ( ) ;
} ) ;
} ;
2017-03-03 10:36:19 -03:00
$scope . $on ( "$ionicView.beforeEnter" , function ( event , data ) {
2017-02-23 14:26:05 -05:00
2017-09-01 15:26:12 -03:00
function setWalletSelector ( coin , network , minAmount , cb ) {
2017-06-21 18:29:03 -03:00
// no min amount? (sendMax) => look for no empty wallets
2017-06-22 00:46:35 -03:00
minAmount = minAmount || 1 ;
2017-06-21 18:29:03 -03:00
2017-06-20 12:14:21 -03:00
$scope . wallets = profileService . getWallets ( {
onlyComplete : true ,
2017-08-24 17:02:49 -03:00
network : network ,
2017-09-01 15:26:12 -03:00
coin : coin
2017-06-20 12:14:21 -03:00
} ) ;
2018-01-02 15:38:40 +00:00
if ( tx . fromWalletId ) {
$scope . wallets = lodash . filter ( $scope . wallets , function ( w ) {
return w . id == tx . fromWalletId ;
} ) ;
}
2017-06-20 12:14:21 -03:00
if ( ! $scope . wallets || ! $scope . wallets . length ) {
2017-09-15 10:22:51 -03:00
setNoWallet ( gettextCatalog . getString ( 'No wallets available' ) , true ) ;
2017-06-21 17:09:33 -03:00
return cb ( ) ;
2017-06-20 12:14:21 -03:00
}
var filteredWallets = [ ] ;
var index = 0 ;
var walletsUpdated = 0 ;
lodash . each ( $scope . wallets , function ( w ) {
walletService . getStatus ( w , { } , function ( err , status ) {
if ( err || ! status ) {
$log . error ( err ) ;
} else {
walletsUpdated ++ ;
w . status = status ;
if ( ! status . availableBalanceSat )
$log . debug ( 'No balance available in: ' + w . name ) ;
if ( status . availableBalanceSat > minAmount ) {
filteredWallets . push ( w ) ;
}
}
if ( ++ index == $scope . wallets . length ) {
if ( ! walletsUpdated )
return cb ( 'Could not update any wallet' ) ;
if ( lodash . isEmpty ( filteredWallets ) ) {
2018-01-29 17:13:59 -04:00
setNoWallet ( gettextCatalog . getString ( 'Insufficient confirmed funds' ) , true ) ;
2017-06-20 12:14:21 -03:00
}
$scope . wallets = lodash . clone ( filteredWallets ) ;
return cb ( ) ;
}
} ) ;
} ) ;
} ;
// Setup $scope
2017-09-15 16:06:06 -03:00
2017-10-02 12:35:08 -03:00
var B = data . stateParams . coin == 'bch' ? bitcoreCash : bitcore ;
var networkName ;
try {
2018-01-23 13:58:31 +00:00
networkName = ( new B . Address ( data . stateParams . toAddress ) ) . network . name ;
2017-10-02 12:35:08 -03:00
} catch ( e ) {
2018-01-11 17:05:03 +09:00
var message = gettextCatalog . getString ( 'Invalid address' ) ;
2017-10-02 12:40:44 -03:00
var backText = gettextCatalog . getString ( 'Go back' ) ;
var learnText = gettextCatalog . getString ( 'Learn more' ) ;
popupService . showConfirm ( null , message , backText , learnText , function ( back ) {
2017-10-02 12:35:08 -03:00
$ionicHistory . nextViewOptions ( {
disableAnimate : true ,
historyRoot : true
} ) ;
$state . go ( 'tabs.send' ) . then ( function ( ) {
$ionicHistory . clearHistory ( ) ;
if ( ! back ) {
var url = 'https://support.bitpay.com/hc/en-us/articles/115004671663' ;
2017-11-13 16:49:59 +09:00
externalLinkService . open ( url ) ;
2017-10-02 12:35:08 -03:00
}
} ) ;
} ) ;
return ;
}
2017-06-20 12:14:21 -03:00
// Grab stateParams
tx = {
toAmount : parseInt ( data . stateParams . toAmount ) ,
sendMax : data . stateParams . useSendMax == 'true' ? true : false ,
2018-01-02 15:38:40 +00:00
fromWalletId : data . stateParams . fromWalletId ,
2017-10-02 12:35:08 -03:00
toAddress : data . stateParams . toAddress ,
2018-01-09 18:04:52 +09:00
displayAddress : data . stateParams . displayAddress ,
2017-06-20 12:14:21 -03:00
description : data . stateParams . description ,
paypro : data . stateParams . paypro ,
2017-09-01 15:26:12 -03:00
feeLevel : configFeeLevel ,
2017-06-20 12:14:21 -03:00
spendUnconfirmed : walletConfig . spendUnconfirmed ,
// Vanity tx info (not in the real tx)
recipientType : data . stateParams . recipientType || null ,
toName : data . stateParams . toName ,
toEmail : data . stateParams . toEmail ,
toColor : data . stateParams . toColor ,
2017-10-02 12:35:08 -03:00
network : networkName ,
2017-08-27 23:50:27 -03:00
coin : data . stateParams . coin ,
2017-06-21 13:03:48 -03:00
txp : { } ,
2017-06-20 12:14:21 -03:00
} ;
2018-03-01 14:19:52 +05:00
if ( data . stateParams . requiredFeeRate ) {
usingMerchantFee = true ;
tx . feeRate = parseInt ( data . stateParams . requiredFeeRate ) ;
}
if ( tx . coin && tx . coin == 'bch' ) {
tx . feeLevel = 'normal' ;
}
2017-09-01 15:26:12 -03:00
2017-06-20 12:14:21 -03:00
// Other Scope vars
$scope . isCordova = isCordova ;
2017-06-22 15:12:42 -03:00
$scope . isWindowsPhoneApp = isWindowsPhoneApp ;
2017-02-22 15:08:51 -05:00
$scope . showAddress = false ;
2016-11-25 17:05:35 -03:00
2017-08-24 17:02:49 -03:00
$scope . walletSelectorTitle = gettextCatalog . getString ( 'Send from' ) ;
2017-06-22 00:46:35 -03:00
2017-09-01 15:26:12 -03:00
setWalletSelector ( tx . coin , tx . network , tx . toAmount , function ( err ) {
2017-08-24 17:02:49 -03:00
if ( err ) {
return exitWithError ( 'Could not update wallets' ) ;
}
2017-06-20 12:14:21 -03:00
2017-08-24 17:02:49 -03:00
if ( $scope . wallets . length > 1 ) {
$scope . showWalletSelector ( ) ;
} else if ( $scope . wallets . length ) {
setWallet ( $scope . wallets [ 0 ] , tx ) ;
}
2017-05-19 10:59:36 -03:00
} ) ;
2017-08-24 17:02:49 -03:00
2018-05-24 15:54:47 -07:00
$scope . displayBalanceAsFiat = walletConfig . settings . priceDisplay === 'fiat' ;
2017-06-20 12:14:21 -03:00
} ) ;
2017-05-16 14:21:33 -03:00
2017-06-21 18:29:03 -03:00
function getSendMaxInfo ( tx , wallet , cb ) {
2017-06-20 12:14:21 -03:00
if ( ! tx . sendMax ) return cb ( ) ;
2017-01-16 16:25:12 -03:00
2017-06-20 12:14:21 -03:00
//ongoingProcess.set('retrievingInputs', true);
walletService . getSendMaxInfo ( wallet , {
feePerKb : tx . feeRate ,
excludeUnconfirmedUtxos : ! tx . spendUnconfirmed ,
returnInputs : true ,
} , cb ) ;
2017-01-16 16:25:12 -03:00
} ;
2016-09-20 15:28:31 -03:00
2017-06-20 12:14:21 -03:00
function getTxp ( tx , wallet , dryRun , cb ) {
// ToDo: use a credential's (or fc's) function for this
2017-06-22 11:49:20 -03:00
if ( tx . description && ! wallet . credentials . sharedEncryptingKey ) {
2017-06-20 12:14:21 -03:00
var msg = gettextCatalog . getString ( 'Could not add message to imported wallet without shared encrypting key' ) ;
$log . warn ( msg ) ;
return setSendError ( msg ) ;
}
if ( tx . toAmount > Number . MAX _SAFE _INTEGER ) {
var msg = gettextCatalog . getString ( 'Amount too big' ) ;
$log . warn ( msg ) ;
return setSendError ( msg ) ;
2016-11-29 14:28:43 -03:00
}
2016-10-10 13:57:25 -03:00
2017-06-20 12:14:21 -03:00
var txp = { } ;
2016-09-20 15:28:31 -03:00
2017-06-20 12:14:21 -03:00
txp . outputs = [ {
'toAddress' : tx . toAddress ,
'amount' : tx . toAmount ,
'message' : tx . description
} ] ;
if ( tx . sendMaxInfo ) {
txp . inputs = tx . sendMaxInfo . inputs ;
txp . fee = tx . sendMaxInfo . fee ;
} else {
2018-03-01 14:19:52 +05:00
if ( usingCustomFee || usingMerchantFee ) {
2017-07-07 10:26:31 -03:00
txp . feePerKb = tx . feeRate ;
} else txp . feeLevel = tx . feeLevel ;
2017-06-20 12:14:21 -03:00
}
2017-06-22 11:49:20 -03:00
txp . message = tx . description ;
2017-06-20 12:14:21 -03:00
if ( tx . paypro ) {
2018-01-23 13:58:31 +00:00
txp . payProUrl = tx . paypro . url ;
2017-06-20 12:14:21 -03:00
}
txp . excludeUnconfirmedUtxos = ! tx . spendUnconfirmed ;
txp . dryRun = dryRun ;
walletService . createTx ( wallet , txp , function ( err , ctxp ) {
if ( err ) {
setSendError ( err ) ;
return cb ( err ) ;
}
return cb ( null , ctxp ) ;
} ) ;
} ;
2017-06-21 13:03:48 -03:00
function updateTx ( tx , wallet , opts , cb ) {
2017-09-01 14:32:49 -03:00
ongoingProcess . set ( 'calculatingFee' , true ) ;
2017-06-20 12:14:21 -03:00
2017-06-21 13:03:48 -03:00
if ( opts . clearCache ) {
tx . txp = { } ;
}
2017-06-20 12:14:21 -03:00
2017-06-21 13:03:48 -03:00
$scope . tx = tx ;
2017-06-20 12:14:21 -03:00
2017-06-21 18:29:03 -03:00
function updateAmount ( ) {
if ( ! tx . toAmount ) return ;
2017-06-20 12:14:21 -03:00
2017-06-21 18:29:03 -03:00
// Amount
2017-08-28 15:51:13 -03:00
tx . amountStr = txFormatService . formatAmountStr ( wallet . coin , tx . toAmount ) ;
2017-06-21 18:29:03 -03:00
tx . amountValueStr = tx . amountStr . split ( ' ' ) [ 0 ] ;
tx . amountUnitStr = tx . amountStr . split ( ' ' ) [ 1 ] ;
2017-08-28 15:51:13 -03:00
txFormatService . formatAlternativeStr ( wallet . coin , tx . toAmount , function ( v ) {
2017-06-21 18:29:03 -03:00
tx . alternativeAmountStr = v ;
} ) ;
}
updateAmount ( ) ;
refresh ( ) ;
2017-06-20 12:14:21 -03:00
2017-06-23 08:52:45 -03:00
// End of quick refresh, before wallet is selected.
2017-09-01 14:32:49 -03:00
if ( ! wallet ) {
ongoingProcess . set ( 'calculatingFee' , false ) ;
return cb ( ) ;
}
2017-06-23 08:52:45 -03:00
2018-03-13 12:00:12 +09:00
var feeServiceLevel = usingMerchantFee && wallet . coin == 'btc' ? 'urgent' : tx . feeLevel ;
feeService . getFeeRate ( wallet . coin , tx . network , feeServiceLevel , function ( err , feeRate ) {
2017-09-01 14:32:49 -03:00
if ( err ) {
ongoingProcess . set ( 'calculatingFee' , false ) ;
return cb ( err ) ;
}
2017-06-20 12:14:21 -03:00
2018-03-01 14:19:52 +05:00
var msg ;
if ( usingCustomFee ) {
msg = gettextCatalog . getString ( 'Custom' ) ;
tx . feeLevelName = msg ;
} else if ( usingMerchantFee ) {
$log . info ( 'Using Merchant Fee:' + tx . feeRate + ' vs. Urgent level:' + feeRate ) ;
msg = gettextCatalog . getString ( 'Suggested by Merchant' ) ;
tx . feeLevelName = msg ;
} else {
tx . feeLevelName = feeService . feeOpts [ tx . feeLevel ] ;
tx . feeRate = feeRate ;
}
2017-06-20 12:14:21 -03:00
2017-06-21 18:29:03 -03:00
getSendMaxInfo ( lodash . clone ( tx ) , wallet , function ( err , sendMaxInfo ) {
2017-06-20 12:14:21 -03:00
if ( err ) {
2017-09-01 14:32:49 -03:00
ongoingProcess . set ( 'calculatingFee' , false ) ;
2017-06-20 12:14:21 -03:00
var msg = gettextCatalog . getString ( 'Error getting SendMax information' ) ;
return setSendError ( msg ) ;
}
if ( sendMaxInfo ) {
2017-06-21 18:29:03 -03:00
$log . debug ( 'Send max info' , sendMaxInfo ) ;
if ( tx . sendMax && sendMaxInfo . amount == 0 ) {
2017-09-01 14:32:49 -03:00
ongoingProcess . set ( 'calculatingFee' , false ) ;
2018-01-29 17:13:59 -04:00
setNoWallet ( gettextCatalog . getString ( 'Insufficient confirmed funds' ) ) ;
2017-06-20 12:14:21 -03:00
popupService . showAlert ( gettextCatalog . getString ( 'Error' ) , gettextCatalog . getString ( 'Not enough funds for fee' ) ) ;
return cb ( 'no_funds' ) ;
2016-10-10 13:57:25 -03:00
}
2017-06-20 12:14:21 -03:00
2017-06-21 18:29:03 -03:00
tx . sendMaxInfo = sendMaxInfo ;
2017-06-22 00:46:35 -03:00
tx . toAmount = tx . sendMaxInfo . amount ;
2017-06-21 18:29:03 -03:00
updateAmount ( ) ;
2017-09-01 14:32:49 -03:00
ongoingProcess . set ( 'calculatingFee' , false ) ;
2017-09-07 16:49:37 -03:00
$timeout ( function ( ) {
showSendMaxWarning ( wallet , sendMaxInfo ) ;
} , 200 ) ;
2016-09-20 15:28:31 -03:00
}
2017-06-21 13:03:48 -03:00
// txp already generated for this wallet?
2017-06-23 10:38:05 -03:00
if ( tx . txp [ wallet . id ] ) {
2017-09-01 14:32:49 -03:00
ongoingProcess . set ( 'calculatingFee' , false ) ;
2017-06-23 10:38:05 -03:00
refresh ( ) ;
2017-06-21 13:03:48 -03:00
return cb ( ) ;
2017-06-23 10:38:05 -03:00
}
2017-06-21 13:03:48 -03:00
getTxp ( lodash . clone ( tx ) , wallet , opts . dryRun , function ( err , txp ) {
2017-09-01 14:32:49 -03:00
ongoingProcess . set ( 'calculatingFee' , false ) ;
if ( err ) {
2018-03-01 14:19:52 +05:00
if ( err . message == 'Insufficient funds' ) {
setNoWallet ( gettextCatalog . getString ( 'Insufficient funds' ) ) ;
popupService . showAlert ( gettextCatalog . getString ( 'Error' ) , gettextCatalog . getString ( 'Not enough funds for fee' ) ) ;
return cb ( 'no_funds' ) ;
} else
return cb ( err ) ;
2017-09-01 14:32:49 -03:00
}
2017-01-16 16:58:42 -03:00
2017-08-28 15:51:13 -03:00
txp . feeStr = txFormatService . formatAmountStr ( wallet . coin , txp . fee ) ;
txFormatService . formatAlternativeStr ( wallet . coin , txp . fee , function ( v ) {
2017-06-21 13:03:48 -03:00
txp . alternativeFeeStr = v ;
2017-12-06 15:45:17 +09:00
if ( txp . alternativeFeeStr . substring ( 0 , 4 ) == '0.00' )
txp . alternativeFeeStr = '< ' + txp . alternativeFeeStr ;
2016-11-29 11:05:55 -03:00
} ) ;
2017-06-22 11:38:13 -03:00
var per = ( txp . fee / ( txp . amount + txp . fee ) * 100 ) ;
2017-12-06 15:45:17 +09:00
var perString = per . toFixed ( 2 ) ;
txp . feeRatePerStr = ( perString == '0.00' ? '< ' : '' ) + perString + '%' ;
2017-07-07 10:26:31 -03:00
txp . feeToHigh = per > FEE _TOO _HIGH _LIMIT _PER ;
2017-06-20 12:14:21 -03:00
tx . txp [ wallet . id ] = txp ;
2017-06-21 13:03:48 -03:00
$log . debug ( 'Confirm. TX Fully Updated for wallet:' + wallet . id , tx ) ;
2017-06-23 10:38:05 -03:00
refresh ( ) ;
2017-06-20 12:14:21 -03:00
return cb ( ) ;
} ) ;
2016-09-20 15:28:31 -03:00
} ) ;
} ) ;
2017-06-20 12:14:21 -03:00
}
2016-11-25 17:05:35 -03:00
2017-06-20 12:14:21 -03:00
function useSelectedWallet ( ) {
2017-02-22 15:08:51 -05:00
2017-06-20 12:14:21 -03:00
if ( ! $scope . useSendMax ) {
showAmount ( tx . toAmount ) ;
}
2016-10-26 14:00:43 -04:00
2017-06-20 12:14:21 -03:00
$scope . onWalletSelect ( $scope . wallet ) ;
}
2016-09-20 15:28:31 -03:00
2017-06-20 12:14:21 -03:00
function setButtonText ( isMultisig , isPayPro ) {
2016-11-29 14:28:43 -03:00
2017-06-20 12:14:21 -03:00
if ( isPayPro ) {
2017-09-04 15:48:11 -03:00
if ( isCordova && ! isWindowsPhoneApp ) {
$scope . buttonText = gettextCatalog . getString ( 'Slide to pay' ) ;
} else {
$scope . buttonText = gettextCatalog . getString ( 'Click to pay' ) ;
}
2017-06-20 12:14:21 -03:00
} else if ( isMultisig ) {
2017-09-04 15:48:11 -03:00
if ( isCordova && ! isWindowsPhoneApp ) {
$scope . buttonText = gettextCatalog . getString ( 'Slide to accept' ) ;
} else {
$scope . buttonText = gettextCatalog . getString ( 'Click to accept' ) ;
}
2017-09-07 16:49:37 -03:00
} else {
2017-09-07 17:16:14 -03:00
if ( isCordova && ! isWindowsPhoneApp ) {
$scope . buttonText = gettextCatalog . getString ( 'Slide to send' ) ;
} else {
$scope . buttonText = gettextCatalog . getString ( 'Click to send' ) ;
}
2017-09-07 16:49:37 -03:00
}
2016-11-29 11:05:55 -03:00
} ;
2016-11-25 15:24:02 -03:00
2017-06-20 12:14:21 -03:00
$scope . toggleAddress = function ( ) {
$scope . showAddress = ! $scope . showAddress ;
} ;
2016-11-23 11:23:19 -03:00
2017-08-24 17:02:49 -03:00
function showSendMaxWarning ( wallet , sendMaxInfo ) {
2016-11-23 11:23:19 -03:00
2017-06-20 12:14:21 -03:00
function verifyExcludedUtxos ( ) {
var warningMsg = [ ] ;
if ( sendMaxInfo . utxosBelowFee > 0 ) {
warningMsg . push ( gettextCatalog . getString ( "A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided." , {
2017-08-28 15:51:13 -03:00
amountBelowFeeStr : txFormatService . formatAmountStr ( wallet . coin , sendMaxInfo . amountBelowFee )
2017-06-20 12:14:21 -03:00
} ) ) ;
}
2016-11-23 11:23:19 -03:00
2017-06-20 12:14:21 -03:00
if ( sendMaxInfo . utxosAboveMaxSize > 0 ) {
warningMsg . push ( gettextCatalog . getString ( "A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded." , {
2017-08-28 15:51:13 -03:00
amountAboveMaxSizeStr : txFormatService . formatAmountStr ( wallet . coin , sendMaxInfo . amountAboveMaxSize )
2017-06-20 12:14:21 -03:00
} ) ) ;
}
return warningMsg . join ( '\n' ) ;
} ;
2016-11-23 11:23:19 -03:00
2017-06-20 12:14:21 -03:00
var msg = gettextCatalog . getString ( "{{fee}} will be deducted for bitcoin networking fees." , {
2017-08-28 15:51:13 -03:00
fee : txFormatService . formatAmountStr ( wallet . coin , sendMaxInfo . fee )
2016-11-23 11:23:19 -03:00
} ) ;
2017-06-20 12:14:21 -03:00
var warningMsg = verifyExcludedUtxos ( ) ;
if ( ! lodash . isEmpty ( warningMsg ) )
msg += '\n' + warningMsg ;
popupService . showAlert ( null , msg , function ( ) { } ) ;
2016-11-23 11:23:19 -03:00
} ;
2016-10-12 20:13:28 -04:00
$scope . onWalletSelect = function ( wallet ) {
2017-06-20 12:14:21 -03:00
setWallet ( wallet , tx ) ;
2016-10-12 18:49:00 -04:00
} ;
2017-06-21 13:03:48 -03:00
$scope . showDescriptionPopup = function ( tx ) {
2016-09-23 12:07:56 -03:00
var message = gettextCatalog . getString ( 'Add description' ) ;
2016-09-16 21:01:19 -03:00
var opts = {
2017-06-21 13:03:48 -03:00
defaultText : tx . description
2016-09-07 16:48:16 -03:00
} ;
2016-09-23 12:07:56 -03:00
popupService . showPrompt ( null , message , opts , function ( res ) {
2017-06-21 13:03:48 -03:00
if ( typeof res != 'undefined' ) tx . description = res ;
2016-09-23 12:07:56 -03:00
$timeout ( function ( ) {
$scope . $apply ( ) ;
2016-11-29 11:05:55 -03:00
} ) ;
2016-08-24 15:47:36 -03:00
} ) ;
} ;
2016-10-20 15:38:57 -03:00
function _paymentTimeControl ( expirationTime ) {
2017-06-22 00:46:35 -03:00
$scope . paymentExpired = false ;
2016-10-20 15:38:57 -03:00
setExpirationTime ( ) ;
countDown = $interval ( function ( ) {
setExpirationTime ( ) ;
} , 1000 ) ;
function setExpirationTime ( ) {
var now = Math . floor ( Date . now ( ) / 1000 ) ;
if ( now > expirationTime ) {
setExpiredValues ( ) ;
return ;
}
var totalSecs = expirationTime - now ;
var m = Math . floor ( totalSecs / 60 ) ;
var s = totalSecs % 60 ;
2017-06-22 00:46:35 -03:00
$scope . remainingTimeStr = ( '0' + m ) . slice ( - 2 ) + ":" + ( '0' + s ) . slice ( - 2 ) ;
2016-11-23 11:23:19 -03:00
} ;
2016-10-20 15:38:57 -03:00
function setExpiredValues ( ) {
2017-06-22 00:46:35 -03:00
$scope . paymentExpired = true ;
$scope . remainingTimeStr = gettextCatalog . getString ( 'Expired' ) ;
2016-10-20 15:38:57 -03:00
if ( countDown ) $interval . cancel ( countDown ) ;
$timeout ( function ( ) {
$scope . $apply ( ) ;
} ) ;
2016-11-23 11:23:19 -03:00
} ;
} ;
2016-10-20 15:38:57 -03:00
2017-06-20 12:14:21 -03:00
/* sets a wallet on the UI, creates a TXPs for that wallet */
function setWallet ( wallet , tx ) {
2016-08-23 16:15:10 -03:00
$scope . wallet = wallet ;
2016-08-18 14:51:35 -03:00
2017-09-01 14:32:49 -03:00
// If select another wallet
tx . coin = wallet . coin ;
2018-03-01 14:19:52 +05:00
if ( usingCustomFee ) {
} else {
tx . feeLevel = wallet . coin == 'bch' ? 'normal' : configFeeLevel ;
}
2017-09-01 14:32:49 -03:00
2017-06-20 12:14:21 -03:00
setButtonText ( wallet . credentials . m > 1 , ! ! tx . paypro ) ;
2017-06-22 00:46:35 -03:00
if ( tx . paypro )
_paymentTimeControl ( tx . paypro . expires ) ;
2017-06-20 12:14:21 -03:00
2017-06-21 13:03:48 -03:00
updateTx ( tx , wallet , {
dryRun : true
} , function ( err ) {
2017-06-20 12:14:21 -03:00
$timeout ( function ( ) {
$ionicScrollDelegate . resize ( ) ;
$scope . $apply ( ) ;
} , 10 ) ;
} ) ;
2016-11-24 16:26:15 -03:00
2016-11-23 11:23:19 -03:00
} ;
2016-08-16 18:38:18 -03:00
2016-08-17 15:36:19 -03:00
var setSendError = function ( msg ) {
2016-10-10 15:27:57 -04:00
$scope . sendStatus = '' ;
2016-10-17 14:46:51 -03:00
$timeout ( function ( ) {
$scope . $apply ( ) ;
} ) ;
2016-12-13 14:21:57 -03:00
popupService . showAlert ( gettextCatalog . getString ( 'Error at confirm' ) , bwcError . msg ( msg ) ) ;
2016-08-17 13:16:06 -03:00
} ;
2016-08-16 18:38:18 -03:00
2016-08-24 19:12:11 -03:00
$scope . openPPModal = function ( ) {
$ionicModal . fromTemplateUrl ( 'views/modals/paypro.html' , {
scope : $scope
} ) . then ( function ( modal ) {
$scope . payproModal = modal ;
$scope . payproModal . show ( ) ;
} ) ;
} ;
2016-12-27 16:16:55 -03:00
$scope . cancel = function ( ) {
$scope . payproModal . hide ( ) ;
} ;
2017-06-21 13:03:48 -03:00
$scope . approve = function ( tx , wallet , onSendStatusChange ) {
2016-12-05 11:17:48 -03:00
2017-06-21 13:03:48 -03:00
if ( ! tx || ! wallet ) return ;
2016-12-05 11:17:48 -03:00
2017-06-22 00:46:35 -03:00
if ( $scope . paymentExpired ) {
2016-10-27 16:15:11 -04:00
popupService . showAlert ( null , gettextCatalog . getString ( 'This bitcoin payment request has expired.' ) ) ;
2016-10-20 16:44:20 -03:00
$scope . sendStatus = '' ;
$timeout ( function ( ) {
$scope . $apply ( ) ;
} ) ;
2016-10-20 15:38:57 -03:00
return ;
}
2016-10-07 20:03:51 -04:00
ongoingProcess . set ( 'creatingTx' , true , onSendStatusChange ) ;
2017-06-21 13:03:48 -03:00
getTxp ( lodash . clone ( tx ) , wallet , false , function ( err , txp ) {
2016-10-07 20:03:51 -04:00
ongoingProcess . set ( 'creatingTx' , false , onSendStatusChange ) ;
2016-12-16 14:54:16 -03:00
if ( err ) return ;
2016-09-21 11:47:19 -03:00
2017-06-21 13:03:48 -03:00
// confirm txs for more that 20usd, if not spending/touchid is enabled
function confirmTx ( cb ) {
if ( walletService . isEncrypted ( wallet ) )
return cb ( ) ;
2017-08-28 15:51:13 -03:00
var amountUsd = parseFloat ( txFormatService . formatToUSD ( wallet . coin , txp . amount ) ) ;
2018-03-01 18:44:22 +05:00
return cb ( ) ;
2017-06-21 13:03:48 -03:00
} ;
function publishAndSign ( ) {
if ( ! wallet . canSign ( ) && ! wallet . isPrivKeyExternal ( ) ) {
$log . info ( 'No signing proposal: No private key' ) ;
return walletService . onlyPublish ( wallet , txp , function ( err ) {
if ( err ) setSendError ( err ) ;
} , onSendStatusChange ) ;
}
walletService . publishAndSign ( wallet , txp , function ( err , txp ) {
if ( err ) return setSendError ( err ) ;
2017-07-14 15:45:18 -03:00
if ( config . confirmedTxsNotifications && config . confirmedTxsNotifications . enabled ) {
2017-07-13 11:12:15 -03:00
txConfirmNotification . subscribe ( wallet , {
2017-07-14 10:18:36 -03:00
txid : txp . txid
2017-07-13 11:12:15 -03:00
} ) ;
}
2017-06-21 13:03:48 -03:00
} , onSendStatusChange ) ;
} ;
confirmTx ( function ( nok ) {
if ( nok ) {
$scope . sendStatus = '' ;
$timeout ( function ( ) {
$scope . $apply ( ) ;
2016-09-21 11:47:19 -03:00
} ) ;
2017-06-21 13:03:48 -03:00
return ;
2016-09-21 11:47:19 -03:00
}
2017-06-21 13:03:48 -03:00
publishAndSign ( ) ;
} ) ;
2016-09-21 11:47:19 -03:00
} ) ;
} ;
2016-10-07 20:12:37 -04:00
function statusChangeHandler ( processName , showName , isOn ) {
2016-10-11 10:19:05 -03:00
$log . debug ( 'statusChangeHandler: ' , processName , showName , isOn ) ;
2016-12-14 15:26:56 -03:00
if (
(
2017-02-20 10:25:52 -05:00
processName === 'broadcastingTx' ||
( ( processName === 'signingTx' ) && $scope . wallet . m > 1 ) ||
2017-01-13 01:21:32 -03:00
( processName == 'sendingTx' && ! $scope . wallet . canSign ( ) && ! $scope . wallet . isPrivKeyExternal ( ) )
2016-12-14 15:26:56 -03:00
) && ! isOn ) {
2016-10-07 20:03:51 -04:00
$scope . sendStatus = 'success' ;
2018-06-29 15:11:34 +02:00
if ( $state . current . name === "tabs.send.confirm" ) // XX SP: Otherwise all open wallets on other devices play this sound if you have been in a send flow before on that device.
soundService . play ( 'misc/payment_sent.mp3' ) ;
2017-11-16 16:35:45 +09:00
firebaseEventsService . logEvent ( 'sent_bitcoin' , { coin : $scope . wallet . coin } ) ;
2016-12-19 11:50:49 -03:00
$timeout ( function ( ) {
$scope . $digest ( ) ;
2017-01-13 01:21:32 -03:00
} , 100 ) ;
2016-10-11 10:19:05 -03:00
} else if ( showName ) {
2016-10-07 20:03:51 -04:00
$scope . sendStatus = showName ;
}
2016-11-23 11:23:19 -03:00
} ;
2016-10-07 20:03:51 -04:00
2016-10-10 15:27:57 -04:00
$scope . statusChangeHandler = statusChangeHandler ;
2016-10-07 20:03:51 -04:00
$scope . onSuccessConfirm = function ( ) {
2016-10-10 15:27:57 -04:00
$scope . sendStatus = '' ;
2017-02-17 13:45:17 -03:00
$ionicHistory . nextViewOptions ( {
disableAnimate : true ,
historyRoot : true
} ) ;
$state . go ( 'tabs.send' ) . then ( function ( ) {
2017-06-22 11:49:20 -03:00
$ionicHistory . clearHistory ( ) ;
2017-02-17 13:45:17 -03:00
$state . transitionTo ( 'tabs.home' ) ;
} ) ;
2016-10-07 20:03:51 -04:00
} ;
2017-06-21 13:03:48 -03:00
$scope . chooseFeeLevel = function ( tx , wallet ) {
2016-12-14 15:26:56 -03:00
2017-08-28 18:01:07 -03:00
if ( wallet . coin == 'bch' ) return ;
2018-03-01 14:19:52 +05:00
if ( usingMerchantFee ) return ;
2017-08-28 18:01:07 -03:00
2017-06-21 17:09:33 -03:00
var scope = $rootScope . $new ( true ) ;
scope . network = tx . network ;
scope . feeLevel = tx . feeLevel ;
scope . noSave = true ;
2017-08-29 15:47:39 -03:00
scope . coin = wallet . coin ;
2017-07-10 14:19:38 -03:00
if ( usingCustomFee ) {
scope . customFeePerKB = tx . feeRate ;
2017-07-18 11:45:30 -03:00
scope . feePerSatByte = tx . feeRate / 1000 ;
2017-07-10 14:19:38 -03:00
}
2017-06-21 17:09:33 -03:00
2017-05-19 10:59:36 -03:00
$ionicModal . fromTemplateUrl ( 'views/modals/chooseFeeLevel.html' , {
2017-06-21 17:09:33 -03:00
scope : scope ,
2017-07-07 10:26:31 -03:00
backdropClickToClose : false ,
hardwareBackButtonClose : false
2017-05-19 10:59:36 -03:00
} ) . then ( function ( modal ) {
2017-06-21 17:09:33 -03:00
scope . chooseFeeLevelModal = modal ;
scope . openModal ( ) ;
2017-05-19 10:59:36 -03:00
} ) ;
2017-06-21 17:09:33 -03:00
scope . openModal = function ( ) {
scope . chooseFeeLevelModal . show ( ) ;
2017-05-19 10:59:36 -03:00
} ;
2017-06-21 17:09:33 -03:00
2017-07-10 14:19:38 -03:00
scope . hideModal = function ( newFeeLevel , customFeePerKB ) {
2017-06-23 10:24:14 -03:00
scope . chooseFeeLevelModal . hide ( ) ;
2017-07-07 10:26:31 -03:00
$log . debug ( 'New fee level choosen:' + newFeeLevel + ' was:' + tx . feeLevel ) ;
2017-07-10 14:19:38 -03:00
usingCustomFee = newFeeLevel == 'custom' ? true : false ;
if ( tx . feeLevel == newFeeLevel && ! usingCustomFee ) return ;
2017-07-07 10:26:31 -03:00
tx . feeLevel = newFeeLevel ;
2017-07-10 14:19:38 -03:00
if ( usingCustomFee ) tx . feeRate = parseInt ( customFeePerKB ) ;
2017-06-21 13:03:48 -03:00
updateTx ( tx , wallet , {
clearCache : true ,
2017-07-07 10:26:31 -03:00
dryRun : true
} , function ( ) { } ) ;
2017-05-19 10:59:36 -03:00
} ;
2017-05-16 14:21:33 -03:00
} ;
2016-08-16 18:38:18 -03:00
} ) ;