PM-AMM hardening: resolve all audit findings#29
Open
Conversation
29 files from lvr_amm EVM contracts, Solana program, and test. PM-core contracts (DuelOutcomeOracle, GoldClob, fight_oracle, gold_clob_market) unchanged.
C-1: declare isDynamic as immutable state variable in LvrMarket C-2: add AccessControl + onlyRole(DEFAULT_ADMIN_ROLE) to setFeeConfig, 10% fee cap H-1: add minAmountOut slippage protection to all buy/sell functions H-2: add z-score bounds validation in SwapMath entry point H-3: add ReentrancyGuard (nonReentrant) to all Router buy/sell functions G-7: fix getPriceYes to use time-decayed liquidity matching _swap execution Also: market allowlist on Router callbacks, 6 exploit regression tests
Rewrites entire math.rs to use Q64.64 fixed-point (i128) arithmetic. Removes libm dependency entirely. - Polynomial erf approximation (Abramowitz & Stegun 7.1.28) - Taylor series exp(-x) with repeated squaring - Integer sqrt via Newton's method - Gaussian CDF/PDF from erf, not f64 stdlib - All public API unchanged (u64 scaled by 1e6) - 6 unit tests including symmetry and determinism verification Solana validators will now produce identical results regardless of hardware floating-point implementation.
…pplied fees (G-2, G-4, G-6) - Add AmmConfig PDA (singleton): authority, treasury, market_maker, fee_bps, config_frozen, paused - Add initialize_config, freeze_config, set_paused instructions - create_bet now reads fee_bps and treasury from AmmConfig, not caller inputs - Add pause guard on create_bet - Add error variants: InvalidAddress, FeeTooHigh, MarketPaused, ConfigFrozen - Removes per-market fee/treasury injection attack vector
Cherry-pick deploy-amm.ts and amm-test-helpers.ts from feat/amm-swap-fees, updated for hardened interfaces: - Router constructor now takes 4 params (mUSD, treasury, feeBps, admin) - Solana create_bet reads fee/treasury from AmmConfig PDA - Test helpers gain ensureAmmConfig() for protocol config setup
Thread AmmConfig PDA into Buy and Sell account contexts. Both instructions now check !amm_config.paused before executing, matching create_bet's existing pause guard and EVM Router parity.
EVM: settleFromOracle() reads DuelOutcomeOracle.getDuel() to resolve market from oracle state. Handles Resolved and Cancelled statuses. Solana: settle_bet accepts optional duel_state account from fight_oracle. Winner deserialized from oracle at known byte offsets rather than trusting caller side_won argument.
get_price now calls calc_liquidity for dynamic markets, matching the execution path in buy/sell. Prevents price read/execution divergence that could be exploited for arbitrage.
This was referenced Mar 20, 2026
…tion - Math.calcLiquidity: return 0 when currentTime >= deadline instead of reverting on uint underflow. getPriceYes/getPriceNo/getMarketDetails now work post-deadline. - LvrMarket.getPriceYes: return neutral 0.5e18 when liquidity is zero. - LvrMarket constructor: require(duration > 0) to prevent permanently locked zero-duration markets. Fixes H3 (zero-duration lock) and H4 (post-deadline view revert).
- adminResolve: slash proposer bond to treasury when disputed and wrong, return bond only if proposer's outcome matches admin's resolution. - sell: burn fee tokens instead of transferring to treasury. Prevents outstanding token supply from exceeding collateral backing (solvency invariant). - constructor: require(duration > 0) to prevent zero-duration markets. - getPriceYes: return neutral 0.5e18 when liquidity is zero post-deadline. Fixes C5 (sell solvency), H2 (bond slashing), H3 (duration), H4 (price view).
Sell fees were transferred as outcome tokens to treasury ATAs, inflating outstanding token supply relative to collateral. Now fees are burned, maintaining the solvency invariant that total redeemable tokens never exceed collateral in the bet PDA. Treasury and treasury ATA accounts removed from the Sell instruction since fee tokens are burned rather than transferred. Fixes H7 (Solana sell solvency) and H5 (treasury ATA validation).
New tests: - test_SellFeeBurnsSolvency: totalSupply <= 2*collateral after sells - test_AdminSlashesBondOnWrongProposal: bond → treasury on wrong dispute - test_AdminReturnsBondOnCorrectProposal: bond returned if proposer right - test_ZeroDurationReverts: duration=0 rejected at creation - test_GetPriceAfterDeadline: view functions return neutral, not revert Also fixes two pre-existing test failures in redeem tests by using static markets (avoids ZScoreOutOfBounds from dynamic liquidity scaling) and approving both token types for the redeem callback.
anchor build -p lvr_amm to sync the IDL and TypeScript types with the updated Sell instruction that no longer requires treasury/treasury ATA accounts.
- Router: add allowedMarkets check to proposerOutcome, dispute, and settleMarket for consistency with buy/sell/settleFromOracle (M-NEW-1) - LvrMarket.settleFromOracle: slash proposer bond when oracle outcome disagrees with proposal, matching adminResolve behavior (M-NEW-3) - Solana Bet: add full 32-byte duel_key field. settle_bet compares all 32 bytes when set, falling back to 8-byte bet_id match for backward compatibility (H-NEW-1) - create_bet: accept duel_key parameter and store in Bet account
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.
Summary
Changes
EVM (Router + LvrMarket):
isDynamicimmutable state var (C-1)settleFromOracle()reads DuelOutcomeOracle for trustless resolution (G-4)Solana (lvr_amm program):
Tooling:
deploy-amm.tsupdated for hardened 4-param Router constructoramm-test-helpers.tsupdated for AmmConfig PDA flowTest plan
forge test— 57/57 pass (9 AMM + 48 PM-core)anchor build— Solana IDL regeneration