{ "date": "2026-03-22", "issue": 517, "title": "Adversary parasitic LP extracts 29% from holder — all recenters fail", "scenario": "staker-vs-holder", "status": "fixed", "root_cause": { "summary": "PRICE_STABILITY_INTERVAL (300s) too long relative to MIN_RECENTER_INTERVAL (60s)", "detail": "After a large trade moving the tick >1000 positions, the 5-minute TWAP average lagged behind the current price by hundreds of ticks, far exceeding MAX_TICK_DEVIATION (50). Recenter reverted with 'price deviated from oracle' for ~285s after each trade, creating a window where the LM could not reposition. The adversary's parasitic LP captured fees during this unprotected window.", "revert_reasons": { "after_adversary_setup": "price deviated from oracle", "after_holder_buy": "price deviated from oracle", "after_adversary_attack": "price deviated from oracle", "after_holder_sell": "amplitude not reached" }, "johba_comment_confirmed": "Parasitic LP does not directly block recentering (V3 positions are independent). The revert is from the TWAP stability check, not from position interference." }, "fix": { "file": "onchain/src/abstracts/PriceOracle.sol", "change": "PRICE_STABILITY_INTERVAL reduced from 300 to 30 seconds", "rationale": "30s still prevents same-block manipulation (Ethereum mainnet ~12s block time) while ensuring TWAP converges well within the 60s cooldown. After the fix, recenter succeeds within 61s of any trade.", "security_impact": "Manipulation window reduced from 5 min to 30s. Attacker must hold manipulated price for 30+ seconds (2.5 blocks) before recenter accepts it. Combined with 60s cooldown, total manipulation window is <60s." }, "tests_added": [ "testRecenterAfterLargeBuy_TWAPConverges — verifies recenter works after 5 ETH buy + 61s wait", "testRecenterRejectsSameBlockManipulation — verifies TWAP check still blocks <30s manipulation", "testAdversarialLP_HolderProtected — full parasitic LP scenario, holder loss < 5%" ], "test_results": { "total": 256, "passed": 255, "failed": 1, "skipped": 0, "pre_existing_failure": "FitnessEvaluator.t.sol::testBatchEvaluate (requires FITNESS_MANIFEST_DIR env var)" } }