> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign a Partially Signed Bitcoin Transaction (PSBT) - Embedded Wallets

<Card title="Recommended: JavaScript SDK with React Hooks" icon="react" color="#4779FE">
  For new React apps, we recommend the JavaScript SDK with React Hooks (`@dynamic-labs-sdk/react-hooks`) instead of the legacy React SDK documented here. The JS SDK comes with many benefits such as a much smaller bundle size and other optimizations. Use the [React quickstart (JavaScript SDK)](/javascript/reference/react-quickstart) to get started.
</Card>

# Signing PSBTs (Partially Signed Bitcoin Transactions) for Bitcoin Embedded Wallets

This guide explains how to use the `signPsbt` method to sign Partially Signed Bitcoin Transactions (PSBTs) for Bitcoin embedded wallets. The method signs only the inputs related to the user's wallet and returns a signed (but not finalized) PSBT for review and finalization.

## Overview

PSBTs allow multiple parties to collaboratively build and sign Bitcoin transactions. The `signPsbt` method:

* **Signs only inputs** that belong to the user's wallet address
* **Does NOT finalize** the PSBT - it remains partially signed for review
* **Returns a signed PSBT** that can be reviewed, combined with other signatures, and finalized by the user

## Method Signature

```typescript theme={"system"}
// From BitcoinWallet with embedded wallet (simplified interface)
const signedPsbt = await wallet.signPsbt(request: EmbeddedWalletSignPsbtRequest): Promise<BitcoinSignPsbtResponse>;
```

## Type Definitions

### For Embedded Wallets

```typescript theme={"system"}
type EmbeddedWalletSignPsbtRequest = {
  unsignedPsbtBase64: string; // The unsigned PSBT in Base64 format (only field required)
};

type BitcoinSignPsbtResponse = {
  signedPsbt: string; // The signed (but not finalized) PSBT in Base64 format
};
```

**Important**: Embedded wallets:

* Only require `unsignedPsbtBase64` - no other parameters needed
* Always use SIGHASH\_ALL (0x01) - not configurable for now
* Automatically sign all inputs that belong to the wallet address
* Do not support the `signature` parameter for now

## Important Notes

### PSBT is NOT Finalized

The `signPsbt` method **does NOT finalize** the PSBT. It only signs the inputs that belong to the user's wallet. The returned PSBT:

* Contains signatures for the user's inputs
* Remains in PSBT format (not a finalized transaction)
* Can be reviewed by the user before finalization
* Can be combined with signatures from other parties
* Must be finalized before broadcasting

### Signing Only User-Related Inputs

The method automatically signs all inputs that belong to the user's wallet address. You cannot specify which inputs to sign - it signs all of them automatically.

### 🔍 User Review and Finalization

After signing, the PSBT should be:

1. **Reviewed** by the user to verify transaction details
2. **Combined** with other signatures if it's a multi-party transaction
3. **Finalized** using a Bitcoin library (e.g., `bitcoinjs-lib`)
4. **Broadcast** to the Bitcoin network

## Usage Examples

### Basic PSBT Signing for Embedded Wallets

This example signs all inputs that belong to the user's embedded wallet:

```tsx theme={"system"}
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';
import { EmbeddedWalletSignPsbtRequest } from '@dynamic-labs/bitcoin';

const MyComponent = () => {
  const { primaryWallet } = useDynamicContext();

  const handleSignPsbt = async (unsignedPsbtBase64: string) => {
    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) {
      throw new Error('Bitcoin wallet not found');
    }

    try {
      // For embedded wallets, only unsignedPsbtBase64 is required
      // It automatically signs all inputs belonging to the wallet using SIGHASH_ALL
      const request: EmbeddedWalletSignPsbtRequest = {
        unsignedPsbtBase64: unsignedPsbtBase64,
      };

      const { signedPsbt } = await primaryWallet.signPsbt(request);

      console.log('Signed PSBT (not finalized):', signedPsbt);
      // The PSBT is signed but not finalized - user should review and finalize
      return signedPsbt;
    } catch (error) {
      console.error('Error signing PSBT:', error);
      throw error;
    }
  };

  return (
    <button onClick={() => handleSignPsbt('your-unsigned-psbt-base64')}>
      Sign PSBT
    </button>
  );
};
```

### Complete Example with Review and Finalization

This example shows the complete flow: signing, reviewing, and finalizing a PSBT:

```tsx theme={"system"}
import { FC, useState } from 'react';
import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';
import { Psbt } from 'bitcoinjs-lib';
import { networks } from 'bitcoinjs-lib';

export const SignPsbtExample: FC = () => {
  const { primaryWallet } = useDynamicContext();
  const [unsignedPsbt, setUnsignedPsbt] = useState<string>('');
  const [signedPsbt, setSignedPsbt] = useState<string>('');
  const [error, setError] = useState<string>('');
  const [isLoading, setIsLoading] = useState<boolean>(false);

  const handleSignPsbt = async () => {
    if (!unsignedPsbt.trim()) {
      setError('Please enter an unsigned PSBT');
      return;
    }

    if (!primaryWallet || !isBitcoinWallet(primaryWallet)) {
      setError('Bitcoin wallet not found');
      return;
    }

    setIsLoading(true);
    setError('');
    setSignedPsbt('');

    try {
      // Sign the PSBT (does not finalize)
      // For embedded wallets, only unsignedPsbtBase64 is required
      const { signedPsbt: result } = await primaryWallet.signPsbt({
        unsignedPsbtBase64: unsignedPsbt.trim(),
      });

      setSignedPsbt(result);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to sign PSBT');
    } finally {
      setIsLoading(false);
    }
  };

  const handleReviewPsbt = () => {
    if (!signedPsbt) {
      setError('No signed PSBT to review');
      return;
    }

    try {
      // Parse the signed PSBT for review
      const psbt = Psbt.fromBase64(signedPsbt, { network: networks.bitcoin });

      // Extract transaction information
      const inputs = psbt.txInputs.map((input, index) => ({
        index,
        hash: input.hash.reverse().toString('hex'),
        index: input.index,
      }));

      const outputs = psbt.txOutputs.map((output, index) => ({
        index,
        address: output.address,
        value: output.value,
      }));

      console.log('PSBT Review:', {
        inputs,
        outputs,
        isFinalized: psbt.finalized,
      });

      // Show review UI to user
      alert(
        `PSBT Review:\nInputs: ${inputs.length}\nOutputs: ${outputs.length}\nFinalized: ${psbt.finalized}`,
      );
    } catch (err) {
      setError('Failed to parse PSBT for review');
    }
  };

  const handleFinalizePsbt = () => {
    if (!signedPsbt) {
      setError('No signed PSBT to finalize');
      return;
    }

    try {
      // Parse the signed PSBT
      const psbt = Psbt.fromBase64(signedPsbt, { network: networks.bitcoin });

      // Finalize all inputs
      psbt.finalizeAllInputs();

      // Extract the finalized transaction
      const finalizedTx = psbt.extractTransaction();

      // Get the transaction hex for broadcasting
      const txHex = finalizedTx.toHex();

      console.log('Finalized transaction hex:', txHex);
      alert(
        'PSBT finalized! Transaction hex: ' + txHex.substring(0, 50) + '...',
      );

      // The transaction can now be broadcast to the Bitcoin network
      return txHex;
    } catch (err) {
      setError(
        'Failed to finalize PSBT: ' +
          (err instanceof Error ? err.message : String(err)),
      );
    }
  };

  return (
    <div className='sign-psbt-example'>
      <h2>Sign PSBT</h2>

      <div>
        <label htmlFor='unsigned-psbt'>Unsigned PSBT (Base64):</label>
        <textarea
          id='unsigned-psbt'
          value={unsignedPsbt}
          onChange={(e) => setUnsignedPsbt(e.target.value)}
          placeholder='Enter unsigned PSBT in Base64 format'
          rows={5}
          style={{ width: '100%', margin: '10px 0' }}
        />
      </div>

      <button
        onClick={handleSignPsbt}
        disabled={isLoading || !unsignedPsbt.trim()}
        style={{ margin: '10px 0' }}
      >
        {isLoading ? 'Signing...' : 'Sign PSBT'}
      </button>

      {signedPsbt && (
        <div>
          <h3>Signed PSBT (Not Finalized)</h3>
          <textarea
            value={signedPsbt}
            readOnly
            rows={5}
            style={{ width: '100%', margin: '10px 0' }}
          />
          <div>
            <button onClick={handleReviewPsbt} style={{ margin: '10px 5px' }}>
              Review PSBT
            </button>
            <button onClick={handleFinalizePsbt} style={{ margin: '10px 5px' }}>
              Finalize PSBT
            </button>
          </div>
        </div>
      )}

      {error && <div style={{ color: 'red', margin: '10px 0' }}>{error}</div>}
    </div>
  );
};
```

## Signature Hash Types

Embedded wallets **always use SIGHASH\_ALL (0x01)** - this is not configurable. You don't need to (and cannot) specify `allowedSighash` for embedded wallets.

## Embedded Wallet Specific Notes

For embedded wallets:

1. **Simplified Interface**: Only `unsignedPsbtBase64` is required - no other parameters needed:

   ```typescript theme={"system"}
   await wallet.signPsbt({
     unsignedPsbtBase64: psbt,
   });
   ```

2. **Automatic Signing**: The method automatically signs **all inputs** that belong to the wallet address. You cannot specify which inputs to sign.

3. **Fixed SIGHASH**: Embedded wallets always use **SIGHASH\_ALL (0x01)** - this is not configurable.

4. **No Signature Parameter**: The `signature` parameter is not supported (TypeScript will prevent you from using it).

5. **MFA Support**: The signing process may require MFA authentication if enabled.

## Error Handling

Common errors and how to handle them:

### Invalid PSBT Format

```typescript theme={"system"}
try {
  // For embedded wallets
  await wallet.signPsbt({
    unsignedPsbtBase64: 'invalid-psbt',
  });
} catch (error) {
  // Handle invalid PSBT format
  console.error('Invalid PSBT format:', error);
}
```

## Best Practices

1. **Always Review Before Finalization**: Parse and display the PSBT details to the user before finalization
2. **Validate PSBT Format**: Check that the PSBT is valid before attempting to sign
3. **Handle Errors Gracefully**: Wrap `signPsbt` calls in try-catch blocks
4. **Use Simplified Interface**: Only provide `unsignedPsbtBase64` - no need for `allowedSighash` or `signature`
5. **Don't Finalize Automatically**: Let the user review and finalize the PSBT manually
6. **Store Signed PSBTs Securely**: Signed PSBTs contain sensitive information - handle them securely

## PSBT Workflow

The typical PSBT workflow is:

1. **Build PSBT**: Create an unsigned PSBT with transaction details
2. **Sign PSBT**: Use `signPsbt` to sign inputs belonging to the user
3. **Review PSBT**: Parse and display transaction details for user review
4. **Combine Signatures**: If multi-party, combine with other signatures
5. **Finalize PSBT**: Convert the PSBT to a finalized transaction
6. **Broadcast Transaction**: Send the finalized transaction to the Bitcoin network

## Additional Resources

* [Dynamic Labs Documentation](https://dynamic.xyz/docs)
* [BIP 174 - Partially Signed Bitcoin Transaction Format](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)
* [bitcoinjs-lib PSBT Documentation](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/psbt.ts)
* [Create a PSBT](/react/wallets/using-wallets/bitcoin/create-a-psbt) guide for testing
* [Sign a PSBT (External Wallets)](/react/wallets/using-wallets/bitcoin/sign-a-psbt) for external wallet flows
