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

# Sign transaction inputs

> Sign Bitcoin transaction inputs using Privy raw sign functionality for UTXO-based transactions

<Info>
  With Privy, you can create Bitcoin segwit and taproot wallets and sign over transactions and any
  other arbitrary input. See the [Tier 2](/wallets/overview/chains) section for more details.
</Info>

## Signing transaction inputs

Bitcoin uses the UTXO model, where each transaction consumes one or more inputs and produces one or more outputs. To sign a transaction using Privy, use Privy's raw sign functionality to sign each input hash, and then add the signature(s) to the transaction.

## Segwit

Segwit (SegWit v0) wallets use the ECDSA signing algorithm with the secp256k1 curve. Use Privy's raw sign functionality to sign each input UTXO for your Bitcoin segwit transaction.

### Example

The following is an example using the [scure/btc-signer](https://github.com/paulmillr/scure-btc-signer) library to sign a segwit transaction input.

```typescript {skip-check} theme={"system"}
import {p2pkh, OutScript} from '@scure/btc-signer/payment';
import {getInputType, getPrevOut, Transaction} from '@scure/btc-signer/transaction';
import {concatBytes} from '@scure/btc-signer/utils';
import secp256k1 from 'secp256k1';

const publicKey = "<the wallet's public key>";

const publicKeyBuffer = Buffer.from(publicKey, 'hex');
const tx = new Transaction({version: 1, allowLegacyWitnessUtxo: true});
// add as many outputs as needed, in this example there is only one
tx.addOutputAddress(outputAddress, outputAmount);

tx.addInput({
  txid, // buffer of utxo txid
  index: 0, // index of the output in the tx
  witnessUtxo: {
    amount: inputAmount,
    script: p2pkh(publicKeyBuffer).script
  }
});

// Loop through each input and sign it
for (let i = 0; i < tx.inputsLength; i++) {
  const input = tx.getInput(i);
  const inputType = getInputType(input, tx.opts.allowLegacyWitnessUtxo);
  const prevOut = getPrevOut(input);
  let script = inputType.lastScript;
  if (inputType.last.type === 'wpkh') {
    script = OutScript.encode({type: 'pkh', hash: inputType.last.hash});
  }
  const hash = tx.preimageWitnessV0(i, script, inputType.sighash, prevOut.amount);

  const signature = ''; // call Privy's raw sign function with bytesToHex(hash), returns '0x...'

  // convert to DER format
  const derSig = secp256k1.signatureImport(signature);

  // update the input with the signature
  tx.updateInput(
    i,
    {
      partialSig: [[publicKeyBuffer, concatBytes(derSig, new Uint8Array([inputType.sighash]))]]
    },
    true
  );
}

// finalize the transaction. It is now ready to be sent!
tx.finalize();
```

## Taproot

Taproot (SegWit v1) wallets use the Schnorr signing algorithm (BIP-340) with the secp256k1 curve. Privy automatically applies the BIP-341 key tweak when signing with a taproot wallet, so the resulting signature is valid for key-path spends against the wallet's P2TR output.

<Warning>
  Privy's taproot support uses key-path spending only. The applied tweak assumes no custom Merkle
  roots or scripts, so script-path spends (e.g., custom Tapscript trees) are not supported at this
  time.
</Warning>

### Example

The following is an example using the [scure/btc-signer](https://github.com/paulmillr/scure-btc-signer) library to sign a taproot transaction input. Note that taproot uses `preimageWitnessV1` for the BIP-341 sighash and attaches the 64-byte Schnorr signature as `tapKeySig` (rather than `partialSig` as with segwit).

```typescript {skip-check} theme={"system"}
import {p2tr} from '@scure/btc-signer/payment';
import {Transaction, getInputType, getPrevOut} from '@scure/btc-signer/transaction';
import {bytesToHex} from 'viem';

// The wallet's `public_key` from the Privy Wallet object (33-byte compressed secp256k1 key, hex)
const publicKey = wallet.public_key;

// Extract the 32-byte x-only public key by stripping the first byte (compression prefix)
const pubKeyBytes = Buffer.from(publicKey, 'hex');
const xOnlyPubKey = pubKeyBytes.subarray(1);

// Build the P2TR output for this wallet
const taprootOutput = p2tr(xOnlyPubKey);

const tx = new Transaction({allowLegacyWitnessUtxo: true});

// Add outputs (as many as needed)
tx.addOutputAddress(outputAddress, outputAmount);

// Add a taproot input
tx.addInput({
  txid, // buffer of UTXO txid
  index: 0, // index of the output in the funding tx
  witnessUtxo: {
    amount: inputAmount, // must match the UTXO amount exactly
    script: taprootOutput.script
  },
  tapInternalKey: xOnlyPubKey
});

// Loop through each input and sign it
for (let i = 0; i < tx.inputsLength; i++) {
  const input = tx.getInput(i);
  const inputType = getInputType(input, true);
  const prevOut = getPrevOut(input);

  // Compute the BIP-341 sighash
  const sighash = tx.preimageWitnessV1(i, [prevOut.script], inputType.sighash, [prevOut.amount]);

  const signature = ''; // call Privy's raw sign function with bytesToHex(sighash), returns '0x...'

  // Decode the 64-byte Schnorr signature and attach it as tapKeySig
  const signatureBytes = Buffer.from(signature.slice(2), 'hex');
  tx.updateInput(i, {tapKeySig: signatureBytes}, true);
}

// Finalize the transaction. finalize() validates the Schnorr signature
// against the tweaked output key and throws if the signature is invalid.
tx.finalize();
```
