@ -5,6 +5,8 @@
* /
var walletPubKeyRegex = /^(bc1|[13]|D)[a-zA-HJ-NP-Z0-9]{25,}$/ ;
var paymentRequestRegex = /^(bitcoin|dogecoin):(bc1|[13]|D)[a-zA-HJ-NP-Z0-9]{25,}$/ ;
var walletPrivateKeyRegex = /^[0-9A-Za-z]+$/ ;
function scanWalletQrCode ( callback ) {
scanBarcode ( function ( result ) {
@ -19,6 +21,182 @@ function scanWalletQrCode(callback) {
} ) ;
}
function scanPrivateKeyQrCode ( callback ) {
scanBarcode ( function ( result ) {
if ( walletPrivateKeyRegex . test ( result ) ) {
callback ( result ) ;
} else {
app . dialog . alert ( "That doesn't look like a valid wallet address." , "Error" ) ;
return ;
}
} , function ( ) {
app . dialog . alert ( "Something went wrong and we can't scan right now." , "Error" ) ;
} ) ;
}
/ * *
* Create and sign a crypto transaction .
*
* @ param { type } bitcoreLib Bitcore , Litecore , Dogecore , etc .
* @ param { type } privateKeyString Private key from wallet QR code
* @ param { type } sourceAddress Sender ' s wallet address
* @ param { type } destinationAddress Recipient ' s wallet address
* @ param { type } txHash From UXTO ( unspent output )
* @ param { type } txOutputIndex From UXTO ( unspent output )
* @ param { type } script From UXTO ( unspent output )
* @ param { type } inputSatoshis From UXTO ( unspent output )
* @ param { type } outputSatoshis Amount to send to recipient ' s wallet
* @ returns { string } Hex of serialized transaction , suitable for broadcast via Bitcoin Core or an API .
* /
function createSignedTransaction ( bitcoreLib , privateKeyString , sourceAddress , destinationAddress , txHash , txOutputIndex , script , inputSatoshis , outputSatoshis ) {
var privateKey = new bitcoreLib . PrivateKey ( privateKeyString ) ;
var utxo = {
"txId" : txHash ,
"outputIndex" : txOutputIndex ,
"address" : sourceAddress ,
"script" : script ,
"satoshis" : inputSatoshis
} ;
var transaction = new bitcoreLib . Transaction ( )
. from ( utxo )
. to ( destinationAddress , outputSatoshis )
. change ( sourceAddress )
. sign ( privateKey ) ;
return transaction . serialize ( ) ;
}
/ * *
* Get unspent outputs for a wallet address .
* @ param { string } walletaddress
* @ param { function } successCallback Passes object with { uxtos : [ { txHash , txOutputIndex , script , value } ] , currency : "DOGE" , label : "Dogecoin" }
* @ param { function } errorCallback Passes string error message suitable for display
* @ returns { undefined }
* /
function getUXTOData ( walletaddress , successCallback , errorCallback ) {
apirequest ( SETTINGS . apis . getuxto , {
walletaddress : walletaddress
} , function ( resp ) {
if ( resp . status == "OK" ) {
successCallback ( {
uxtos : resp . unspent _outputs ,
currency : resp . currency ,
label : resp . label
} ) ;
} else {
errorCallback ( resp . msg ) ;
}
} , function ( errorData ) {
try {
var error = $ . parseJSON ( errorData . responseText ) ;
if ( error && typeof error . msg != 'undefined' ) {
errorCallback ( resp . msg ) ;
sendErrorReport ( "Crypto" , "Couldn't get UXTO data" , error . msg ) ;
} else {
errorCallback ( "There's a server or network problem. Check your Internet connection or try again later. Your funds are safe." ) ;
sendErrorReport ( "Crypto" , "Couldn't get UXTO data" , "Server/network problem: " + xhr . status + ": " + xhr . statusText ) ;
}
} catch ( ex ) {
errorCallback ( "There's a server or network problem. Check your Internet connection or try again later. Your funds are safe." ) ;
sendErrorReport ( "Crypto" , "Couldn't get UXTO data" , "Server/network problem: " + xhr . status + ": " + xhr . statusText ) ;
}
} ) ;
}
function sendCoins ( privatekey , fromaddress , toaddress , satoshis ) {
var progressdialog = app . dialog . progress ( "Querying blockchain..." , 25 ) ;
getUXTOData ( fromaddress , function ( success ) {
progressdialog . setProgress ( 50 ) ;
progressdialog . setText ( "Creating transaction..." ) ;
if ( success . uxtos . length == 0 ) {
app . dialog . close ( ) ;
app . dialog . alert ( "Your wallet has no available funds (ZERO_LENGTH_UXTO)." , "Error" ) ;
return ;
} else if ( success . uxtos . length > 1 ) {
app . dialog . close ( ) ;
app . dialog . alert ( "For technical reasons, your wallet isn't compatible with this app right now. You can still sweep this wallet into an alternative app to spend it. (MULTIPLE_UXTO)" , "Error" ) ;
return ;
}
var bitcore = null ;
switch ( success . currency ) {
case "DOGE" :
bitcore = require ( "bitcore-lib-doge" ) ;
break ;
case "BTC" :
bitcore = require ( "bitcore-lib" ) ;
break ;
default :
app . dialog . close ( ) ;
app . dialog . alert ( "This app version doesn't support " + success . currency + "." , "Error" ) ;
return ;
}
var txdata = createSignedTransaction ( bitcore , privatekey , fromaddress , toaddress ,
success . uxtos [ 0 ] . txHash , success . uxtos [ 0 ] . txOutputIndex , success . uxtos [ 0 ] . script ,
success . uxtos [ 0 ] . value , satoshis ) ;
progressdialog . setProgress ( 75 ) ;
progressdialog . setText ( "Sending payment..." ) ;
apirequest ( SETTINGS . apis . broadcasttransaction , {
transactiondata : txdata ,
currency : success . currency
} , function ( resp ) {
if ( resp . status == "OK" ) {
app . dialog . close ( ) ;
app . dialog . alert ( "Sent " + ( satoshis / 100000000 ) + " " + success . currency + " to " + toaddress , "Success!" ) ;
return ;
} else {
app . dialog . close ( ) ;
}
} , function ( errorData ) {
app . dialog . close ( ) ;
try {
var error = $ . parseJSON ( errorData . responseText ) ;
if ( error && typeof error . msg != 'undefined' ) {
app . dialog . alert ( error . msg , "Error" ) ;
sendErrorReport ( "Crypto" , "Couldn't broadcast transaction" , error . msg ) ;
} else {
app . dialog . alert ( "There's a server or network problem. Check your Internet connection or try again later. Your funds are safe." , "Error" ) ;
sendErrorReport ( "Crypto" , "Couldn't broadcast transaction" , "Server/network problem: " + xhr . status + ": " + xhr . statusText ) ;
}
} catch ( ex ) {
app . dialog . alert ( "There's a server or network problem. Check your Internet connection or try again later. Your funds are safe." , "Error" ) ;
sendErrorReport ( "Crypto" , "Couldn't broadcast transaction" , "Server/network problem: " + xhr . status + ": " + xhr . statusText ) ;
}
} ) ;
} , function ( error ) {
app . dialog . close ( ) ;
app . dialog . alert ( error , "Error" ) ;
} ) ;
}
function walletGUISendCoins ( ) {
if ( ! walletPubKeyRegex . test ( $ ( '#walletFromAddress' ) . val ( ) ) ) {
app . dialog . alert ( "Your wallet address doesn't look right. Check it and try again." , "Error" ) ;
return ;
}
if ( isNaN ( $ ( '#transactionAmount' ) . val ( ) ) || $ ( '#transactionAmount' ) . val ( ) < 0.00000001 ) {
app . dialog . alert ( "The amount to send doesn't look right. Check it and try again." , "Error" ) ;
return ;
}
// Remove payment request URL stuff
if ( $ ( '#walletToAddress' ) . val ( ) . startsWith ( "bitcoin:" ) ) {
$ ( '#walletToAddress' ) . val ( $ ( '#walletToAddress' ) . val ( ) . replace ( "bitcoin:" , "" ) ) ;
}
if ( $ ( '#walletToAddress' ) . val ( ) . startsWith ( "dogecoin:" ) ) {
$ ( '#walletToAddress' ) . val ( $ ( '#walletToAddress' ) . val ( ) . replace ( "dogecoin:" , "" ) ) ;
}
if ( ! walletPubKeyRegex . test ( $ ( '#walletToAddress' ) . val ( ) ) ) {
app . dialog . alert ( "The recipient's wallet address doesn't look right. Check it and try again." , "Error" ) ;
return ;
}
sendCoins ( $ ( '#walletPrivateKey' ) . val ( ) , $ ( '#walletFromAddress' ) . val ( ) , $ ( '#walletToAddress' ) . val ( ) , $ ( '#transactionAmount' ) . val ( ) * 100000000 ) ;
}
function displayWalletBalance ( address ) {
if ( ! walletPubKeyRegex . test ( address ) ) {
app . dialog . alert ( "That doesn't look like a valid wallet address." , "Error" ) ;