bitcoinjs / bitcoinjs-lib

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

psbt.addOutput isnt adding my output to my PSBT. #2158

Closed BarneyChambers closed 2 months ago

BarneyChambers commented 2 months ago

For some reason when I try to add an output to my PSBT, the output is not defined when I decode the final PSBT values bitcoinlibjs gives me:

export async function createUnsignedPSBT(    
    network: string, 
    eventData: any
): Promise<string | undefined> {
    try {
        console.log('Creating unsigned PSBT...');

        const multisigDescriptor = JSON.stringify(MULTISIG_DESCRIPTORS);
        const amount = Number(eventData[0].amount);
        const utxoFee = Number(eventData[0].utxoFee);
        const pkscript = eventData[0].pkscript;

        const totalSpend = amount + utxoFee;

        // Convert network string to bitcoin.Network object
        const networkString = getBitcoinNetworkString(network); 
        const networkType = getBitcoinNetwork(network); 

        const destinationAddress = getDestinationAddress(pkscript, networkString);

        const feeRate = Number(eventData[0].feeRate || process.env.FEE_RATE);
        if (isNaN(feeRate)) {
            throw new Error('Fee rate is not specified or invalid.');
        }

        // Derive the multisig address (index 0)
        const multisigAddress = getMultisigAddress(multisigDescriptor, 0, networkType);
        console.log(`Multisig Address: ${multisigAddress}`);

        // Determine the correct mempool.space API endpoint
        const mempoolSpaceAPI = network === 'coreMainnet' ? 'https://mempool.space/api' : 'https://mempool.space/testnet/api';

        // Fetch balance for the multisig address using mempool.space API
        const balanceResponse = await axios.get(`${mempoolSpaceAPI}/address/${multisigAddress}/utxo`);
        const balanceSatoshis = balanceResponse.data.reduce((acc: number, utxo: any) => acc + utxo.value, 0);
        const balanceBTC = balanceSatoshis / 100000000; // Convert satoshis to BTC

        if (balanceSatoshis < totalSpend) {
            throw new Error("Insufficient balance in wallet.");
        }

        // Fetch UTXOs for the multisig address from mempool.space
        const utxos = balanceResponse.data;

        if (utxos.length === 0) {
            throw new Error("No UTXOs available to create the transaction.");
        }

        const psbt = new bitcoin.Psbt({ network: networkType });

        let inputAmountSatoshis = 0;

        for (const utxo of utxos) {
            psbt.addInput({
                hash: utxo.txid,
                index: utxo.vout,
                witnessUtxo: {
                    script: bitcoin.address.toOutputScript(multisigAddress, networkType),
                    value: utxo.value,
                },
            });
            inputAmountSatoshis += utxo.value;
            if (inputAmountSatoshis >= totalSpend + (250 * feeRate)) {
                break;
            }
        }

        const outputAmountSatoshis = Math.floor(amount);
        const changeAmountSatoshis = inputAmountSatoshis - outputAmountSatoshis - (250 * feeRate);

        // Add output to destination address
        psbt.addOutput({
            address: destinationAddress,
            value: outputAmountSatoshis,
        });

        console.log('=================================');
        console.log('destination address: ');
        console.log(destinationAddress);
        console.log('output amount: ');
        console.log(outputAmountSatoshis);
        console.log('=================================')

        // Use the same multisig address for change
        if (changeAmountSatoshis > 0) {
            psbt.addOutput({
                address: multisigAddress,
                value: changeAmountSatoshis,
            });

            console.log('Adding output for sending change: '+changeAmountSatoshis+' to: '+multisigAddress);
        }

        console.log('PSBT in base 64:');
        console.log(psbt.toBase64());

        console.log('stringified PSBT: ');
        console.log(JSON.stringify(psbt));

        // Return the PSBT in base64 format
        return psbt.toBase64();

    } catch (error: any) {
        console.log('Error in createUnsignedPSBT:', error.message);
        console.log('Stack trace:', error.stack);
        return undefined;
    }
}

Here is the resulting data that is returned:


=================================
destination address:
miAby844kZiD9aRmF2weyMznmDyrrp4ieD
output amount:
120006
=================================

Adding output for sending change: 9877494 to: tb1qmht5jjzzdgf0q7256c3w244h4gmppyulp07a5savtec6eqazrkxsdyuze4

PSBT in base 64:
cHNidP8BAIACAAAAAdRVGxXNUgdAZW1cOfMSWvXShemETQhl3B5K+rYN8qgaAAAAAAD/////AsbUAQAAAAAAGXapFB0PFyoOy0iu4b4afKgDscXlqbFuiKz2t5YAAAAAACIAIN3XSUhCahLweVTWIuVWt6o2EJOfC/3aQ6xecayDoh2NAAAAAAABASuAlpgAAAAAACIAIN3XSUhCahLweVTWIuVWt6o2EJOfC/3aQ6xecayDoh2NAAAA

stringified PSBT:
{"data":{"inputs":[{"unknownKeyVals":[],"witnessUtxo":{"script":{"type":"Buffer","data":[0,32,221,215,73,72,66,106,18,240,121,84,214,34,229,86,183,170,54,16,147,159,11,253,218,67,172,94,113,172,131,162,29,141]},"value":10000000}}],"outputs":[{"unknownKeyVals":[]},{"unknownKeyVals":[]}],"globalMap":{"unsignedTx":{}}}}

As you can see in my final psbt, the outputs are empty, why is this?

"outputs":[{"unknownKeyVals":[]},{"unknownKeyVals":[]}]
javiervargas commented 4 weeks ago

Hi @BarneyChambers , I'm having same issue with my PSBT. What I'm missing pls?

My code:

` const psbt = async function () { const p2sh = createPayment('p2sh-p2ms(2 of 3)', xpubs, networks.testnet) if (p2sh) { const redeemScript = p2sh.output; console.log("Redeem Script (hex):", redeemScript.toString('hex')); } else { console.log("Could not generate P2SH transaction."); } const redeemScript = p2sh.output; const inputData = await getInputData(p2sh, 'p2sh'); const {address} = bitcoin.payments.p2pkh({ pubkey: cosigner2Pubkey, network: bitcoin.networks.testnet })

const spendable = 10000;
const fees = spendable - 5000;
const totalToSend = spendable - fees;
const rawTx = '02000000000101880573fb870032e84f7a28fe0dffc57f4df9b8bf1951feea484c1efc861f1fb20000000000fdffffff02571f02000000000017a914ef7e3a4c09df771b92e49c38da2fd87f56b849f387a08601000000000017a91416c4f91e5419de87b7b1a439ee7344b2bb32126c870247304402207bb54e1cd710b282d3eeec18baa69a88ce2446167c804114e1c59b0f231bb86a02201acee5aef96b0fb2f2f86280d9b378a2b51a281ad7032b4e9167ccdafe5eb311012103deba8a8d6eb407da0558716c051b172b139934ea0ae471f4d696006c3a9627b334b43000';
const nonWitnessUtxo = Buffer.from(rawTx, 'hex');
const psbt = new bitcoin.Psbt({ network: testnet })
    .addInput({
        hash: '7124ec113be92f02dc35c4a93e4b93441a47d72672a9b4fead07618d675cd4b6',
        index: 1,
        nonWitnessUtxo,
        redeemScript: redeemScript,
        bip32Derivation: [
            {
                masterFingerprint: Buffer.from('44c11f58', 'hex'),
                pubkey: cosigner1Pubkey,
                path: "m/45'/1'/0'", 
            },
            {
                masterFingerprint: Buffer.from('ad38d9ea', 'hex'),
                pubkey: cosigner2Pubkey,
                path: "m/45'/1'/0'", 
            },
            {
                masterFingerprint: Buffer.from('93c410d8', 'hex'),
                pubkey: cosigner3Pubkey,
                path: "m/45'/1/0", 
            }
        ]
    });
psbt.addOutput({address, value: totalToSend})
console.log("PSBT (unsigned):", psbt);
console.log("PSBT JSON: ", psbt.data);
console.log('PSBT Base64: ', psbt.toBase64());
console.log("Raw Hex" ,  psbt.toHex());
console.log("JSON: ", JSON.stringify(psbt));
return psbt;

}; ` Output

Redeem Script (hex): 5221022372073f517ddd85783b9cb6ef3b73a9167e9cb17f8e7d9cba61412ec25d1436210236a1a67f1498c147088b0e0f222feee67052892a7cea3a5b91f1c1107dc779a321038c582b63322f50bc2837315b3e900e10f2091fd7e3454d1f8fae5c5f9651ab8f53ae PSBT (unsigned): Psbt { data: Psbt { inputs: [ [Object] ], outputs: [ [Object] ], globalMap: { unsignedTx: PsbtTransaction {} } } } PSBT JSON: Psbt { inputs: [ { unknownKeyVals: [], redeemScript: <Buffer 52 21 02 23 72 07 3f 51 7d dd 85 78 3b 9c b6 ef 3b 73 a9 16 7e 9c b1 7f 8e 7d 9c ba 61 41 2e c2 5d 14 36 21 02 36 a1 a6 7f 14 98 c1 47 08 8b 0e 0f 22 ... 55 more bytes>, bip32Derivation: [Array], nonWitnessUtxo: [Getter/Setter] } ], outputs: [ { unknownKeyVals: [] } ], globalMap: { unsignedTx: PsbtTransaction {} } } PSBT Base64: cHNidP8BAFUCAAAAAbbUXGeNYQet/rSpcibXRxpEk0s+qcQ13AIv6TsR7CRxAQAAAAD/////AYgTAAAAAAAAGXapFNPCFDSmzeYKli+eAgd5bQx5dJI+iKwAAAAAAAEA4AIAAAAAAQGIBXP7hwAy6E96KP4N/8V/Tfm4vxlR/upITB78hh8fsgAAAAAA/f///wJXHwIAAAAAABepFO9+OkwJ33cbkuScONov2H9WuEnzh6CGAQAAAAAAF6kUFsT5HlQZ3oe3saQ57nNEsrsyEmyHAkcwRAIge7VOHNcQsoLT7uwYuqaaiM4kRhZ8gEEU4cWbDyMbuGoCIBrO5a75aw+y8vhigNmzeKK1Giga1wMrTpFnzNr+XrMRASED3rqKjW60B9oFWHFsBRsXKxOZNOoK5HH01pYAbDqWJ7M0tDAAAQRpUiECI3IHP1F93YV4O5y27ztzqRZ+nLF/jn2cumFBLsJdFDYhAjahpn8UmMFHCIsODyIv7uZwUokqfOo6W5HxwRB9x3mjIQOMWCtjMi9QvCg3MVs+kA4Q8gkf1+NFTR+PrlxfllGrj1OuIgYCKxwRBV2ABjP5pos2dxUB7PWBwpoZvACb1pSMKYFQ/dAQrTjZ6i0AAIABAACAAAAAgCIGA048T/5g3ASlmmC6tiXfGpoWXs7SJnqWic7l2S3p6AtIEJPEENgtAACAAQAAAAAAAAAiBgPsIeWGl51+oUw+z9ifLKWu3UyZyKis/zLk2F4VdCxUFBBEwR9YLQAAgAEAAIAAAACAAAA= Raw Hex 70736274ff0100550200000001b6d45c678d6107adfeb4a97226d7471a44934b3ea9c435dc022fe93b11ec24710100000000ffffffff0188130000000000001976a914d3c21434a6cde60a962f9e0207796d0c7974923e88ac00000000000100e002000000000101880573fb870032e84f7a28fe0dffc57f4df9b8bf1951feea484c1efc861f1fb20000000000fdffffff02571f02000000000017a914ef7e3a4c09df771b92e49c38da2fd87f56b849f387a08601000000000017a91416c4f91e5419de87b7b1a439ee7344b2bb32126c870247304402207bb54e1cd710b282d3eeec18baa69a88ce2446167c804114e1c59b0f231bb86a02201acee5aef96b0fb2f2f86280d9b378a2b51a281ad7032b4e9167ccdafe5eb311012103deba8a8d6eb407da0558716c051b172b139934ea0ae471f4d696006c3a9627b334b430000104695221022372073f517ddd85783b9cb6ef3b73a9167e9cb17f8e7d9cba61412ec25d1436210236a1a67f1498c147088b0e0f222feee67052892a7cea3a5b91f1c1107dc779a321038c582b63322f50bc2837315b3e900e10f2091fd7e3454d1f8fae5c5f9651ab8f53ae2206022b1c11055d800633f9a68b36771501ecf581c29a19bc009bd6948c298150fdd010ad38d9ea2d00008001000080000000802206034e3c4ffe60dc04a59a60bab625df1a9a165eced2267a9689cee5d92de9e80b481093c410d82d0000800100000000000000220603ec21e586979d7ea14c3ecfd89f2ca5aedd4c99c8a8acff32e4d85e15742c54141044c11f582d00008001000080000000800000 JSON: {"data":{"inputs":[{"unknownKeyVals":[],"redeemScript":{"type":"Buffer","data":[82,33,2,35,114,7,63,81,125,221,133,120,59,156,182,239,59,115,169,22,126,156,177,127,142,125,156,186,97,65,46,194,93,20,54,33,2,54,161,166,127,20,152,193,71,8,139,14,15,34,47,238,230,112,82,137,42,124,234,58,91,145,241,193,16,125,199,121,163,33,3,140,88,43,99,50,47,80,188,40,55,49,91,62,144,14,16,242,9,31,215,227,69,77,31,143,174,92,95,150,81,171,143,83,174]},"bip32Derivation":[{"masterFingerprint":{"type":"Buffer","data":[68,193,31,88]},"pubkey":{"type":"Buffer","data":[3,236,33,229,134,151,157,126,161,76,62,207,216,159,44,165,174,221,76,153,200,168,172,255,50,228,216,94,21,116,44,84,20]},"path":"m/45'/1'/0'"},{"masterFingerprint":{"type":"Buffer","data":[173,56,217,234]},"pubkey":{"type":"Buffer","data":[2,43,28,17,5,93,128,6,51,249,166,139,54,119,21,1,236,245,129,194,154,25,188,0,155,214,148,140,41,129,80,253,208]},"path":"m/45'/1'/0'"},{"masterFingerprint":{"type":"Buffer","data":[147,196,16,216]},"pubkey":{"type":"Buffer","data":[3,78,60,79,254,96,220,4,165,154,96,186,182,37,223,26,154,22,94,206,210,38,122,150,137,206,229,217,45,233,232,11,72]},"path":"m/45'/1/0"}],"nonWitnessUtxo":{"type":"Buffer","data":[2,0,0,0,0,1,1,136,5,115,251,135,0,50,232,79,122,40,254,13,255,197,127,77,249,184,191,25,81,254,234,72,76,30,252,134,31,31,178,0,0,0,0,0,253,255,255,255,2,87,31,2,0,0,0,0,0,23,169,20,239,126,58,76,9,223,119,27,146,228,156,56,218,47,216,127,86,184,73,243,135,160,134,1,0,0,0,0,0,23,169,20,22,196,249,30,84,25,222,135,183,177,164,57,238,115,68,178,187,50,18,108,135,2,71,48,68,2,32,123,181,78,28,215,16,178,130,211,238,236,24,186,166,154,136,206,36,70,22,124,128,65,20,225,197,155,15,35,27,184,106,2,32,26,206,229,174,249,107,15,178,242,248,98,128,217,179,120,162,181,26,40,26,215,3,43,78,145,103,204,218,254,94,179,17,1,33,3,222,186,138,141,110,180,7,218,5,88,113,108,5,27,23,43,19,153,52,234,10,228,113,244,214,150,0,108,58,150,39,179,52,180,48,0]}}],"outputs":[{"unknownKeyVals":[]}],"globalMap":{"unsignedTx":{}}}} PSBT created but not finalized (pending hardware wallet signatures).