Merge pull request 'fix: fix: il-crystallization-80 attack times out (153 steps) (#597)' (#616) from fix/issue-597 into master

This commit is contained in:
johba 2026-03-12 16:26:03 +01:00
commit d84ff5f762
2 changed files with 56 additions and 158 deletions

View file

@ -127,9 +127,12 @@ interface IUniswapV3Factory {
* DEPLOYMENTS_FILE Path to deployments JSON (default: deployments-local.json)
*
* Supported operations:
* buy Swap WETHKRK via SwapRouter. Fields: amount (wei string), token (ignored, WETH assumed)
* sell Swap KRKWETH via SwapRouter. Fields: amount (wei string or "all"), token (ignored)
* recenter Call LM.recenter() via recenterAccess account. Emits a snapshot.
* buy Swap WETHKRK via SwapRouter. Fields: amount (wei string), token (ignored, WETH assumed)
* sell Swap KRKWETH via SwapRouter. Fields: amount (wei string or "all"), token (ignored)
* recenter Call LM.recenter() via recenterAccess account. Emits a snapshot.
* buy_recenter_loop Batch N×(buyrecenter) cycles in a single op. Fields: count (uint), amount (wei string).
* Emits a snapshot after each successful recenter. Avoids per-step forge overhead for
* high-cycle attacks that would otherwise time out (e.g. il-crystallization-80).
* stake Call Stake.snatch(). Fields: amount (wei string), taxRateIndex (raw taxRate value passed to Stake.snatch)
* unstake Call Stake.exitPosition(). Fields: positionId
* mint_lp Add LP via NPM. Fields: tickLower, tickUpper, amount0 (wei string), amount1 (wei string)
@ -179,6 +182,9 @@ contract AttackRunner is Script {
/// recenter_is_up=null on the initial snapshot (before any recenter has occurred)
/// rather than the ambiguous false default.
bool internal _hasRecentered;
/// @dev Snapshot sequence counter. Shared between run() and batch ops like
/// buy_recenter_loop that emit their own snapshots internally.
uint256 internal _seq;
// Entry point
@ -207,18 +213,18 @@ contract AttackRunner is Script {
// Execute attack operations, snapshotting after each recenter.
string memory attackFile = vm.envString("ATTACK_FILE");
uint256 seq = 1;
_seq = 1;
string memory line = vm.readLine(attackFile);
while (bytes(line).length > 0) {
bool isRecenter = _execute(line);
if (isRecenter) {
_logSnapshot(seq++);
_logSnapshot(_seq++);
}
line = vm.readLine(attackFile);
}
// Final state snapshot.
_logSnapshot(seq);
_logSnapshot(_seq);
}
// Setup
@ -272,6 +278,8 @@ contract AttackRunner is Script {
_executeMintLp(line);
} else if (_eq(op, "burn_lp")) {
_executeBurnLp(line);
} else if (_eq(op, "buy_recenter_loop")) {
_executeBuyRecenterLoop(line);
} else if (_eq(op, "mine")) {
uint256 blocks = vm.parseJsonUint(line, ".blocks");
vm.roll(block.number + blocks);
@ -298,6 +306,47 @@ contract AttackRunner is Script {
vm.stopBroadcast();
}
/**
* @dev Batch buyrecenter loop. Executes `count` cycles of (buy `amount` WETH recenter),
* emitting a snapshot after each successful recenter. Using a single op instead of
* individual JSONL lines eliminates per-step forge-script dispatch overhead, allowing
* high-cycle attacks (e.g. 80 cycles) to complete within the fitness evaluation budget.
*
* Fields: count (uint), amount (wei string)
*/
function _executeBuyRecenterLoop(string memory line) internal {
uint256 count = vm.parseJsonUint(line, ".count");
uint256 amount = vm.parseUint(vm.parseJsonString(line, ".amount"));
for (uint256 i = 0; i < count; i++) {
// Buy WETHKRK.
vm.startBroadcast(ADV_PK);
ISwapRouter02(SWAP_ROUTER).exactInputSingle(
ISwapRouter02.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: krkAddr,
fee: POOL_FEE,
recipient: advAddr,
amountIn: amount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
})
);
vm.stopBroadcast();
// Recenter.
vm.startBroadcast(RECENTER_PK);
try ILM(lmAddr).recenter() returns (bool isUp) {
_lastRecenterIsUp = isUp;
_hasRecentered = true;
_logSnapshot(_seq++);
} catch {
console.log("recenter: skipped (amplitude not reached)");
}
vm.stopBroadcast();
}
}
/// @dev Swap KRKWETH via SwapRouter02. amount="all" uses full adversary KRK balance.
function _executeSell(string memory line) internal {
string memory amtStr = vm.parseJsonString(line, ".amount");

View file

@ -1,153 +1,2 @@
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy","amount":"100000000000000000000","token":"WETH"}
{"op":"recenter"}
{"op":"buy_recenter_loop","count":80,"amount":"100000000000000000000"}
{"op":"sell","amount":"all","token":"KRK"}