feat: pox-5 reward claims, STX-staking rewards, and principal staking endpoints#2582
Merged
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
As an application developer building pox-5 staking UIs, I want a principal's staking rewards — what they've accrued and claimed — and their staking positions available through the API, for bond (sBTC) staking and STX staking alike, without expensive on-read aggregation.
This PR ingests pox-5 reward claims and the STX-staking reward stream (on top of the bond reward accrual that already existed), materializes the per-principal staking totals, and restructures the principal staking endpoints.
Motivation. The
@stacks/codecupgrade made reward claims fully attributable:claim-staker-rewards-for-signeridentifies the staker (and bond, ornullfor STX staking), andclaim-rewardscarries the per-signer aggregate. STX-staking rewards (paid in sBTC bycalculate-rewards) previously had no per-staker materialization. The original single staking endpoint also conflated a paginated list (bond positions, each with lifecycle status) with a singleton (STX staking) under a/balances/path, and computed per-principal totals withSUM/COUNTon every read.What changed
Reward ingestion
claim-staker-rewards-for-signerwith abond_indexis recorded inprincipal_bond_reward_claimsand rolled intoprincipal_bond_positions.claimed_rewards.calculate-rewardsevent's STX-staker pool is split across current pox-5 STX lockers by locked weight (floor(locked_ustx × accrued_rewards_per_ustx / 1e18)) intoprincipal_stx_reward_distributionssource rows + the running per-principal total;claim-staker-rewards-for-signerevents with anullbond_indexfeed the claimed side.claim-rewardsis recorded insigner_reward_claims(STX + per-bond breakdown + totals) as audit/bookkeeping only.ft_balances/ft_events); the audit aggregate only flips its canonical flag.Materialized per-principal staking summary
principal_staking_totalstable — one row per principal holding the STX-staking reward totals (stx_accrued_rewards,stx_claimed_rewards) and a rollup of the principal's bond positions (bond_count,bond_btc_locked,bond_stx_locked,bond_accrued_rewards,bond_claimed_rewards). Maintained incrementally at the same delta sites that update the per-bondbondsaggregate, so the staking summary is a single-row lookup — noSUM/COUNTon read (the bond-positions list endpoint also sources itstotalfrombond_count).stx_locked_balances(expiry is burn-height-driven with no event at expiry), consistent with/balances/stx.Endpoints — the old
GET /extended/v3/principals/:principal/balances/staking(a bare array that mixed bond positions and STX staking) is replaced by two resources under/staking:GET /extended/v3/principals/:principal/staking— one-call summary:{ "stx": { "locked": "50000000", "rewards": { "btc": { "accrued": "8000", "claimed": "0", "claimable": "8000" } } }, "bonds": { "count": 3, "locked": { "btc": "5000", "stx": "30000000" }, "rewards": { "btc": { "accrued": "12000", "claimed": "4000", "claimable": "8000" } } } }GET /extended/v3/principals/:principal/staking/bonds— cursor-paginated bond positions (bybond_index), each flattened so status/enrollment are peers of the amounts:{ "bond_index": 0, "status": "running", "active": true, "enrollment": { "tx_id": "0x…", "btc_lockup": { "amount": "1000" } }, "locked": { "btc": "1000", "stx": "10000000" }, "rewards": { "btc": { "accrued": "2000", "claimed": "1500", "claimable": "500" } } }STX-staking rewards are paid in sBTC, hence
rewards.btcin both shapes.How this impacts application developers
A principal's staking is now two clean resources: a singleton summary and a paginated bond-positions list. Reward totals (accrued/claimed/claimable) are available per bond position, per STX-staking position, and as per-principal aggregates.
Type of Change
Does this introduce a breaking change?
Yes, on the unreleased pox-5 line (no released API surface affected):
GET /extended/v3/principals/:principal/balances/stakingis removed, replaced byGET .../staking(summary) andGET .../staking/bonds(paginated positions).balances.{locked,rewards}wrapper is gone —status,active,enrollment,locked, andrewardsare now top-level, and the flataccrued_rewardsis replaced byrewards.btc.{accrued,claimed,claimable}.Are documentation updates required?
openapi.yamlis regenerated in this PR (the generated client schema is left to release automation). External docs may want the new shapes once pox-5 ships.Testing information
tests/api/pox5/bonds.test.ts(no live chain): bond reward accrual + claim, STX-staking accrual + claim + expiry, the signer-claim aggregate, the materialized summary aggregate (multi-position + distribute→claim→reorg), bond-positions cursor pagination, and reorg-revert for all of it. 39 pox5 API tests pass.markEntitiesCanonicalreorg blocks inpg-write-store.ts; the v3 staking read path; the staking schema/serializer/routes.principal_bond_reward_claimstable with anullbond_index;principal_staking_totalsis maintained by signed deltas, so its invariants depend on every position-mutating site applying the matching delta.DB tables added/changed
principal_staking_totals(new) — materialized per-principal staking summary (STX reward totals + bond rollup).principal_bond_reward_claims(new) — per-staker claim ledger (bond and, withnullbond_index, STX-staking claims).principal_stx_reward_distributions(new) — STX-staking accrual source rows.signer_reward_claims(new) — per-signer claim aggregate (audit).principal_bond_positions(changed) — addedclaimed_rewardscolumn.