bitcoinjs-lib
bitcoinjs-lib copied to clipboard
Send a Transaction with SegWit P2PKH Keypair
Hi,
I'm new to bitcoinjs-lib and am having a hard time of creating a simple node.js script to send BTC on testnet. A lot of the resources online use outdated method names (e.g., bitcoin.Transaction() vs bitcoin.TransactionBuilder(), need to import ECPair, etc..)
If someone can assist with a working script it would be much appreciated. Currently, I have something along the lines of:
const bitcoin = require('bitcoinjs-lib');
const ECPairFactory = require('ecpair').default;
const ecc = require('tiny-secp256k1');
const ECPair = ECPairFactory(ecc);
const TESTNET = bitcoin.networks.testnet
const privateKey = '';
const toAddress = '';
const keyPair = ECPair.fromWIF(privateKey, TESTNET);
const account = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: TESTNET })
const psbt = new bitcoin.Psbt({ network: keyPair.network });
psbt.addOutput({
address: toAddress,
value: 1500,
psbt.signAllInputs(keyPair.signSchnorr)
psbt.finalizeAllInputs();
const transaction = psbt.extractTransaction();
console.log(transaction)
Try reading these tests from top to bottom. For the helper functions, don't bother looking at them first, just glean the fact that they return a bunch of payment information.
Once you get a grasp on how each script type is constructed using PSBT (read each example including comments) then you should have a better grasp on what you need to do.
https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts
Here is one example you can try to test it it should work but you have to fund your wallet with test btc in order that works. Also replace the placeholder values like previous_transaction_hash and previous_output_script with the actual values from the UTXO you are spending. You did not precisely explain what do you want to achieve? below are two working examples:
1.Example
const bitcoin = require('bitcoinjs-lib');
const ECPair = require('bitcoinjs-lib').ECPair;
const networks = require('bitcoinjs-lib').networks;
const privateKey = ''; // Replace with your private key
const toAddress = ''; // Replace with the recipient's address
const TESTNET = networks.testnet;
const address = keyPair.getAddress();
const keyPair = ECPair.fromWIF(privateKey, TESTNET);
const address = keyPair.getAddress();
const psbt = new bitcoin.Psbt({ network: TESTNET });
// Add the input (UTXO) - assuming you have an existing unspent transaction output (UTXO)
psbt.addInput({
hash: 'previous_transaction_hash',
index: 0, // index of the output in the previous transaction
witnessUtxo: {
script: Buffer.from('previous_output_script', 'hex'),
value: 1000000, // value of the UTXO in satoshis
},
redeemScript: undefined, // redeemScript if P2SH
witnessScript: undefined, // witnessScript if P2WSH
});
// Add the output
psbt.addOutput({
address: toAddress,
value: 1500, // amount to send in satoshis
});
// Sign the input
psbt.signInput(0, keyPair);
// Finalize the transaction
psbt.finalizeAllInputs();
// Get the finalized transaction
const transaction = psbt.extractTransaction();
console.log(transaction.toHex());
2.Example
const bitcoin = require('bitcoinjs-lib');
const axios = require('axios');
const fromAddress = ''; // Sender's testnet address
const toAddress = ''; // Recipient's testnet address
const privateKey = ''; // Sender's private key in WIF format
const amountToSend = 1500; // Amount to send in satoshis
const network = bitcoin.networks.testnet;
async function sendBitcoin() {
const keyPair = bitcoin.ECPair.fromWIF(privateKey, network);
const address = keyPair.getAddress();
// Fetch UTXOs
const utxos = await axios.get(`https://api.blockcypher.com/v1/btc/test3/addrs/${fromAddress}?unspentOnly=true`);
const utxo = utxos.data.txrefs[0]; // Assuming you have at least one UTXO
const txb = new bitcoin.TransactionBuilder(network);
// Add the UTXO
txb.addInput(utxo.tx_hash, utxo.tx_output_n);
// Add the recipient address as an output
txb.addOutput(toAddress, amountToSend);
// Sign the transaction
txb.sign(0, keyPair);
// Build and broadcast the transaction
const tx = txb.build();
const txHex = tx.toHex();
// Broadcast the transaction
await axios.post(`https://api.blockcypher.com/v1/btc/test3/txs/push`, { tx: txHex });
console.log(`Transaction sent: https://live.blockcypher.com/btc-testnet/tx/${tx.getId()}`);
}
sendBitcoin();
@junderw i am getting error [Error: Can not finalize input #0]
import * as bitcoin from 'bitcoinjs-lib';
const keyPaird = bitcoin.ECPair.fromWIF(privateKey,network);
const payment = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ pubkey: keyPaird.publicKey, network: network }) });
console.log(payment)
const { address } = payment;
const redeemScript = payment.redeem?.output;
console.log('bro')
if (!redeemScript || !address) return null;
console.log('redde,')
const psbt = new bitcoin.Psbt({ network });
// // Fetch the source address UTXO data
const utxoData = await axios.get(`https://api.blockcypher.com/v1/btc/test3/addrs/${sourceAddress}/full`);
console.log(utxoData.data)
let balance=utxoData.data.balance
console.log(balance)
const utxos = utxoData.data.txs.flatMap((tx) => {
let voutIndex = 0;
return tx.outputs.map((output) => {
const utxo = {
txid: tx.hash,
vout: voutIndex++,
scriptPubKey: output.script,
amount: output.value,
};
if (!output.spent_by) {
return utxo;
}
});
}).filter(Boolean);
console.log(utxos,'utox')
console.log(utxos)
const fee = 10000; // Set an appropriate fee (in satoshis)
for (const element of utxos) {
const response = await axios.get(`https://api.blockcypher.com/v1/btc/test3/txs/${element.txid}?includeHex=true`);
const previousTx = response.data.hex;
console.log(previousTx,'dasds')
psbt.addInput({
hash: element.txid,
index: element.vout,
nonWitnessUtxo: Buffer.from(previousTx, 'hex'),
redeemScript:redeemScript
});
}
const feeRate = 1; // Adjust this value based on the current network conditions
const left=balance-feeRate-amountToSend
// // Add output
psbt.addOutput({
address: destinationAddress,
value: amountToSend+feeRate
});
psbt.addOutput({
address:sourceAddress,
value:left
})
// }
// Sign each input
try {
psbt.signAllInputs(keyPaird)
console.log(psbt.data.inputs)
// Finalize all inputs
psbt.finalizeAllInputs();
// Extract transaction
const tx = psbt.extractTransaction().toHex();
console.log('Transaction:', tx);
getTransactionId(tx);
} catch (error) {
console.log(error)
}
You need to make sure that every output is actually your output.
[{"nonWitnessUtxo": [Object], "redeemScript": [Object], "unknownKeyVals": []}, {"nonWitnessUtxo": [Object], "partialSig": [[Object]], "redeemScript": [Object], "unknownKeyVals": []}, {"nonWitnessUtxo": [Object], "redeemScript": [Object], "unknownKeyVals": []}] is the response I am getting from
psbt.signAllInputs(keyPaird console.log(psbt.data.inputs) I think I don't have a partialSig for first element of array and I am getting error. I am not able to resolve it.
That's because it's not your UTXO.
@junderw how do i resolve const payment = bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ pubkey: keyPaird.publicKey, network: network }) }); to get correct address and redeemScript from
const { address } = payment;
const redeemScript = payment.redeem?.output;
The address I am getting from here is not the actual address I have,that I am making https://api.blockcypher.com/v1/btc/test3/addrs/${sourceAddress}/full
API call from.
You are misunderstanding me. I didn't say your address or redeemScript are wrong.
You are trying to sign a utxo which is not yours.
Your code is adding EVERY utxo. You only want to add the utxo which actually has your address.
You can not spend Bitcoin that was not sent to you.
Bitcoin transactions have multiple outputs, and each output might be sent to a different address.
Do you understand now? Please let me know if something is not clear.