For the complete documentation index, see llms.txt. This page is also available as Markdown.

Read-Only Queries

Informational reads — positions, bond browsing, ratio quotes, and schedule helpers. No transactions.

Read-only queries cover the surface a wallet, explorer, or dashboard hits to render PoX-5 state without sending a transaction. The flows below group them by what they answer:

  • Positions — what a given address has locked, across STX-only and paired-bond legs.

  • Bond browsing — capacity, target APY, ratio, and the caller's allowlist entry for an upcoming bond.

  • Ratio quotes — required STX for a given BTC commitment under a bond's static ratio.

  • Schedule helpers — cycle, burn-height, and bond-period conversions for "X days until next bond opens" and re-lock window timing.

Read a staker's current position

A wallet or dashboard fetches everything needed to render "what does this address have locked?" in one shot.

import {
  fetchAccountStatus,
  fetchBondMembership,
  fetchPoxInfo,
  fetchStakerInfo,
} from '@stacks/bitcoin-staking';

const network = 'mainnet';
const address = user.stxAddress;

const [pox, account, stxOnly, bond] = await Promise.all([
  fetchPoxInfo({ network }),
  fetchAccountStatus({ address, network }),
  fetchStakerInfo({ address, network }),     // STX-only position (if any)
  fetchBondMembership({ address, network }), // paired-bond position (if any)
]);

const position = {
  burnHeight: pox.currentBurnchainBlockHeight,
  cycle: pox.rewardCycleId,
  liquidUstx: account.balance - account.locked,
  lockedUstx: account.locked,
  unlockHeight: account.unlockHeight,
  stxOnly: stxOnly.staked ? stxOnly.details : null,
  bond: bond ?? null,
};

fetchStakerInfo returns { staked: false } if the address has no STX-only lock, or { staked: true, details: { amountUstx, firstRewardCycle, numCycles, signer } }details is the decoded staker-info tuple. The details.signer field (pox-5.clar:224) is the principal a client passes as oldSignerManager to stake-update / unstake.

fetchBondMembership returns the decoded protocol-bond-memberships tuple — { bondIndex, amountUstx, signer, isL1Lock, amountSats }. amountSats is the BTC shares currently attributed to the membership — an L1 lockup total when isL1Lock is true, an sBTC contribution when false — and is the authoritative source for per-cycle bond share accounting. fetchProtocolBondMemberships reads the raw protocol-bond-memberships map entry and does not filter out expired memberships, unlike fetchBondMembership (which goes through get-bond-membership) — the latter returns undefined once the bond's unlock cycle is reached.

Browse an upcoming bond

A partner or pool operator inspects the next bond's parameters before enrolling: capacity, target APY, ratio, open burn height, and their own allowlist entry.

fetchBond returns the decoded protocol-bonds tuple — { bondIndex, targetRateBps, stxValueRatio, minUstxRatioBps, earlyUnlockBytes }. The earlyUnlockBytes field is the early-exit subscript embedded in the L1 lockup script (a pre-pushed Bitcoin script fragment that validates the early-exit key(s) signature and must leave a valid boolean result on the stack — consumed by the shared OP_VERIFY after OP_ENDIF — e.g. <pubkey> OP_CHECKSIG or an M-of-N OP_CHECKMULTISIG template). In practice every deployed bond uses the single-key form; the M-of-N template is permitted by the contract but unused. The openBurnHeight and firstRewardCycle are not stored on the bond — derive them with the per-bond conversions (bondPeriodToBurnHeight, bondPeriodToRewardCycle), which take { bondIndex, poxInfo } and read the first bond-period cycle off poxInfo.contractVersions internally (firstPox5RewardCycle(pox) exposes the same lookup for an explicit is-pox-5-active check).

fetchProtocolBond is the read-only wrapper around the same data; fetchBond reads the map entry directly. Both return the same shape.

Quote required STX for a BTC commitment

Before enrolling, a partner computes how much STX must be paired with a given sats amount under the bond's static ratio.

Schedule helper

Convert between bond index, reward cycle, and burn height. Useful for "X days until next bond opens", re-lock window timing, and indexer pagination.

bondPhaseRanges returns the bond's lifecycle as four named, burn-height-anchored ranges — open, locked, unlocked, finished — each { name, startBurnHeight, length, endBurnHeight } with an exclusive endBurnHeight (the next phase begins there). It is PoxInfo-pure: no fetches. open ends prepareCycleLength blocks before the bond's start height, because registration is blocked in the final prepare phase (ERR_STAKE_IN_PREPARE_PHASE) — so open.endBurnHeight is the practical registration cutoff. unlocked is the last rewardCycleLength / 2 blocks of the term (the re-lock window), and finished is capped at one bond term's worth of blocks as a UI convention.

bondStatus classifies the current burn height (poxInfo.currentBurnchainBlockHeight) into one of those phase names without assuming the bond exists on-chain. For a bond where setup-bond hasn't been called (isBondSetup: false) it resolves to too-early (before the setup window), eligible (inside the BOND_GAP_CYCLES setup window — the admin can setup-bond now), or missed (the start height passed without setup; this bond period can never run). fetchBondStatus is the fetching variant — it fetches poxInfo and the setup check (fetchProtocolBond) for whatever isn't injected:

The same boundaries can be derived by hand with the pure conversions (all take { ..., poxInfo }, no network):

  • bondPeriodToRewardCycle / bondPeriodToBurnHeight — a bond's first reward cycle and start burn height.

  • rewardCycleToBurnHeight — mirrors pox-5.reward-cycle-to-burn-height.

  • burnHeightToRewardCycle — mirrors pox-5.burn-height-to-reward-cycle; throws when burnHeight is below first-burnchain-block-height, mirroring the contract's runtime abort.

  • burnHeightToDistributionIndex — mirrors pox-5.burn-height-to-distribution-index; distribution cycles tick twice per reward cycle (every rewardCycleLength / 2 burn blocks).

The re-lock window at the end of every bond is when BTC unlocks before STX. On mainnet it is 1,050 blocks ≈ 7.3 days (pox-reward-cycle-length / 2, with pox-reward-cycle-length = 2100); on testnet it is 525. The contract's get-bond-l1-unlock-height(bondIndex) returns bondPeriodToBurnHeight(bondIndex + 6) - (pox-reward-cycle-length / 2); fetchBondL1UnlockHeight wraps it. This is the read to use for L1 timelock construction; the unlock-burn-height returned from register-for-bond / stake / stake-update is the start of the unlock cycle (a different value derived from reward-cycle-to-burn-height) and is only for indexing the membership's unlock schedule.

isInPreparePhase is the gate that register-for-bond, update-bond-registration, stake, stake-update, announce-l1-early-exit, and unstake-sbtc all hit (ERR_STAKE_IN_PREPARE_PHASE u47). unstake enforces the same window through its own separate check (ERR_UNSTAKE_IN_PREPARE_PHASE u28).

Earned rewards and protocol totals

The reward model splits "what is this signer owed?" into two raw on-chain reads, plus the get-earned aggregator that combines them (pox-5.clar:2341, pox-5.clar:3217, pox-5.clar:3203):

  • get-earned(signer, reward-cycle, bond-index) -> uint — pending + accrued, in sBTC sats. The single number a UI should display.

  • get-signer-unclaimed-rewards-for-cycle — running pending sBTC for { reward-cycle, bond-index, signer } since the last settlement (bond-index: (some N) for a bond cycle, none for STX-only).

  • get-signer-rewards-per-token-settled-for-cycle — last-settled rewards-per-token snapshot for the same key.

Staker-level rewards

For a signer manager paying individual stakers out of its slice, the staker-level layer mirrors the signer-level reads one level down (pox-5.clar:2358, pox-5.clar:3247, pox-5.clar:3231, pox-5.clar:3263):

  • get-earned-staker-rewards(signer, reward-cycle, bond-index, staker) -> uint — pending + accrued for a single staker under one signer.

  • get-staker-unclaimed-rewards-for-cycle — running pending sBTC for { reward-cycle, bond-index, signer, staker }.

  • get-staker-rewards-per-token-settled-for-cycle — staker's last-settled RPT snapshot.

  • get-signer-rewards-per-token-for-cycle — the signer's own RPT watermark for the cycle, used as the staker-side RPT reference. Returns 0n for signers that never crossed SIGNER_SET_MIN_USTX in the cycle.

fetchStakerCustodiedSbtc returns the sBTC the contract currently holds for a given staker (the source side of roll-sbtc). fetchBondOverlapsNewPosition wraps the on-chain overlap predicate and is the right call to gate a rollover UI without broadcasting.

For protocol-wide totals:

Was this helpful?