Using P2OP

How to use P2OP in OP_NET

P2OP addresses are used exclusively for OP_NET smart contract addresses. The Address class provides a static p2op() method to generate P2OP addresses from contract bytes.

Note

P2OP addresses use a custom HRP (Human-Readable Part) defined in the network configuration as bech32Opnet. Ensure your network configuration includes this property.

Generating a P2OP Address

Use the static Address.p2op() method to generate a P2OP address from contract bytes. The method accepts the contract bytes, network configuration, and an optional deployment version.

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

// Contract bytes (e.g., from deployment or contract identifier)
const contractBytes = Buffer.from('your-contract-identifier', 'utf8');

// Generate P2OP address for mainnet
const p2opAddress = Address.p2op(contractBytes, networks.bitcoin);
console.log('P2OP Address:', p2opAddress);
// Output: opnet1s...

// Generate P2OP address for testnet
const p2opTestnet = Address.p2op(contractBytes, networks.testnet);
console.log('P2OP Testnet:', p2opTestnet);
// Output: optest1s...

// Generate P2OP address with custom deployment version
const p2opV1 = Address.p2op(contractBytes, networks.bitcoin, 1);
console.log('P2OP Version 1:', p2opV1);

Method Signature

The p2op() method has the following signature:

Method signature for Address.p2op():
typescript
public static p2op(
    bytes: Buffer | Uint8Array,
    network: Network = networks.bitcoin,
    deploymentVersion: number = 0,
): string

Parameters

  • bytes: The contract identifier bytes as a Buffer or Uint8Array.
  • network: The Bitcoin network configuration (default: networks.bitcoin).
  • deploymentVersion: The deployment version byte (default: 0).

Returns

Returns the Bech32m-encoded P2OP address string.

Internal Implementation

The P2OP address generation follows these steps internally:

The following shows the internal implementation logic:
typescript
// 1. Create witness program with deployment version prefix
const witnessProgram = Buffer.concat([
    Buffer.from([deploymentVersion]),
    bitcoin.crypto.hash160(Buffer.from(bytes)),
]);

// 2. Validate witness program length (2-40 bytes required)
if (witnessProgram.length < 2 || witnessProgram.length > 40) {
    throw new Error('Witness program must be 2-40 bytes.');
}

// 3. Compile script with OP_16 (SegWit version 16)
const scriptData = script.compile([opcodes.OP_16, witnessProgram]);

// 4. Encode using Bech32m with custom OP_NET HRP
return fromOutputScript(scriptData, network);

Network Configuration

P2OP addresses require the network to have a bech32Opnet property defined. This custom HRP ensures P2OP addresses are distinguishable from standard Bitcoin addresses.

Example network configuration with bech32Opnet:
typescript
const customNetwork: Network = {
    ...networks.bitcoin,
    bech32Opnet: 'opnet',  // Custom HRP for P2OP addresses
};
Warning

If the network does not have bech32Opnet defined, the toFutureOPNetAddress() encoding function will throw an error: "Network does not support opnet".

Bech32m Encoding for P2OP

P2OP uses the toFutureOPNetAddress() function which handles the Bech32m encoding for SegWit versions 2-16:

The encoding function handles future SegWit versions:
typescript
import { bech32m } from 'bech32';

export function toFutureOPNetAddress(output: Buffer, network: Network): string {
    if (!Buffer.isBuffer(output)) throw new TypeError('output must be a Buffer');
    if (!network.bech32Opnet) throw new Error('Network does not support opnet');

    const opcode = output[0];

    // Parse the push-data to extract the witness program
    let pushPos = 1, progLen: number;
    if (output[1] < 0x4c) {
        progLen = output[1];
        pushPos = 2;
    } else if (output[1] === 0x4c) {
        progLen = output[2];
        pushPos = 3;
    } else {
        throw new TypeError('Unsupported push opcode in script');
    }

    const program = Buffer.from(output.subarray(pushPos, pushPos + progLen));

    // Determine SegWit version from opcode
    const version =
        opcode === opcodes.OP_0
            ? 0
            : opcode >= opcodes.OP_1 && opcode <= opcodes.OP_16
              ? opcode - (opcodes.OP_1 - 1)
              : -1;

    // Encode with Bech32m and custom HRP
    const words = [version, ...bech32m.toWords(program)];
    return bech32m.encode(network.bech32Opnet, words);
}

Use Cases

P2OP addresses are primarily used for:

  • Contract Deployment: Generating deterministic addresses for deployed contracts.
  • Contract Interaction: Specifying the target contract in transaction outputs.
  • Contract Discovery: Sharing readable contract addresses with users.
  • Cross-Version Compatibility: The deployment version allows for future contract format upgrades.