buildLockScript
Builds the L1 lockup witness script for a paired-BTC bond, byte for byte as pox-5's construct-lockup-script builds it. Pure computation: no network call.
Usage
import {
buildLockScript,
buildUnlockScript,
computeBondUnlockHeight,
fetchBond,
fetchPoxInfo,
} from '@stacks/bitcoin-staking';
const bondIndex = 2;
const poxInfo = await fetchPoxInfo({ network: 'mainnet' });
const bond = await fetchBond({ bondIndex, network: 'mainnet' });
if (!bond) throw new Error(`bond ${bondIndex} is not set up`);
const lockScript = buildLockScript({
stxAddress: 'SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7',
unlockHeight: computeBondUnlockHeight({ bondIndex, poxInfo }), // 994700 for mainnet bond 2
unlockBytes: buildUnlockScript(
'02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc'
),
earlyUnlockBytes: bond.earlyUnlockBytes,
});Notes
The script is
OP_IF <unlockHeight> OP_CHECKLOCKTIMEVERIFY OP_ELSE OP_SIZE <32> OP_EQUALVERIFY OP_SHA256 <H> OP_EQUALVERIFY <earlyUnlockBytes> OP_ENDIF OP_VERIFY <unlockBytes>(construct-lockup-script).<H>issha256(sha256(to-consensus-buff? staker)). The early-exit (OP_ELSE) branch reveals its preimage, which computeRegisterPreimage computes.unlockBytesruns last in both branches.unlockBytesandearlyUnlockBytesare spliced in raw, without a push prefix, so each must be a complete subscript that leaves a boolean on the stack.Throws if
unlockBytesis empty or does not decode as Bitcoin script.earlyUnlockBytesgoes through validateEarlyUnlockBytes, with the shape check controlled byopts.validateEarlyUnlockBytes.Throws if
unlockHeightis 0, negative, or at or above BITCOIN_LOCKTIME_THRESHOLD. Height 0 encodes asOP_0, which leaves an empty value that the sharedOP_VERIFYreads as false, so the locktime branch could never be spent.pox-5 accepts any unlock height at or above the bond's minimum and below 500,000,000. A height below the minimum fails
register-for-bondwithERR_INVALID_UNLOCK_HEIGHT (u52)(L2074-L2078). computeBondUnlockHeight returns the minimum.Keep the script. buildReclaim needs it to spend the lockup, and buildLockProof accepts it as
lockScript. To fund the lockup, derive the address with buildLockAddress, or use buildRegisterMetadata to get the script, address and output script in one call.
Signature
Returns
Uint8Array
The witness script. Its P2WSH hash is what the funding output must pay.
Parameters
opts.stxAddress (required)
Type:
string
The staker's Stacks principal, standard or contract. register-for-bond rebuilds the script with its transaction sender as the staker, so this must be the address that sends that transaction.
opts.unlockHeight (required)
Type:
IntegerType
The Bitcoin block height at which the OP_CHECKLOCKTIMEVERIFY branch becomes spendable. A number, bigint or numeric string.
opts.unlockBytes (required)
Type:
Uint8Array | string
The staker subscript, as bytes or hex. Usually the output of buildUnlockScript.
opts.earlyUnlockBytes (required)
Type:
Uint8Array | string
The bond's early-unlock subscript, as bytes or hex. Read it from fetchBond.
opts.validateEarlyUnlockBytes (optional)
Type:
boolean
Set to false to skip the shape check on earlyUnlockBytes. The structural check still runs. Defaults to true.
Was this helpful?