test(amm): LP reward regression coverage for the 2026-07-21 findings - #4
test(amm): LP reward regression coverage for the 2026-07-21 findings#4OxToF wants to merge 3 commits into
Conversation
Isolated rewards-enabled pool (two throwaway mints). Covers, in-epoch: - happy path: a real depositor claims continuous oSOLA (was uncovered) - Finding B guard: a fresh wallet holding TRANSFERRED LP reverts NothingToClaim - Finding A guard: a fresh wallet banks zero checkpoint weight Full epoch-emission path (emit_pool_rewards -> claim_lp_emissions) needs a 7-day epoch warp this mocha/validator harness can't do — documented gap (bankrun). 37 passing on localnet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The mocha suite runs against a live cluster (Anchor.toml pins devnet), so it is stuck with the real clock and could only cover the in-epoch half of the LP reward system. The per-epoch half (checkpoint_lp -> emit_pool_rewards -> claim_lp_emissions) needs a 7-day boundary crossed, which is the documented gate on arming configure_emissions with initial > 0 on mainnet. bankrun's setClock() moves the epoch on demand. 7 tests, ~1s, no validator: - full cycle across a real epoch boundary: allocation is the undecayed emission in epoch 0, then decayed 1% in epoch 1, which pins decayed_emission too - replay of the same (user, pool, epoch) rejected. Worth noting the LpEpochClaim `init` is an ACCOUNT CONSTRAINT, so it fires during validation, before the body's NothingToClaim. The opposite assumption was made and disproved by the test. - invariant sum(claims) <= osola_allocated over a 2-LP epoch. The clamp itself does not bind: two honest LPs under-subscribe the pot, which is the safe direction. - a late depositor cannot bank a full epoch of weight (Finding A) - withdrawal decrements lp_amount, so it shrinks the reward basis - reward_basis follows the wallet balance DOWN, the reverse leg of Finding B that nothing covered: high recorded deposit but LP moved out must bank zero weight - a position recorded at lp_amount = 0 (legacy or transfer-acquired) can still withdraw Verified load-bearing by mutation: putting window_start back to epoch_start in checkpoint_lp makes the late-depositor test fail (weight/LP 1114620 vs 1114560, equality being the back-credit signature) while the other 6 stay green. A cycle test asserting only "gained > 0" passes on the bug. Tooling trap: bankrun cannot load an SBPFv3 binary. cargo build-sbf --arch v3, required for a devnet deploy, yields a .so rejected with the misleading "Program is not deployed". Build plain to test, re-add --arch v3 to deploy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sync found Two entries. The missing per-epoch coverage is written (bankrun), and a delivery defect was found on the way: the committed IDL carried lp_user_info for add_liquidity, claim_lp_rewards and remove_liquidity but NOT for checkpoint_lp, precisely the subject of 8e4454d. The production bundle was inspected and serves checkpoint_lp with 9 accounts while the deployed program expects 10. Nothing caught it because LpEmissions.tsx passes lpUserInfo through an `as any` cast, and the component is not mounted in page.tsx, so no tester could reach the path. Same failure mode as the "always rebuild the IDL" rule, one notch more devious: the IDL had been regenerated and partially committed. Check the IDL that is SERVED, not the local one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Parking this as a draft rather than closing it: it is kept as research, not as pending work. Status as of 2026-08-31. Most of what this branch proves has since landed on One case from this branch is still uncovered on The branch |
Follow-up to #3 — adds the test coverage that was entirely missing on the AMM LP-reward paths.
Isolated rewards-enabled pool (two throwaway mints, no interference with the existing SOLA/USDC / curve / POL / invariant tests). Covers, in-epoch:
claim_lp_rewards→ must revertNothingToClaim. This encodes the exact confirmed devnet exploit as a permanent regression test.checkpoint_lp→ banks zero weight, so the same LP walked through N wallets can no longer inflate the epoch pot.Documented gap
The full epoch-emission cycle (
emit_pool_rewards→claim_lp_emissions, incl. theosola_claimedcap) requires crossing a 7-day epoch boundary, which this mocha/validator harness can't warp. That path stays uncovered here and needs a bankrun-style clock — flagged before mainnet emission arming.Verification
anchor test --provider.cluster localnet→ 37 passing, 0 failing. Must run on localnet: the tests callconfigure_continuous_emissions, which writes global state and would clobber the live devnet config.🤖 Generated with Claude Code