Using P2WSH

How to use P2WSH in OP_NET

P2WSH (Pay-to-Witness-Script-Hash) is the SegWit equivalent of P2SH, providing lower transaction fees and support for larger scripts up to 10,000 bytes. In OP_NET, P2WSH is used for contract interactions that require larger calldata payloads than P2SH can accommodate.

Tip

P2WSH addresses start with 'bc1q' on mainnet and 'tb1q' on testnet. They use Bech32 encoding which provides better error detection than Base58Check.

Generating a Witness Script

The following example demonstrates how to generate a P2WSH witness script for OP_NET contract interactions. Similar to P2SH, the LegacyCalldataGenerator creates the script, but wrapped in a SegWit witness structure.

The following example shows how to generate a P2WSH witness script:
typescript
import bitcoin, { Network } from '@btc-vision/bitcoin';
import { Address, BinaryWriter, LegacyCalldataGenerator } from '@btc-vision/transaction';

export interface WitnessScript {
    address: string;
    witnessScript: Buffer;
    scriptPubKey: Buffer;
}

// Create calldata with a function selector and parameters
const calldata = new BinaryWriter();
calldata.writeSelector(1);
calldata.writeBytes(Buffer.alloc(500)); // Larger payload supported

export function generateWitnessScript(address: Address, network: Network): WitnessScript {
    const senderPubKey = address.originalPublicKeyBuffer();
    const calldataGenerator = new LegacyCalldataGenerator(senderPubKey, network);

    const witnessScript = calldataGenerator.compile(
        Buffer.from(calldata.getBuffer()),
        senderPubKey,
        Buffer.alloc(0),
        0n,
        [],
    );

    const p2wsh = bitcoin.payments.p2wsh({
        redeem: { output: witnessScript },
        network,
    });

    if (!p2wsh.address) {
        throw new Error('P2WSH address is required');
    }

    if (!p2wsh.output) {
        throw new Error('P2WSH output script is required');
    }

    console.log('P2WSH Address:', p2wsh.address);
    console.log('Witness Script (hex):', witnessScript.toString('hex'));

    return {
        address: p2wsh.address,
        witnessScript: witnessScript,
        scriptPubKey: p2wsh.output,
    };
}

P2SH vs P2WSH Comparison

Understanding when to use P2SH versus P2WSH depends on your requirements:

Feature P2SH P2WSH
Script Size Limit 520 bytes 10,000 bytes
Address Prefix (Mainnet) 3 bc1q
Transaction Fees Higher Lower (SegWit discount)
Encoding Base58Check Bech32
Legacy Compatibility Full Requires SegWit support

Validating P2WSH Addresses

Use the AddressVerificator class to validate P2WSH addresses before use.

The following example shows how to validate a P2WSH address:
typescript
import { AddressVerificator } from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';

const isValid = AddressVerificator.isP2WSHAddress(p2wshAddress, networks.bitcoin);
console.log('P2WSH valid:', isValid); // true

Nested SegWit (P2SH-P2WSH)

For maximum compatibility, you can wrap P2WSH inside P2SH. This allows older wallets that don't support native SegWit to still send to SegWit-enabled addresses.

The following example shows how to create a nested P2SH-P2WSH address:
typescript
const p2wsh = bitcoin.payments.p2wsh({
    redeem: { output: witnessScript },
    network,
});

const p2shWrapped = bitcoin.payments.p2sh({
    redeem: p2wsh,
    network,
});

console.log('Nested P2SH-P2WSH Address:', p2shWrapped.address);
// Output: 3... (P2SH format with SegWit benefits)