bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.7k stars 2.11k forks source link

High Fee Warning and Transaction Error when Sending BTC Using P2WPKH #1971

Closed kiran-bhalerao closed 1 year ago

kiran-bhalerao commented 1 year ago

Description: I'm attempting to send Bitcoin (BTC) using the Pay-to-Witness-Public-Key-Hash (P2WPKH) format. However, I'm encountering an error message that reads: [Error: Warning: You are paying around 0.04777745 in fees, which is 19501 satoshi per byte for a transaction with a VSize of 245 bytes (segwit counted as 0.25 byte per byte). Use setMaximumFeeRate method to raise your threshold, or pass true to the first arg of extractTransaction.]

Code: Here's the relevant code snippet I'm using:


// There are multiple unspentTransactions
for (const tx of unspentTransactions) {
  transaction.addInput({
    hash: tx.txHash,
    index: tx.index,
    witnessUtxo: {
      script: payment.output!,
      value: tx.value,
    },
  });
}

// Add an output to send funds to the recipient address
transaction.addOutput({
  address: recipientAddress,
  value: sendAmount,
});

// Sign the transaction inputs with the sender's private key
for (let i = 0; i < unspentTransactions.length; i++) {
  transaction.signInput(i, this.keyPair!);
}

// Finalize the Transaction
transaction.finalizeAllInputs();

// Get the raw signed transaction in hexadecimal format
const rawSignedTxHex = transaction.extractTransaction().toHex();

I'm looking for guidance on how to properly calculate and set the transaction fees for P2WPKH transactions. I would appreciate insights into why I'm encountering this error and how to rectify it.

junderw commented 1 year ago

I am glad I put that safe guard in there!!!!!

That just saved you from losing $1311.92 of Bitcoin!!!

In Bitcoin, the fee of a transaction is the value of all inputs minus the value of all outputs.

let totalInputValue = 0; // <------ LOOK HERE --------------
// There are multiple unspentTransactions
for (const tx of unspentTransactions) {
  totalInputValue += tx.value; // <------ LOOK HERE --------------
  transaction.addInput({
    hash: tx.txHash,
    index: tx.index,
    witnessUtxo: {
      script: payment.output!,
      value: tx.value,
    },
  });
}

// Add an output to send funds to the recipient address
transaction.addOutput({
  address: recipientAddress,
  value: sendAmount,
});

// Add an output to send change back to ourself
const feeSatoshis = 3000; // <------ LOOK HERE --------------
const myAddress = 'This should be your own address string';
const changeValue = totalInputValue - sendAmount - feeSatoshis; // <------ LOOK HERE --------------
// If change value is too small, skip the change and just include it into fees
// (outputs lower than 546 satoshis are known as "dust" and not worth creating.
if (changeValue > 600) {
  transaction.addOutput({
    address: myAddress,
    value: changeValue, // <------ LOOK HERE --------------
  });
}

// Sign the transaction inputs with the sender's private key
for (let i = 0; i < unspentTransactions.length; i++) {
  transaction.signInput(i, this.keyPair!);
}

// Finalize the Transaction
transaction.finalizeAllInputs();

// Get the raw signed transaction in hexadecimal format
const rawSignedTxHex = transaction.extractTransaction().toHex();
junderw commented 1 year ago

Also, for fee estimation, you can query your Bitcoin RPC node using estimatesmartfee (https://developer.bitcoin.org/reference/rpc/estimatesmartfee.html) (this gives you the recommended fee rate in BTC per kilo-vByte)

Or you can use a 3rd party trusted service like https://mempool.space/api/v1/fees/recommended which will tell you the recommended fee rate in satoshis per vByte

Now that you have a fee rate, you need to estimate how many vBytes your transaction size will be.

A vByte is the byte size where each segwit byte is counted as 0.25 bytes.

Since byte sizes of inputs like P2WPKH don't change, it is possible to create a function to estimate the size based on number of inputs and outputs of a given type. (I have a messy prototype here: https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c)

kiran-bhalerao commented 1 year ago

@junderw thanks a lot 👍