---
description: >-
  Request the signature and broadcast of a Partially Signed Bitcoin Transaction
  (PSBT)
---

# signPsbt

## Method name

`signPsbt`

## Parameters

<table><thead><tr><th width="169">Parameter</th><th width="348">Description</th><th width="96" data-type="checkbox">Required</th><th width="123">Type</th></tr></thead><tbody><tr><td>hex</td><td>Hex of PSBT payload for signing</td><td>true</td><td>string</td></tr><tr><td>allowedSighash</td><td>Sighash types allowed for signing and finalizing inputs (defaults to type ALL)</td><td>false</td><td>SignatureHash[]</td></tr><tr><td>signAtIndex</td><td>Input index(es) that should be signed (defaults to sign all inputs)</td><td>false</td><td>number | number[]</td></tr><tr><td>network</td><td>Network for signing: <code>mainnet</code>, <code>testnet</code>, <code>signet</code>, <code>sbtcDevenv</code> or <code>devnet</code></td><td>true</td><td>string</td></tr><tr><td>account</td><td>Index of account for signing (defaults to active account)</td><td>false</td><td>uint</td></tr><tr><td>broadcast</td><td>Whether to broadcast upon signing (default <code>false</code>). <code>txid</code>returned on successful broadcast.</td><td>false</td><td>boolean</td></tr></tbody></table>

## Example request

### Sign PSBT

<pre class="language-typescript"><code class="lang-typescript"><strong>interface SignPsbtRequestParams {
</strong>  hex: string;
  allowedSighash?: SignatureHash[];
  signAtIndex?: number | number[];
  network?: NetworkModes;         // default is user's current network
  account?: number;               // default is user's current account
  broadcast?: boolean;            // default is false - finalize/broadcast tx
}

const requestParams: SignPsbtRequestParams = { ...params };

const response = await window.LeatherProvider.request('signPsbt', requestParams);
</code></pre>

### Broadcast PSBT

Our API returns a PSBT back to the app, with the newly applied signatures. If your use-case is to spend the transaction immediately, you'll first need to `finalize` it, then broadcast it to the network.

See this example below how to extract a signed transaction from the PSBT

```typescript
import * as btc from '@scure/btc-signer';
const hex = response.result.hex;
const tx = btc.Transaction.fromPSBT(hexToBytes(hex));
tx.finalize();
await broadcast(tx.hex);
```

### Types and helper functions

```tsx
interface BitcoinNetwork {
  bech32: string;
  pubKeyHash: number;
  scriptHash: number;
  wif: number;
}

const bitcoinTestnet: BitcoinNetwork = {
  bech32: 'tb',
  pubKeyHash: 0x6f,
  scriptHash: 0xc4,
  wif: 0xef,
};

const ecdsaPublicKeyLength = 33;

function ecdsaPublicKeyToSchnorr(pubKey: Uint8Array) {
  if (pubKey.byteLength !== ecdsaPublicKeyLength) throw new Error('Invalid public key length');
  return pubKey.slice(1);
}

function getTaprootPayment(publicKey: Uint8Array) {
  return btc.p2tr(ecdsaPublicKeyToSchnorr(publicKey), undefined, bitcoinTestnet);
}
```

### Helper functions for Native SegWit `PsbtRequestOptions`

```tsx
// Example request options for Native SegWit
function buildNativeSegwitPsbtRequest(pubKey: Uint8Array): PsbtRequestOptions {
  // SegWit pubKey used here is provided through auth response
  const p2wpkh = btc.p2wpkh(pubKey, bitcoinTestnet);

  const tx = new btc.Transaction();

  // Example inputs with included witness
  tx.addInput({
    index: 0,
    txid: '15f34b3bd2aab555a003cd1c6959ac09b36239c6af1cb16ff8198cef64f8db9c',
    witnessUtxo: {
      amount: BigInt(1000),
      script: p2wpkh.script,
    },
  });
  tx.addInput({
    index: 1,
    txid: 'dca5179afaa63eae112d8a97794de2d30dd823315bcabe6d8b8a6b98e3567705',
    witnessUtxo: {
      amount: BigInt(2000),
      script: p2wpkh.script,
    },
  });
  
  // Add outputs and/or other psbt data...

  const psbt = tx.toPSBT();

  // This example is choosing to sign all inputs bc no index(es) are provided
  return { hex: bytesToHex(psbt) };
}
```

### Helper functions for Taproot `PsbtRequestOptions`

```tsx
// Similar example request options for Taproot
function buildTaprootPsbtRequest(pubKey: Uint8Array): PsbtRequestOptions {
  // Taproot pubKey used here is provided through auth response
  const payment = getTaprootPayment(pubKey);

  const tx = new btc.Transaction();

  tx.addInput({
    index: 0,
    tapInternalKey: payment.tapInternalKey,
    txid: '15f34b3bd2aab555a003cd1c6959ac09b36239c6af1cb16ff8198cef64f8db9c',
    witnessUtxo: {
      amount: BigInt(1000),
      script: payment.script,
    },
  });
  tx.addInput({
    index: 1,
    tapInternalKey: payment.tapInternalKey,
    txid: 'dca5179afaa63eae112d8a97794de2d30dd823315bcabe6d8b8a6b98e3567705',
    witnessUtxo: {
      amount: BigInt(2000),
      script: payment.script,
    },
  });
  
  // Add outputs and/or other psbt data...

  const psbt = tx.toPSBT();

  return { hex: bytesToHex(psbt) };
}
```

## Custom scripts

For more complex PSBTs, such as those using custom Bitcoin scripts, we recommend you read the supporting documentation of the `@scure/btc-signer` library.

[Bitcoin script examples →](https://github.com/paulmillr/scure-btc-signer?tab=readme-ov-file#script)

## Debugging

If you're running into issues signing a PSBT with Leather, the best way to debug the problem is to sign it without Leather.

Starting with a test-only mnemonic, drive a BTC address, and try sign the transaction. This helps ensure your PSBT is constructed correctly.

Here's a [Codesandbox with a PSBT debugging environment](https://codesandbox.io/p/sandbox/debug-leather-psbt-signing-x2349s) set up for you to test. When asking for PSBT signing support, provide a test case forked from this.&#x20;

## Preview

<figure><img src="https://2085786563-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F41U87FfDFo2dIKYr0ZNX%2Fuploads%2FxzH4T2l5x5nWpxS0NnFT%2FSCR-20240419-qtxp.png?alt=media&#x26;token=c6d5f052-801d-4e08-afe4-b4bc5e734c1b" alt=""><figcaption></figcaption></figure>
