buildUnstake
Builds an unsigned contract call to pox-5 unstake, which ends the sender's STX-only stake after the current reward cycle. The STX unlocks at the start of the next cycle.
Usage
import {
buildUnstake,
fetchEligibleUnstake,
fetchStakerInfo,
} from '@stacks/bitcoin-staking';
import {
Pc,
TransactionSigner,
broadcastTransaction,
fetchNonce,
privateKeyToAddress,
privateKeyToPublic,
publicKeyToHex,
} from '@stacks/transactions';
const network = 'mainnet';
const privateKey = process.env.STAKER_KEY!;
const staker = privateKeyToAddress(privateKey, network);
const info = await fetchStakerInfo({ address: staker, network });
if (!info.staked) throw new Error('no active STX-only stake');
const oldSignerManager = info.details.signer;
const check = await fetchEligibleUnstake({ staker, oldSignerManager, network });
if (!check.ok) throw new Error(`unstake would fail: ${check.reasons}`);
const tx = await buildUnstake({
oldSignerManager,
publicKey: publicKeyToHex(privateKeyToPublic(privateKey)),
fee: 10_000n, // micro-STX
nonce: await fetchNonce({ address: staker, network }),
network,
postConditions: [Pc.principal(staker).willPerformPox()],
});
new TransactionSigner(tx).signOrigin(privateKey);
const result = await broadcastTransaction({ transaction: tx, network });Notes
Send it during the reward phase of any cycle, which excludes its last
prepareCycleLengthBitcoin blocks (100 on mainnet), the prepare phase. In the prepare phase it fails withERR_UNSTAKE_IN_PREPARE_PHASE (u28), a different code from theu47thatstakeandstake-updateuse (pox-5.clar L1423-L1470). Check with isInPreparePhase.Fails with
ERR_NOT_STAKING (u27)without an active STX-only stake, andERR_INVALID_OLD_SIGNER_MANAGER (u36)whenoldSignerManageris not the recorded signer-manager.fetchEligibleUnstake dry-runs every check.
pox-5 keeps the stake's
amount-ustx, rewritesnum-cyclesso the stake ends with the current reward cycle, and removes the sender from the signer-manager's later cycles. The sender stays in the current cycle.unstakeis a position-altering PoX action for the sender (pox_5.rs). In the defaultdenymode, and inoriginatormode, the node aborts the transaction unless it carries a PoX post condition for the sender, such asPc.principal(staker).willPerformPox()(node check). pox-5unstaketransfers no asset, so no other post condition is needed.A txid from broadcastTransaction confirms submission only. The call can still fail on-chain: read the mined transaction's result and pass an
(err uN)to parsePox5Error.
Signature
The transaction fields come from TxParams.
Returns
Promise<StacksTransactionWire>
Resolves to an unsigned StacksTransactionWire that calls pox-5 unstake. Sign it with TransactionSigner, then send it with broadcastTransaction.
Parameters
args.oldSignerManager (required)
Type:
string
Contract principal of the signer-manager recorded for the stake. fetchStakerInfo returns it as details.signer.
args.fee (required)
Type:
IntegerType
Transaction fee in micro-STX.
args.nonce (required)
Type:
IntegerType
Nonce of the origin account. Read it with fetchNonce.
args.network (required)
Type:
StacksNetworkName | StacksNetwork
A name such as 'mainnet' or 'testnet', or a StacksNetwork object. Sets the contract address (the network's boot address, SP000000000000000000002Q6VF78 on mainnet) and the transaction version and chain ID.
args.publicKey (required for a single-sig origin)
Type:
string
Hex public key of the account that signs the transaction.
args.publicKeys and args.numSignatures (required for a multisig origin)
Type:
PublicKey[]andnumber
Pass these instead of publicKey to build for an M-of-N multisig origin. This package defaults useNonSequentialMultiSig to true; pass false for the sequential hash mode. Pass address to check the key order against the multisig address.
args.postConditions (optional)
Type:
PostCondition[]
Post conditions to attach. The builder adds none.
args.postConditionMode (optional)
Type:
PostConditionModeName
'allow', 'deny' or 'originator'. Defaults to 'deny'.
Was this helpful?