Skip to content

feat(stacking): move the stacking skill to pox-5 - #430

Merged
biwasxyz merged 2 commits into
mainfrom
feat/stacking-pox5
Oct 8, 2026
Merged

biwasxyz merged 2 commits into
mainfrom
feat/stacking-pox5

Conversation

@biwasxyz

Copy link
Copy Markdown
Contributor

Closes #429. Skills-side counterpart of aibtcdev/aibtc-mcp-server#682 (part of aibtc-mcp-server#677).

Why

pox-5 has been the active PoX contract on mainnet since cycle 141 and removed every function the stacking skill called. #424 made writes refuse; this adds real pox-5 staking.

What

Ported from aibtc-mcp-server#682, which I checked against mainnet before porting: its stake, stake-update and unstake arguments and post-conditions match real successful transactions, and its reward-claim post-conditions match the Xverse signer manager's payout code.

Subcommand pox-5 call
get-pox-info get-pox-info + burn height: cycle, next cycle start, prepare phase
get-stacking-status get-staker-info, get-bond-membership, locked/unlocked balance
list-signers (new) walks the signer set for a cycle; optional claim style per manager
stack-stx stake(signer-manager, amount, num-cycles, start-burn-ht, signer-calldata)
extend-stacking stake-update: extend, increase, switch signer, change payout calldata
unstake-stx (new) unstake
get-rewards (new) staker unclaimed + manager unpulled for a cycle
claim-rewards (new) pulls for the manager if needed, then claim-staker-rewards

stack-stx / extend-stacking flags change: signer manager instead of a PoX BTC address and burn height. Optional --btc-reward-address (+ --max-withdrawal-fee-sats) encodes {pox-addr, max-fee} calldata; --signer-calldata-hex passes raw calldata.

Differences from the MCP version

Also

  • pillar-direct: direct-stack-stx --pool fast-pool and direct-revoke-fast-pool refuse while pox-4 is not active (the Pillar wallet contract hardcodes pox-4; the real fix is on Pillar's side). The Stacking DAO path is unchanged.
  • @stacks/* → ^7.6.0 (staking / pox post-condition types). The lockfile change is limited to those packages.

Verification

  • Mainnet reads: get-pox-info (cycle 143, prepare phase from 968350); list-signers → 26 signers for cycle 144 (same as the MCP PR); get-stacking-status for a real Xverse staker → 500 STX, unlock burn height 1170050, matching Hiro's burnchain_unlock_height; get-rewards → claim style staker-arg.
  • src/lib/services/stacking.service.test.ts: the 16 MCP tests ported, plus 2 for the active-contract guard (verified to fail with the guard removed).
  • CLI validation paths (amount 0, 97 cycles, both payout options, bad cycle) error before the wallet loads; Fast Pool commands refuse on mainnet before touching keys.
  • bun run typecheck, bun run validate; stacking/stacking.ts and pillar/pillar-direct.ts type-checked separately (the repo's typecheck only covers src/).
  • Full bun test (Bun 1.4.2): 180 pass / 38 fail vs main 162 / 38. The 38 failures are the existing cross-file mock leak; x402 files pass alone (72/72) on 7.6.0.
  • Not exercised live: no transaction was broadcast. A small testnet stake → unstake before relying on it is worthwhile, as with the MCP PR.

🤖 Generated with Claude Code

biwasxyz and others added 2 commits October 8, 2026 17:26
pox-5 is the active PoX contract on mainnet (cycle 141+) and removed every
pox-4 function the skill called. Port the pox-5 staking implementation from
aibtcdev/aibtc-mcp-server#682:

- StackingService rebuilt on pox-5: stake / stake-update / unstake with a
  signer manager, signer-set walk, claim-style detection, and sBTC reward
  pull + staker claim. Deny-mode post-conditions (staking lock amount,
  performs-PoX, sBTC only out of pox-5 / the manager). Pre-checks refuse
  before signing: prepare phase, already/not staking, unregistered signer,
  balance, 1-96 cycles.
- Added over the MCP version: every write confirms pox-5 is still the
  network's active PoX contract, and a dependency seam replaces module mocks
  in tests.
- CLI: get-pox-info, get-stacking-status, list-signers, stack-stx,
  extend-stacking, unstake-stx, get-rewards, claim-rewards; strict argument
  parsing; optional BTC payout calldata.
- pillar-direct: Fast Pool stack and revoke refuse while pox-4 is not
  active (the Pillar wallet contract hardcodes pox-4).
- btcAddressToPoxAddr in src/lib/utils/bitcoin.ts; POX_5 replaces POX_4.
- @stacks/* bumped to ^7.6.0 for the staking and pox post-condition types.

Closes #429.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…pin claim nonces

Review follow-ups for the pox-5 port, verified against mainnet pox-5 and
the live signer-manager contracts:

- get-rewards / claim-rewards: pox-5's get-earned-staker-rewards reads the
  signer's rewards-per-token, which only advances when the manager pulls, so
  it reported 0 and claim-rewards refused before ever sending the pull.
  Project the claimable amount with pox-5's compute-earned-rewards over the
  cycle's global rewards-per-token.
- extend-stacking: stake-update forwards calldata to the manager's
  validate-stake!, and Xverse/Fast Pool delete the stored BTC payout address
  when it is none. Require --btc-reward-address, --signer-calldata-hex or an
  explicit --sbtc-payout.
- claim-rewards: pin consecutive nonces for the pull + claim pair.
- Take the sBTC contract for reward post-conditions from /v2/pox
  pox_5_sbtc_contract (differs from the default sBTC on testnet).
- Refuse writes within 3 burn blocks of the prepare phase.
- Error messages name CLI subcommands, not MCP tool names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@biwasxyz
biwasxyz force-pushed the feat/stacking-pox5 branch from 8a9036c to 01665a6 Compare October 8, 2026 11:51
@biwasxyz

biwasxyz commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Review: rebased onto main (skills.json regenerated). Verified every pox-5 call against the live mainnet contract, its interface, 6 signer-manager contracts and ~450 real stake/stake-update/unstake txs: function names, arg order, calldata encoding (byte-identical to a real Xverse stake), pox-addr versions, post-conditions (Deny everywhere), cycle math and the fail-closed pox-5 guard all check out. Live mainnet reads match Hiro (status for a 12,000 STX staker: lock + unlock height 1172150 exact).

Fixed in 01665a6:

  1. get-rewards / claim-rewards reported 0 and refused to claim until the manager pulled — get-earned-staker-rewards reads the signer's rewards-per-token, which only advances on the pull. Now projects via pox-5's compute-earned-rewards over the cycle's global rpt. Live: xverse-3 staker SP3WJ7…ZC7F cycle 144 now shows 336 sats (was 0).
  2. extend-stacking silently deleted the BTC payout address — Xverse/Fast Pool validate-stake! map-delete the pox-addr when calldata is none (planbetter rejects it). Now requires --btc-reward-address / --signer-calldata-hex / explicit --sbtc-payout.
  3. claim pull + claim could collide on nonce — consecutive nonces pinned.
  4. Testnet claims always aborted — reward post-conditions now use /v2/pox pox_5_sbtc_contract.
  5. 3-block margin before the prepare phase; error messages name CLI subcommands, not MCP tools.

+4 tests (22/22 stacking, 196 in src/lib). Known, not fixed: stake during an sBTC-bond rollover would abort on the uncovered refund (costs the fee, no loss); balance checks don't reserve the tx fee. No live broadcast was done — a small testnet stake → unstake is still worth running.

@biwasxyz
biwasxyz merged commit e9e886c into main Oct 8, 2026
5 checks passed
@biwasxyz
biwasxyz deleted the feat/stacking-pox5 branch October 8, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stacking: real pox-5 staking, and unguarded pox-4 Fast Pool path in pillar-direct

1 participant