From aba3c871ba132ebc798b68ef6c976ef858546668 Mon Sep 17 00:00:00 2001 From: Skylar Ittner Date: Fri, 22 Apr 2022 21:00:47 -0600 Subject: [PATCH] Get tx fees from API instead of using bitcore's too-expensive fees --- www/assets/js/crypto.js | 96 +++++++++++++++++++++++++++++------------ www/settings.js | 3 +- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/www/assets/js/crypto.js b/www/assets/js/crypto.js index acb8b23..f9d08d9 100644 --- a/www/assets/js/crypto.js +++ b/www/assets/js/crypto.js @@ -45,19 +45,34 @@ function scanPrivateKeyQrCode(callback) { * @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, utxos, outputSatoshis) { +function createSignedTransaction(bitcoreLib, privateKeyString, sourceAddress, destinationAddress, utxos, outputSatoshis, feePerByte) { + if (typeof feePerByte == "undefined") { + feePerByte = -1; + } try { var privateKey = new bitcoreLib.PrivateKey(privateKeyString); var transaction = new bitcoreLib.Transaction() .from(utxos) .to(destinationAddress, outputSatoshis) - .change(sourceAddress) - .sign(privateKey); + .change(sourceAddress); + + var size = transaction._estimateSize(); + var fee = size * feePerByte; + + if (feePerByte > -1) { + // use our fee + transaction = transaction.fee(fee); + } else { + // use lib's fee + fee = transaction.getFee(); + } + + transaction = transaction.sign(privateKey); var inputTotal = transaction._getInputAmount(); - var outputTotal = transaction.getFee() + outputSatoshis; + var outputTotal = fee + outputSatoshis; } catch (ex) { throw new Error("There was an internal error while creating the transaction. Details: " + ex.message); } @@ -132,9 +147,9 @@ function getUTXOData(walletaddress, successCallback, errorCallback) { } function sendCoins(privatekey, fromaddress, toaddress, amount) { - var progressdialog = app.dialog.progress("Querying blockchain...", 25); + var progressdialog = app.dialog.progress("Querying blockchain...", 20); getUTXOData(fromaddress, function (success) { - progressdialog.setProgress(50); + progressdialog.setProgress(40); progressdialog.setText("Creating transaction..."); if (success.utxos.length == 0) { app.dialog.close(); @@ -160,29 +175,56 @@ function sendCoins(privatekey, fromaddress, toaddress, amount) { return; } - try { - var txdata = createSignedTransaction(bitcore, privatekey, fromaddress, toaddress, utxos, satoshis); - } catch (ex) { - console.error(ex); - app.dialog.close(); - app.dialog.alert(ex.message, "Error"); - return; - } - - progressdialog.setProgress(75); - progressdialog.setText("Sending payment..."); - - apirequest(SETTINGS.apis.broadcasttransaction, { - transactiondata: txdata, + progressdialog.setProgress(60); + progressdialog.setText("Calculating fees..."); + apirequest(SETTINGS.apis.cryptofees, { currency: success.currency }, function (resp) { if (resp.status == "OK") { - app.dialog.close(); - app.dialog.alert("Sent " + amount + " " + success.currency + " to " + toaddress.substring(0, 5) + "..." + toaddress.substring(toaddress.length - 5, 999), "Success!"); - $('#walletPrivateKey').val(""); // clear private key input box - return; + try { + var txdata = createSignedTransaction(bitcore, privatekey, fromaddress, toaddress, utxos, satoshis, resp.feePerByte); + } catch (ex) { + console.error(ex); + app.dialog.close(); + app.dialog.alert(ex.message, "Error"); + return; + } + + progressdialog.setProgress(80); + 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 " + amount + " " + success.currency + " to " + toaddress.substring(0, 5) + "..." + toaddress.substring(toaddress.length - 5, 999), "Success!"); + $('#walletPrivateKey').val(""); // clear private key input box + return; + } else { + app.dialog.close(); + app.dialog.alert(resp.msg, "Error"); + } + }, 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); + } + }); } else { app.dialog.close(); + app.dialog.alert(resp.msg, "Error"); } }, function (errorData) { app.dialog.close(); @@ -190,14 +232,14 @@ function sendCoins(privatekey, fromaddress, toaddress, amount) { 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); + sendErrorReport("Crypto", "Couldn't get transaction fees", 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); + sendErrorReport("Crypto", "Couldn't get transaction fees", "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); + sendErrorReport("Crypto", "Couldn't get transaction fees", "Server/network problem: " + xhr.status + ": " + xhr.statusText); } }); }, function (error) { diff --git a/www/settings.js b/www/settings.js index 2b557f8..f062edc 100644 --- a/www/settings.js +++ b/www/settings.js @@ -41,7 +41,8 @@ var SETTINGS = { // Crypto: check balance and send transactions walletbalance: "http://localhost/helena.express/apis/crypto/walletbalance", getutxo: "http://localhost/helena.express/apis/crypto/getutxo", - broadcasttransaction: "http://localhost/helena.express/apis/crypto/broadcasttransaction" + broadcasttransaction: "http://localhost/helena.express/apis/crypto/broadcasttransaction", + cryptofees: "http://localhost/helena.express/apis/crypto/fees" }, stripe_pubkey: "pk_test_51J6qFXCa1Fboir5UzPO3LCiMsVNiFP2lq4wR0dEcjJJVzAaJ3uRggDekZPB3qeYpMD3ayIYHKyD5sSn0IFLlEXMW001LqrvGSH", branding: {