( privateKey: ECPairInterface, utxos: IUtxo[], to: ISendToAddress[], change: string, // Change address network: bitcoin.Network, fee = 1000 // Assume a fee of 1000 )
| 5 | |
| 6 | // Create a transaction to pay any number of addresses using P2WPKH or P2TR UTXOs |
| 7 | export function createCoinPsbt( |
| 8 | privateKey: ECPairInterface, |
| 9 | utxos: IUtxo[], |
| 10 | to: ISendToAddress[], |
| 11 | change: string, // Change address |
| 12 | network: bitcoin.Network, |
| 13 | fee = 1000 // Assume a fee of 1000 |
| 14 | ) { |
| 15 | const psbt = new bitcoin.Psbt({network}); |
| 16 | const totalInput = addPsbtPayUtxos(privateKey, psbt, utxos, network); |
| 17 | |
| 18 | // Send all to one address |
| 19 | if (to.length === 1 && Number(to[0]!.amount) === totalInput) { |
| 20 | const value = totalInput - fee; |
| 21 | if (value < MIN_SATOSHIS) { |
| 22 | throw new Error("Insufficient fund"); |
| 23 | } |
| 24 | psbt.addOutput({ |
| 25 | address: to[0]!.address, |
| 26 | value: value, |
| 27 | }); |
| 28 | } else { |
| 29 | let totalOutput = 0; |
| 30 | for (const item of to) { |
| 31 | psbt.addOutput({ |
| 32 | address: item.address, |
| 33 | value: Number(item.amount), |
| 34 | }); |
| 35 | totalOutput += Number(item.amount); |
| 36 | } |
| 37 | |
| 38 | const value = totalInput - totalOutput - fee; |
| 39 | if (value < 0) throw new Error("NoFund"); |
| 40 | if (value > MIN_SATOSHIS) { |
| 41 | psbt.addOutput({ |
| 42 | address: change, |
| 43 | value: value, |
| 44 | }); |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // Sign each input |
| 49 | for (let i = 0; i < psbt.inputCount; i++) { |
| 50 | const privateKeyWif = utxos[i]?.privateKeyWif; |
| 51 | if (privateKeyWif) { |
| 52 | signPsbtInput(ECPair.fromWIF(privateKeyWif, network), psbt, i); |
| 53 | } else { |
| 54 | signPsbtInput(privateKey, psbt, i); |
| 55 | } |
| 56 | } |
| 57 | psbt.finalizeAllInputs(); |
| 58 | return psbt.extractTransaction(); |
| 59 | } |
no test coverage detected