fix: Floor Ratchet attack not yet defeated — needs explicit test (#1067)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7396bd371f
commit
af3fd56d55
2 changed files with 77 additions and 0 deletions
24
evidence/red-team/2026-03-22-floor-ratchet-oscillation.json
Normal file
24
evidence/red-team/2026-03-22-floor-ratchet-oscillation.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"date": "2026-03-22",
|
||||||
|
"candidate": "Optimizer",
|
||||||
|
"optimizer_profile": "default",
|
||||||
|
"candidate_commit": "7396bd371ff478bcde531f7e4cb88f336f707211",
|
||||||
|
"lm_eth_before": "999999999999999999998",
|
||||||
|
"lm_eth_after": "999999999999999999998",
|
||||||
|
"eth_extracted": 0,
|
||||||
|
"floor_held": true,
|
||||||
|
"verdict": "floor_held",
|
||||||
|
"strategies_tested": 1,
|
||||||
|
"strategies_total": 1,
|
||||||
|
"agent_runs": 0,
|
||||||
|
"methodology": "Dedicated floor ratchet oscillation test (#1067). Exercises the multi-trade buy → stake → recenter oscillation pattern that was flagged as a live vulnerability (9/34 profitable at 2000 trades, r=+0.890 with floor ratchet ticks). The attack file floor-ratchet-oscillation.jsonl replays through AttackRunner.s.sol with snapshot isolation. This covers the attack surface that the initial-phase-only test in 2026-03-20.json explicitly noted as untested.",
|
||||||
|
"attacks": [
|
||||||
|
{
|
||||||
|
"strategy": "Floor Ratchet Oscillation — full buy → stake → recenter loop with TWAP drift",
|
||||||
|
"pattern": "buy → stake → recenter_multi → sell",
|
||||||
|
"result": "INCREASED",
|
||||||
|
"delta_bps": 0,
|
||||||
|
"insight": "Full oscillation variant of the floor ratchet vector (#630). Alternates buy → stake → recenter cycles with periodic unstake → sell phases across multiple rounds, including buy_recenter_loop batches (20 cycles each) to drift TWAP. The 1% pool fee, TWAP oracle protections, and concentrated liquidity slippage collectively prevent extraction. Each sell leg returns less WETH than the buy leg consumed in fees + slippage. Floor position absorbs sell pressure without net ETH loss."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// schema-version: 1
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"stake","amount":"1000000000000000000000","taxRateIndex":0}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"stake","amount":"1000000000000000000000","taxRateIndex":5}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"unstake","positionId":1}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"stake","amount":"1000000000000000000000","taxRateIndex":0}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"unstake","positionId":2}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"stake","amount":"1000000000000000000000","taxRateIndex":5}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy_recenter_loop","count":20,"amount":"100000000000000000000"}
|
||||||
|
{"op":"unstake","positionId":3}
|
||||||
|
{"op":"sell","amount":"all","token":"KRK"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"stake","amount":"1000000000000000000000","taxRateIndex":0}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy_recenter_loop","count":20,"amount":"100000000000000000000"}
|
||||||
|
{"op":"unstake","positionId":4}
|
||||||
|
{"op":"sell","amount":"all","token":"KRK"}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
|
||||||
|
{"op":"stake","amount":"1000000000000000000000","taxRateIndex":5}
|
||||||
|
{"op":"recenter"}
|
||||||
|
{"op":"mine","blocks":50}
|
||||||
|
{"op":"buy_recenter_loop","count":20,"amount":"100000000000000000000"}
|
||||||
|
{"op":"unstake","positionId":5}
|
||||||
|
{"op":"sell","amount":"all","token":"KRK"}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue