Using P2SH

How to use P2SH in OP_NET

P2SH addresses in OP_NET are primarily used for legacy contract interactions and compatibility with older Bitcoin infrastructure. The LegacyCalldataGenerator class creates redeem scripts that encode contract calldata into a P2SH-compatible format.

Note

P2SH has a 520-byte script size limit. For larger calldata payloads, consider using P2WSH which supports scripts up to 10,000 bytes.

Generating a Redeem Script

The following example demonstrates how to generate a P2SH redeem script for OP_NET contract interactions. The generateRedeemScript function creates a redeem script from calldata and returns the P2SH address along with the script components.

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

export interface RedeemScript {
    address: string;
    p2shOutputScript: Buffer;
    redeemScript: Buffer;
}

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

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

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

    const p2sh = bitcoin.payments.p2sh({
        redeem: { output: redeemScript },
        network,
    });

    const outputRedeem: Buffer | undefined = p2sh.redeem?.output;
    if (!outputRedeem) {
        throw new Error('Output redeem is required');
    }

    console.log('P2SH Address:', p2sh.address);
    console.log('P2SH Redeem Script (hex):', outputRedeem.toString('hex'));

    if (!p2sh.address) {
        throw new Error('P2SH address is required');
    }

    const p2shOutputScript: Buffer | undefined = p2sh?.redeem?.output;
    if (!p2shOutputScript) {
        throw new Error('No redeem output');
    }

    return {
        address: p2sh.address,
        p2shOutputScript,
        redeemScript: redeemScript,
    };
}

Understanding the Components

BinaryWriter

The BinaryWriter class is used to construct the calldata that will be encoded into the redeem script. The writeSelector() method writes a function selector (identifying which contract function to call), and writeBytes() writes the function parameters.

LegacyCalldataGenerator

The LegacyCalldataGenerator takes the sender's public key and network configuration. The compile() method generates the redeem script from the calldata, including:

  • The calldata buffer containing the function selector and parameters.
  • The sender's public key for authentication.
  • An optional interaction script (empty buffer in this example).
  • The transaction value as a bigint.
  • Additional parameters as an array.

bitcoin.payments.p2sh

The bitcoin.payments.p2sh() function from the @btc-vision/bitcoin library constructs the P2SH payment object. It takes the compiled redeem script and generates the corresponding P2SH address and output scripts.

Validating P2SH Addresses

Use the AddressVerificator class to validate P2SH addresses before use.

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

const isValid = AddressVerificator.isP2SHAddress(p2shAddress, networks.bitcoin);
console.log('P2SH valid:', isValid); // true