Skip to content

fix(wzrd-rails): M-03 emission remainder-carry + StakePool realloc migration#116

Merged
twzrd-sol merged 1 commit into
mainfrom
claude/m03-pool-migration-cp
Jun 22, 2026
Merged

fix(wzrd-rails): M-03 emission remainder-carry + StakePool realloc migration#116
twzrd-sol merged 1 commit into
mainfrom
claude/m03-pool-migration-cp

Conversation

@twzrd-sol

Copy link
Copy Markdown
Owner

Summary

  • Adds reward_remainder: u128 to StakePool — captures sub-lamport CCM emission that was previously silently discarded each epoch
  • Grows StakePool::LEN 61 → 77 bytes (with LEGACY_LEN = 61 constant for migration guard)
  • Adds realloc_stake_pool admin instruction that safely migrates legacy 61-byte pools to 77 bytes
  • Uses UncheckedAccount for the pool account (typed Account<StakePool> fails deserialization against the old 61-byte layout before realloc applies)

Audit Finding

M-03 from the wzrd-rails security audit. Remainder loss accumulates proportionally with staker count and reward rate.

Deploy Runbook (MANDATORY — funds-touching)

This is a two-step upgrade. Pool must be migrated before any staking operations resume.

  1. Deploy new binary (id.json upgrade authority, 4.838 SOL buffer)
  2. Admin calls realloc_stake_pool { pool_id: 0 } against pool 6oQDChd9...
    • Verifies pool is now 77 bytes, reward_remainder = 0
  3. Resume normal operations

Do NOT stake/unstake/claim/fund between steps 1 and 2.

Test Coverage

  • 33 unit tests (state size, seed constants, math invariants)
  • 58 litesvm integration tests — all passing including:
    • realloc_stake_pool_migrates_legacy_pool_full_path
    • realloc_stake_pool_is_idempotent_on_fresh_pool
    • realloc_stake_pool_rejects_non_admin
  • 1 e2e test

Total: 92 tests passing, 0 failures

Notes

  • Cherry-picked from claude/m03-pool-migration onto current main (squash-merge desync — original branch based on pre-squash 7cbf580)
  • Binary grows 717k → 731k

Co-Authored-By: twzrd-sol 33047129+twzrd-sol@users.noreply.github.com

…gration

The MasterChef accrual floored per-slot emissions to zero once total_staked
exceeded ~1M base units, permanently burning rewards and stranding the matching
CCM in reward_vault. The live pool (pool_id=0, 6oQDChd9...) has 3.1M CCM staked
with emissions ON, so this bug is actively truncating real rewards every slot.

The fix carries the truncation remainder forward across accruals (numerator =
slots*rate*SCALE + reward_remainder; increment = numerator/total; remainder =
numerator%total), making accrual cadence-independent and conserving emissions.

This requires a new reward_remainder:u128 field on StakePool (LEN 61 -> 77).
Because Anchor deserializes a typed Account<StakePool> BEFORE applying a realloc
constraint (verified against anchor-syn 0.32.1 codegen), the migration IX
realloc_stake_pool takes the pool as a raw UncheckedAccount, validates owner +
discriminator + PDA manually, tops up rent, and resizes 61->77 with zero-init
(reward_remainder = 0 genesis). It is idempotent (already-77 -> Ok) and
admin-gated via Config has_one.

The realloc IX and the 77-byte struct ship in the SAME binary, as required:
once deployed, the seven IXs that load Account<StakePool> cannot read the old
61-byte pool until it is migrated.

DEPLOY RUNBOOK (mandatory order):
1. Deploy this binary.
2. Admin runs realloc_stake_pool(pool_id=0) against 6oQDChd9... BEFORE any
   stake/unstake/claim/update_pool/set_reward_rate/fund_reward_pool. Those six
   revert on the un-migrated pool until step 2 completes. Staking is briefly
   unavailable between step 1 and step 2 — this is expected.
3. Verify the pool account is 77 bytes on-chain; reward_remainder reads 0.

Test proof: realloc_stake_pool_migrates_legacy_pool_full_path installs a raw
61-byte pool with the live values (3.1M CCM, rate 1000), asserts update_pool
FAILS pre-migration, runs the migration, confirms 77 bytes + all legacy fields
preserved bit-for-bit + reward_remainder=0 + accrual carries the dust. Plus
idempotency and non-admin-rejection tests. Full suite: 92 passed, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@twzrd-sol twzrd-sol merged commit d441724 into main Jun 22, 2026
3 checks passed
@twzrd-sol twzrd-sol deleted the claude/m03-pool-migration-cp branch June 22, 2026 07:46
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.

1 participant