2016-08-16 18:38:18 -03:00
'use strict' ;
2018-08-08 11:23:35 +09:00
angular . module ( 'copayApp.controllers' ) . controller ( 'confirmController' , function ( $rootScope , $scope , $interval , $filter , $timeout , $ionicScrollDelegate , $ionicLoading , ionicToast , addressbookService , gettextCatalog , walletService , platformInfo , lodash , configService , $stateParams , $window , $state , $log , profileService , bitcore , bitcoreCash , txFormatService , ongoingProcess , $ionicModal , popupService , $ionicHistory , $ionicConfig , payproService , feeService , bitcoinCashJsService , bwcError , txConfirmNotification , externalLinkService , firebaseEventsService , soundService , clipboardService ) {
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 = { } ;
2018-07-16 17:21:15 +02:00
var lastTxId = "" ;
2017-06-20 12:14:21 -03:00
// Config Related values
var config = configService . getSync ( ) ;
var walletConfig = config . wallet ;
var configFeeLevel = walletConfig . settings . feeLevel ? walletConfig . settings . feeLevel : 'normal' ;
// Platform info
var isCordova = platformInfo . isCordova ;
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
}
2018-07-16 17:21:15 +02:00
$scope . shareTransaction = function ( ) {
var explorerTxUrl = 'https://explorer.bitcoin.com/' + tx . coin + '/tx/' + lastTxId ;
if ( platformInfo . isCordova ) {
var text = 'Take a look at this Bitcoin transaction here: ' + explorerTxUrl ;
window . plugins . socialsharing . share ( text , null , null , null ) ;
} else {
ionicToast . show ( gettextCatalog . getString ( 'Copied to clipboard' ) , 'bottom' , false , 3000 ) ;
clipboardService . copyToClipboard ( explorerTxUrl ) ;
}
} ;
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 ( ) ;
} ) ;
} ;
2018-07-24 14:24:22 +02:00
var setWalletSelector = function ( coin , network , minAmount , cb ) {
2017-02-23 14:26:05 -05:00
2018-07-24 14:24:22 +02:00
// no min amount? (sendMax) => look for no empty wallets
minAmount = minAmount || 1 ;
2017-06-21 18:29:03 -03:00
2018-07-24 14:24:22 +02:00
$scope . wallets = profileService . getWallets ( {
onlyComplete : true ,
network : network ,
coin : coin
} ) ;
2017-06-21 18:29:03 -03:00
2018-07-24 14:24:22 +02: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
} ) ;
2018-07-24 14:24:22 +02:00
}
2018-01-02 15:38:40 +00:00
2018-07-24 14:24:22 +02:00
if ( ! $scope . wallets || ! $scope . wallets . length ) {
setNoWallet ( gettextCatalog . getString ( 'No wallets available' ) , true ) ;
return cb ( ) ;
}
2017-06-20 12:14:21 -03:00
2018-07-24 14:24:22 +02:00
var filteredWallets = [ ] ;
var index = 0 ;
var walletsUpdated = 0 ;
2017-06-20 12:14:21 -03:00
2018-07-24 14:24:22 +02:00
lodash . each ( $scope . wallets , function ( w ) {
walletService . getStatus ( w , { } , function ( err , status ) {
if ( err || ! status ) {
$log . error ( err ) ;
} else {
walletsUpdated ++ ;
w . status = status ;
2017-06-20 12:14:21 -03:00
2018-07-24 14:24:22 +02:00
if ( ! status . availableBalanceSat )
$log . debug ( 'No balance available in: ' + w . name ) ;
2017-06-20 12:14:21 -03:00
2018-07-24 14:24:22 +02:00
if ( status . availableBalanceSat > minAmount ) {
filteredWallets . push ( w ) ;
2017-06-20 12:14:21 -03:00
}
2018-07-24 14:24:22 +02:00
}
2017-06-20 12:14:21 -03:00
2018-07-24 14:24:22 +02:00
if ( ++ index == $scope . wallets . length ) {
if ( ! walletsUpdated )
return cb ( 'Could not update any wallet' ) ;
2017-06-20 12:14:21 -03:00
2018-07-24 14:24:22 +02:00
if ( lodash . isEmpty ( filteredWallets ) ) {
setNoWallet ( gettextCatalog . getString ( 'Insufficient confirmed funds' ) , true ) ;
2017-06-20 12:14:21 -03:00
}
2018-07-24 14:24:22 +02:00
$scope . wallets = lodash . clone ( filteredWallets ) ;
return cb ( ) ;
}
2017-06-20 12:14:21 -03:00
} ) ;
2018-07-24 14:24:22 +02:00
} ) ;
} ;
2018-07-25 17:25:38 +02:00
$scope . getContacts = function ( addr ) {
addressbookService . list ( function ( err , ab ) {
if ( err ) $log . error ( err ) ;
$scope . hasContacts = lodash . isEmpty ( ab ) ? false : true ;
if ( ! $scope . hasContacts ) return cb ( ) ;
var completeContacts = [ ] ;
lodash . each ( ab , function ( v , k ) {
completeContacts . push ( {
name : lodash . isObject ( v ) ? v . name : v ,
address : k ,
email : lodash . isObject ( v ) ? v . email : null ,
recipientType : 'contact' ,
coin : v . coin ,
displayCoin : ( v . coin == 'bch'
? ( config . bitcoinCashAlias || defaults . bitcoinCashAlias )
: ( config . bitcoinAlias || defaults . bitcoinAlias ) ) . toUpperCase ( )
} ) ;
} ) ;
return cb ( ) ;
} ) ;
2018-08-02 18:30:35 +02:00
} ;
2018-07-25 17:25:38 +02:00
2018-07-24 14:24:22 +02:00
$scope . $on ( "$ionicView.beforeEnter" , function ( event , data ) {
2018-07-25 15:07:15 +02:00
$scope . fromWallet = profileService . getWallet ( data . stateParams . fromWalletId ) ; // Wallet to send from
2017-09-15 16:06:06 -03:00
2018-07-25 17:25:38 +02:00
2017-06-20 12:14:21 -03:00
// Grab stateParams
tx = {
2018-07-24 14:24:22 +02:00
amount : parseInt ( data . stateParams . amount ) ,
2017-06-20 12:14:21 -03:00
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 ,
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)
2018-07-24 14:24:22 +02:00
recipientType : $scope . recipientType || null ,
2018-07-25 15:07:15 +02:00
toName : null ,
toEmail : null ,
toColor : null ,
network : false ,
2018-07-24 14:24:22 +02:00
coin : $scope . fromWallet . 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 ) ;
}
2018-07-25 15:07:15 +02:00
if ( tx . coin && tx . coin === 'bch' ) {
2018-03-01 14:19:52 +05:00
tx . feeLevel = 'normal' ;
}
2017-09-01 15:26:12 -03:00
2018-07-25 15:07:15 +02:00
var B = data . stateParams . coin === 'bch' ? bitcoreCash : bitcore ;
var networkName ;
$scope . recipientType = null ;
try {
if ( data . stateParams . toWalletId ) { // There is a toWalletId, so we presume this is a wallet-to-wallet transfer
$scope . recipientType = 'wallet' ; // set transaction type to wallet-to-wallet
$ionicLoading . show ( ) ;
var toWallet = profileService . getWallet ( data . stateParams . toWalletId ) ;
tx . toColor = toWallet . color ;
tx . toName = toWallet . name ;
// We need an address to send to, so we ask the walletService to create a new address for the toWallet.
walletService . getAddress ( toWallet , true , function ( err , addr ) {
$ionicLoading . hide ( ) ;
tx . toAddress = addr ;
networkName = ( new B . Address ( tx . toAddress ) ) . network . name ;
tx . network = networkName ;
setupTx ( tx ) ;
} ) ;
} else { // This is a Wallet-to-address transfer
networkName = ( new B . Address ( tx . toAddress ) ) . network . name ;
tx . network = networkName ;
setupTx ( tx ) ;
}
} catch ( e ) {
var message = gettextCatalog . getString ( 'Invalid address' ) ;
popupService . showAlert ( null , message , function ( ) {
$ionicHistory . nextViewOptions ( {
disableAnimate : true ,
historyRoot : true
} ) ;
$state . go ( 'tabs.send' ) . then ( function ( ) {
$ionicHistory . clearHistory ( ) ;
} ) ;
} ) ;
return ;
}
} ) ;
2018-07-25 16:05:07 +02:00
var setupTx = function ( tx ) {
if ( tx . coin === 'bch' ) {
tx . displayAddress = bitcoinCashJsService . readAddress ( tx . toAddress ) . cashaddr ;
} else {
tx . displayAddress = entry . address ;
}
2018-07-25 15:07:15 +02:00
2018-07-25 17:25:38 +02:00
addressbookService . get ( tx . coin + tx . toAddress , function ( err , addr ) { // Check if the recipient is a contact
if ( ! err && addr ) {
tx . toName = addr . name ;
tx . toEmail = addr . email ;
tx . recipientType = 'contact' ;
}
} ) ;
2017-06-20 12:14:21 -03:00
// Other Scope vars
$scope . isCordova = isCordova ;
2017-02-22 15:08:51 -05:00
$scope . showAddress = false ;
2017-08-24 17:02:49 -03:00
$scope . walletSelectorTitle = gettextCatalog . getString ( 'Send from' ) ;
2017-06-22 00:46:35 -03:00
2018-07-24 14:24:22 +02:00
setWalletSelector ( tx . coin , tx . network , tx . amount , 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' ;
2018-07-24 14:24:22 +02: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 ) ;
}
2018-07-24 14:24:22 +02:00
if ( tx . amount > Number . MAX _SAFE _INTEGER ) {
2017-06-20 12:14:21 -03:00
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 ,
2018-07-24 14:24:22 +02:00
'amount' : tx . amount ,
2017-06-20 12:14:21 -03:00
'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 ( ) {
2018-07-24 14:24:22 +02:00
if ( ! tx . amount ) return ;
2017-06-20 12:14:21 -03:00
2017-06-21 18:29:03 -03:00
// Amount
2018-07-24 14:24:22 +02:00
tx . amountStr = txFormatService . formatAmountStr ( wallet . coin , tx . amount ) ;
2017-06-21 18:29:03 -03:00
tx . amountValueStr = tx . amountStr . split ( ' ' ) [ 0 ] ;
tx . amountUnitStr = tx . amountStr . split ( ' ' ) [ 1 ] ;
2018-07-24 14:24:22 +02:00
txFormatService . formatAlternativeStr ( wallet . coin , tx . amount , function ( v ) {
2018-07-02 11:20:34 +12:00
var parts = v . split ( ' ' ) ;
2017-06-21 18:29:03 -03:00
tx . alternativeAmountStr = v ;
2018-07-02 11:20:34 +12:00
tx . alternativeAmountValueStr = parts [ 0 ] ;
tx . alternativeAmountUnitStr = ( parts . length > 0 ) ? parts [ 1 ] : '' ;
2017-06-21 18:29:03 -03:00
} ) ;
}
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 ;
2018-07-24 14:24:22 +02:00
tx . amount = 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 ) {
2018-07-24 14:24:22 +02:00
showAmount ( tx . amount ) ;
2017-06-20 12:14:21 -03:00
}
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 ) {
2018-07-25 15:07:15 +02:00
if ( isCordova ) {
2017-09-04 15:48:11 -03:00
$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 ) {
2018-07-25 15:07:15 +02:00
if ( isCordova ) {
2017-09-04 15:48:11 -03:00
$scope . buttonText = gettextCatalog . getString ( 'Slide to accept' ) ;
} else {
$scope . buttonText = gettextCatalog . getString ( 'Click to accept' ) ;
}
2017-09-07 16:49:37 -03:00
} else {
2018-07-25 15:07:15 +02:00
if ( isCordova ) {
2017-09-07 17:16:14 -03:00
$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
} ;
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 ) {
2018-07-02 11:20:34 +12:00
var feeAlternative = '' ,
msg = '' ;
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
2018-06-14 22:16:25 +12:00
feeAlternative = txFormatService . formatAlternativeStr ( wallet . coin , sendMaxInfo . fee ) ;
if ( feeAlternative ) {
msg = gettextCatalog . getString ( "{{feeAlternative}} will be deducted for bitcoin networking fees ({{fee}})." , {
fee : txFormatService . formatAmountStr ( wallet . coin , sendMaxInfo . fee ) ,
feeAlternative : feeAlternative
} ) ;
} else {
2018-06-15 10:24:00 +12:00
msg = gettextCatalog . getString ( "{{fee}} will be deducted for bitcoin networking fees)." , {
2018-06-14 22:16:25 +12:00
fee : txFormatService . formatAmountStr ( wallet . coin , sendMaxInfo . fee )
} ) ;
}
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
} ) ;
2018-07-16 17:21:15 +02:00
lastTxId = 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
2018-07-03 12:15:42 +09: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.
2018-06-29 15:11:34 +02:00
soundService . play ( 'misc/payment_sent.mp3' ) ;
2018-07-03 12:15:42 +09:00
}
2018-07-25 16:17:42 +09:00
var channel = "ga" ;
if ( platformInfo . isCordova ) {
channel = "firebase" ;
2018-07-13 17:19:31 +09:00
}
var log = new window . BitAnalytics . LogEvent ( "transfer_success" , [ {
"coin" : $scope . wallet . coin ,
"type" : "outgoing" ,
"amount" : $scope . amount ,
"fees" : $scope . fee
} ] , [ channel , "adjust" ] ) ;
window . BitAnalytics . LogEventHandlers . postEvent ( log ) ;
// Should be removed
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
} ) ;