Create And Sign PSBT

  • Purpose: To handle the process of signing a Bitcoin transaction.

  • Steps:

    1. Retrieve UTXOs: Fetches unspent transaction outputs (UTXOs) for a particular address on Bitcoin's testnet.

    2. Construct Transaction: Builds the transaction by selecting UTXOs and defining the recipient.

    3. Convert to PSBT: Converts the transaction into a base64 encoded PSBT.

    4. Sign the PSBT: Calls signPsbt to sign the transaction with the relevant keys.

See more about sign pbst on sats-connect docs here.

import { useZky, useBitcoin } from "@kondor-finance/zky-toolkit";
import { useCallback, useState } from "react";
import * as btc from "micro-btc-signer";
import { hex, base64 } from "@scure/base";
import axios from "axios";

export default function SignPsbt() {
  const { publicKeys, addresses } = useZky();
  const [ psbtSigned, setPsbtSigned ] = useState<string | null>(null);
  const { signPsbt } = useBitcoin();


  const handleSignPsbt = useCallback(async () => {
    try {
      const utxos = await axios.get(`https://blockstream.info/testnet/api/address/${addresses.ordinalsAddress}/utxo`);
      if (utxos.data.length === 0) throw "No UTXOs found for the connected address";
      const bitcoinTestnet = {
        bech32: 'tb',
        pubKeyHash: 0x6f,
        scriptHash: 0xc4,
        wif: 0xef,
      }
      const utxo = utxos.data[0]; // Select the UTXO to spend
      const tx = new btc.Transaction();
      const publicKeyBytes = hex.decode(publicKeys.ordinalsPublicKey!);
      const p2tr = btc.p2tr(publicKeyBytes, undefined, bitcoinTestnet)
     
      const txidBuffer = Buffer.from(utxo.txid, 'hex');
      const txidBufferLE = Buffer.from(txidBuffer).reverse(); // Convert to little-endian

      tx.addInput({
        txid: txidBufferLE,
        index: utxo.vout,
        witnessUtxo: {
          script: p2tr.script,
          amount: BigInt(546),
        },
        tapInternalKey: publicKeyBytes,
      })

      const recipient = "tb1pzwa68q3udj0f7g5xtdakgecvf45dvssu66ry7y3at22w7vus20vq3sgw62"
      tx.addOutputAddress(recipient, BigInt(150), bitcoinTestnet)

      const psbt = tx.toPSBT(0)
      const psbtB64 = base64.encode(psbt)

      const response = await signPsbt({
        psbt: psbtB64,
        signInputs: { [addresses.ordinalsAddress!]: [0] },
      })

      console.log("PSBT signed:", response);
      if (response.psbt) setPsbtSigned(response.psbt);
    } catch (error) {
      console.error("Failed to send BTC transaction:", error);
    }
  }, [addresses, publicKeys, signPsbt]);


  return (
    <div className="flex gap-4">
      <p className="text-2xl font-bold">Psbt Signed:  {psbtSigned}</p>
        <button
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
          onClick={handleSignPsbt}
        >
          Sign BTC Psbt
        </button>
      </div>
  );
}

Last updated