fix: Investigate: adversary parasitic LP extracts 29% from holder, all recenters fail (#517)

Root cause: PRICE_STABILITY_INTERVAL (300s) was too long relative to
MIN_RECENTER_INTERVAL (60s). After any significant trade moving the tick
>1000 positions, the 5-minute TWAP lagged behind the current price by
hundreds of ticks, exceeding MAX_TICK_DEVIATION (50). Recenter reverted
with "price deviated from oracle" for ~285s — creating a window where
the LM could not reposition and adversary parasitic LP could extract
value from passive holders.

Fix: Reduce PRICE_STABILITY_INTERVAL from 300s to 30s. This ensures
TWAP converges within the 60s cooldown while still preventing same-block
manipulation (30s > ~12s Ethereum mainnet block time).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
johba 2026-03-22 19:45:35 +00:00
parent 367652ec94
commit 937f2a833b
9 changed files with 333 additions and 145 deletions

View file

@ -26,7 +26,7 @@ import { console2 } from "forge-std/console2.sol";
*
* Access model: recenter() is permissionless no special access grant is required.
* EventReplayer advances block.timestamp via vm.warp, so the 60-second cooldown and
* the 300-second TWAP window pass normally during simulation.
* the 30-second TWAP window pass normally during simulation.
*
* TODO(#319): The negligible-impact assumption means we replay historical events
* as-is without accounting for KrAIken's own liquidity affecting swap outcomes.
@ -258,7 +258,11 @@ contract StrategyExecutor {
internal
view
{
console2.log(string.concat("=== Recenter #", totalRecenters.str(), " @ block ", blockNum.str(), " direction=", isBootstrap ? "BOOTSTRAP" : (isUp ? "UP" : "DOWN"), " ==="));
console2.log(
string.concat(
"=== Recenter #", totalRecenters.str(), " @ block ", blockNum.str(), " direction=", isBootstrap ? "BOOTSTRAP" : (isUp ? "UP" : "DOWN"), " ==="
)
);
console2.log(string.concat(" Floor pre: tick [", int256(fLoPre).istr(), ", ", int256(fHiPre).istr(), "] liq=", uint256(fLiqPre).str()));
console2.log(string.concat(" Anchor pre: tick [", int256(aLoPre).istr(), ", ", int256(aHiPre).istr(), "] liq=", uint256(aLiqPre).str()));
console2.log(string.concat(" Disc pre: tick [", int256(dLoPre).istr(), ", ", int256(dHiPre).istr(), "] liq=", uint256(dLiqPre).str()));