fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
pragma solidity ^0.8.19;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @title FitnessEvaluator
|
|
|
|
|
* @notice In-process (revm) batch fitness evaluator for Push3 evolution.
|
|
|
|
|
*
|
|
|
|
|
* Replaces the Anvil+forge-script pipeline with in-process EVM execution.
|
|
|
|
|
* Uses Foundry's native revm backend: vm.snapshot/revertTo are memory operations
|
|
|
|
|
* with no JSON-RPC overhead, giving 100-1000x speedup over per-candidate Anvil.
|
|
|
|
|
*
|
|
|
|
|
* Architecture:
|
|
|
|
|
* batch-eval.sh compiles each candidate (Push3→Solidity→bytecode) and writes a
|
|
|
|
|
* two-file manifest (ids.txt + bytecodes.txt). This test reads the manifest,
|
|
|
|
|
* forks Base mainnet once, deploys the full KRAIKEN stack once, then for each
|
|
|
|
|
* candidate:
|
|
|
|
|
* 1. snapshot → etch candidate bytecode → UUPS upgrade proxy → bootstrap
|
|
|
|
|
* 2. For each attack: snapshot → execute → accumulate lm_eth_total → revert
|
|
|
|
|
* 3. Emit JSON score line
|
|
|
|
|
* 4. Revert to pre-bootstrap snapshot
|
|
|
|
|
*
|
|
|
|
|
* Required env vars:
|
|
|
|
|
* BASE_RPC_URL Base network RPC endpoint (for fork)
|
|
|
|
|
* FITNESS_MANIFEST_DIR Directory containing ids.txt and bytecodes.txt
|
|
|
|
|
*
|
|
|
|
|
* Optional env vars:
|
|
|
|
|
* ATTACKS_DIR Path to *.jsonl attack files (default: script/backtesting/attacks)
|
|
|
|
|
*
|
|
|
|
|
* Run:
|
|
|
|
|
* BASE_RPC_URL=https://mainnet.base.org \
|
|
|
|
|
* FITNESS_MANIFEST_DIR=/tmp/manifest \
|
|
|
|
|
* forge test --match-contract FitnessEvaluator --match-test testBatchEvaluate -vv
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import "forge-std/Test.sol";
|
|
|
|
|
import { Kraiken } from "../src/Kraiken.sol";
|
|
|
|
|
import { Stake } from "../src/Stake.sol";
|
|
|
|
|
import { Optimizer } from "../src/Optimizer.sol";
|
2026-03-13 00:25:49 +00:00
|
|
|
import { OptimizerInput } from "../src/IOptimizer.sol";
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
import { LiquidityManager } from "../src/LiquidityManager.sol";
|
|
|
|
|
import { ERC1967Proxy } from "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";
|
|
|
|
|
import { UUPSUpgradeable } from "@openzeppelin/proxy/utils/UUPSUpgradeable.sol";
|
|
|
|
|
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
|
|
|
|
|
import { IUniswapV3Factory } from "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
|
|
|
|
|
import { IUniswapV3Pool } from "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
|
|
|
|
import { LiquidityAmounts } from "@aperture/uni-v3-lib/LiquidityAmounts.sol";
|
|
|
|
|
import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol";
|
|
|
|
|
import { UniswapHelpers } from "../src/helpers/UniswapHelpers.sol";
|
|
|
|
|
import { IWETH9 } from "../src/interfaces/IWETH9.sol";
|
|
|
|
|
|
|
|
|
|
// ─── External interfaces (mirrors AttackRunner.s.sol) ─────────────────────────
|
|
|
|
|
|
|
|
|
|
interface ISwapRouter02 {
|
|
|
|
|
struct ExactInputSingleParams {
|
|
|
|
|
address tokenIn;
|
|
|
|
|
address tokenOut;
|
|
|
|
|
uint24 fee;
|
|
|
|
|
address recipient;
|
|
|
|
|
uint256 amountIn;
|
|
|
|
|
uint256 amountOutMinimum;
|
|
|
|
|
uint160 sqrtPriceLimitX96;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function exactInputSingle(ExactInputSingleParams calldata params) external returns (uint256);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ILM {
|
|
|
|
|
function getVWAP() external view returns (uint256);
|
|
|
|
|
function positions(uint8 stage) external view returns (uint128 liquidity, int24 tickLower, int24 tickUpper);
|
|
|
|
|
function recenter() external returns (bool);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IStake {
|
|
|
|
|
function snatch(uint256 assets, address receiver, uint32 taxRate, uint256[] calldata positionsToSnatch)
|
|
|
|
|
external
|
|
|
|
|
returns (uint256 positionId);
|
|
|
|
|
function exitPosition(uint256 positionId) external;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface INonfungiblePositionManager {
|
|
|
|
|
struct MintParams {
|
|
|
|
|
address token0;
|
|
|
|
|
address token1;
|
|
|
|
|
uint24 fee;
|
|
|
|
|
int24 tickLower;
|
|
|
|
|
int24 tickUpper;
|
|
|
|
|
uint256 amount0Desired;
|
|
|
|
|
uint256 amount1Desired;
|
|
|
|
|
uint256 amount0Min;
|
|
|
|
|
uint256 amount1Min;
|
|
|
|
|
address recipient;
|
|
|
|
|
uint256 deadline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct DecreaseLiquidityParams {
|
|
|
|
|
uint256 tokenId;
|
|
|
|
|
uint128 liquidity;
|
|
|
|
|
uint256 amount0Min;
|
|
|
|
|
uint256 amount1Min;
|
|
|
|
|
uint256 deadline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct CollectParams {
|
|
|
|
|
uint256 tokenId;
|
|
|
|
|
address recipient;
|
|
|
|
|
uint128 amount0Max;
|
|
|
|
|
uint128 amount1Max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
|
|
|
|
|
function positions(uint256 tokenId)
|
|
|
|
|
external
|
|
|
|
|
view
|
|
|
|
|
returns (
|
|
|
|
|
uint96 nonce,
|
|
|
|
|
address operator,
|
|
|
|
|
address token0,
|
|
|
|
|
address token1,
|
|
|
|
|
uint24 fee,
|
|
|
|
|
int24 tickLower,
|
|
|
|
|
int24 tickUpper,
|
|
|
|
|
uint128 liquidity,
|
|
|
|
|
uint256 feeGrowthInside0LastX128,
|
|
|
|
|
uint256 feeGrowthInside1LastX128,
|
|
|
|
|
uint128 tokensOwed0,
|
|
|
|
|
uint128 tokensOwed1
|
|
|
|
|
);
|
|
|
|
|
function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
|
|
|
|
|
function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Main test contract ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
contract FitnessEvaluator is Test {
|
|
|
|
|
using UniswapHelpers for IUniswapV3Pool;
|
|
|
|
|
|
|
|
|
|
// ─── Base network constants ───────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
uint24 internal constant POOL_FEE = 10_000;
|
2026-03-23 09:51:46 +00:00
|
|
|
// Base mainnet WETH — https://basescan.org/address/0x4200000000000000000000000000000000000006
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
address internal constant WETH_ADDR = 0x4200000000000000000000000000000000000006;
|
2026-03-23 09:51:46 +00:00
|
|
|
// Base mainnet SwapRouter02 — https://basescan.org/address/0x2626664c2603336E57B271c5C0b26F421741e481
|
2026-03-15 01:50:54 +00:00
|
|
|
address internal constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
|
|
|
|
|
// Base mainnet NonfungiblePositionManager — https://basescan.org/address/0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1
|
|
|
|
|
address internal constant NPM_ADDR = 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1;
|
|
|
|
|
// Base mainnet Uniswap V3 Factory — https://basescan.org/address/0x33128a8fC17869897dcE68Ed026d694621f6FDfD
|
|
|
|
|
address internal constant V3_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
|
2026-03-15 02:13:04 +00:00
|
|
|
/// @dev Intentionally differs from DeployBaseMainnet.sol (0xf6a3...D9011).
|
|
|
|
|
/// On a Base mainnet fork that address already has contract bytecode, which
|
|
|
|
|
/// causes LiquidityManager.setFeeDestination() to set feeDestinationLocked=true
|
|
|
|
|
/// and subsequently revert if called again during snapshot/revert cycles.
|
|
|
|
|
/// This keccak-derived address is a guaranteed EOA on any live network.
|
2026-03-15 01:50:54 +00:00
|
|
|
address internal constant FEE_DEST = 0x8A9145E1Ea4C4d7FB08cF1011c8ac1F0e10F9383;
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
|
|
|
|
|
/// @dev Fixed address used with vm.etch to inject candidate bytecode.
|
|
|
|
|
/// Chosen to be deterministic and not collide with real Base addresses.
|
|
|
|
|
address internal constant IMPL_SLOT = address(uint160(uint256(keccak256("fitness.impl.slot"))));
|
|
|
|
|
|
2026-03-13 01:05:37 +00:00
|
|
|
/// @dev Must match Optimizer.CALCULATE_PARAMS_GAS_LIMIT. Candidates that exceed
|
|
|
|
|
/// this limit would unconditionally produce bear defaults in production and
|
|
|
|
|
/// are disqualified (fitness = 0) rather than scored against their theoretical output.
|
2026-03-15 00:00:56 +00:00
|
|
|
uint256 internal constant CALCULATE_PARAMS_GAS_LIMIT = 500_000;
|
2026-03-13 01:05:37 +00:00
|
|
|
|
2026-03-13 00:25:49 +00:00
|
|
|
/// @dev Soft gas penalty: wei deducted from fitness per gas unit used by calculateParams.
|
|
|
|
|
/// Creates selection pressure toward leaner programs while keeping gas as a
|
|
|
|
|
/// secondary criterion (ETH retention still dominates).
|
|
|
|
|
/// At 15 k gas (current seed): ~1.5e17 wei penalty.
|
2026-03-15 00:28:12 +00:00
|
|
|
/// At 500 k gas (hard cap boundary): ~5e18 wei penalty.
|
2026-03-13 00:25:49 +00:00
|
|
|
uint256 internal constant GAS_PENALTY_FACTOR = 1e13;
|
|
|
|
|
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
// ─── Anvil test accounts (deterministic mnemonic) ────────────────────────
|
|
|
|
|
|
|
|
|
|
/// @dev Account 8 — adversary (10 000 ETH in Anvil; funded via vm.deal here)
|
|
|
|
|
uint256 internal constant ADV_PK = 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97;
|
2026-03-13 22:32:53 +00:00
|
|
|
/// @dev Account 2 — recenter caller (recenter() is now permissionless)
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
uint256 internal constant RECENTER_PK = 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a;
|
|
|
|
|
|
|
|
|
|
// ─── Runtime state ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
address internal lmAddr;
|
|
|
|
|
address internal krkAddr;
|
|
|
|
|
address internal stakeAddr;
|
|
|
|
|
address internal optProxy;
|
|
|
|
|
address internal advAddr;
|
|
|
|
|
address internal recenterAddr;
|
|
|
|
|
IUniswapV3Pool internal pool;
|
|
|
|
|
bool internal token0isWeth;
|
|
|
|
|
|
|
|
|
|
/// @dev Mirrors AttackRunner._stakedPositionIds: position IDs returned by stake ops.
|
|
|
|
|
/// vm.snapshot/revertTo reverts this array's storage between attacks.
|
|
|
|
|
uint256[] internal _stakedPositionIds;
|
|
|
|
|
|
2026-03-12 12:31:11 +00:00
|
|
|
/// @dev NPM tokenIds returned by mint_lp ops (in insertion order).
|
|
|
|
|
/// burn_lp references positions by 1-based index into this array so that
|
|
|
|
|
/// attack files are fork-block-independent (tokenIds vary by fork tip).
|
|
|
|
|
uint256[] internal _mintedNpmTokenIds;
|
|
|
|
|
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
// ─── Entry point ─────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @notice Batch fitness evaluator: score all candidates in the manifest.
|
|
|
|
|
*
|
|
|
|
|
* Reads FITNESS_MANIFEST_DIR/{ids.txt,bytecodes.txt} line-by-line.
|
|
|
|
|
* Outputs one JSON line per candidate to stdout:
|
|
|
|
|
* {"candidate_id":"gen0_c000","fitness":1234567890}
|
|
|
|
|
*
|
|
|
|
|
* Skipped (with a pass) if BASE_RPC_URL is not set, so CI without a Base
|
|
|
|
|
* RPC key does not fail the test suite.
|
|
|
|
|
*/
|
|
|
|
|
function testBatchEvaluate() public {
|
|
|
|
|
string memory rpcUrl = vm.envOr("BASE_RPC_URL", string(""));
|
|
|
|
|
vm.skip(bytes(rpcUrl).length == 0);
|
|
|
|
|
|
|
|
|
|
string memory manifestDir = vm.envOr("FITNESS_MANIFEST_DIR", string(""));
|
|
|
|
|
require(bytes(manifestDir).length > 0, "FITNESS_MANIFEST_DIR env var required");
|
|
|
|
|
|
|
|
|
|
string memory attacksDir = vm.envOr("ATTACKS_DIR", string("script/backtesting/attacks"));
|
|
|
|
|
|
|
|
|
|
// Fork Base mainnet so Uniswap V3, WETH, etc. exist at canonical addresses.
|
|
|
|
|
vm.createSelectFork(rpcUrl);
|
|
|
|
|
|
|
|
|
|
advAddr = vm.addr(ADV_PK);
|
|
|
|
|
recenterAddr = vm.addr(RECENTER_PK);
|
|
|
|
|
|
|
|
|
|
// Deploy the full KRAIKEN stack once on the fork.
|
|
|
|
|
_deploy();
|
|
|
|
|
|
|
|
|
|
// Snapshot after deployment (pre-bootstrap, pre-candidate-specific state).
|
|
|
|
|
uint256 baseSnap = vm.snapshot();
|
|
|
|
|
|
|
|
|
|
// Discover attack files (sorted alphabetically by path).
|
|
|
|
|
string memory idsFile = string.concat(manifestDir, "/ids.txt");
|
|
|
|
|
string memory bytecodesFile = string.concat(manifestDir, "/bytecodes.txt");
|
|
|
|
|
|
|
|
|
|
// Process candidates one at a time.
|
|
|
|
|
while (true) {
|
|
|
|
|
string memory candidateId = vm.readLine(idsFile);
|
|
|
|
|
string memory bytecodeHex = vm.readLine(bytecodesFile);
|
|
|
|
|
if (bytes(candidateId).length == 0) break;
|
2026-03-15 02:13:04 +00:00
|
|
|
if (bytes(bytecodeHex).length == 0) {
|
|
|
|
|
console.log("FitnessEvaluator: bytecodes.txt EOF before ids.txt - pipeline mismatch, stopping");
|
|
|
|
|
break;
|
|
|
|
|
}
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
|
|
|
|
|
// Revert to clean post-deploy state for each candidate.
|
|
|
|
|
vm.revertTo(baseSnap);
|
|
|
|
|
baseSnap = vm.snapshot();
|
|
|
|
|
|
2026-03-12 19:54:58 +00:00
|
|
|
// Etch candidate optimizer bytecode onto the implementation address
|
|
|
|
|
// and update the ERC1967 implementation slot directly.
|
|
|
|
|
// Skips UUPS upgradeTo() check — candidates are standalone contracts
|
|
|
|
|
// (OptimizerV3Push3) without UUPSUpgradeable inheritance.
|
|
|
|
|
// This is safe because we only care about calculateParams() output.
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
bytes memory candidateBytecode = vm.parseBytes(bytecodeHex);
|
2026-03-12 19:54:58 +00:00
|
|
|
if (candidateBytecode.length == 0) {
|
|
|
|
|
console.log(string.concat('{"candidate_id":"', candidateId, '","fitness":0,"error":"empty_bytecode"}'));
|
2026-03-12 12:31:11 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2026-03-12 19:54:58 +00:00
|
|
|
vm.etch(IMPL_SLOT, candidateBytecode);
|
|
|
|
|
// ERC1967 implementation slot = keccak256("eip1967.proxy.implementation") - 1
|
|
|
|
|
bytes32 ERC1967_IMPL = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
|
|
|
|
vm.store(optProxy, ERC1967_IMPL, bytes32(uint256(uint160(IMPL_SLOT))));
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
|
2026-03-13 22:32:53 +00:00
|
|
|
// Bootstrap: fund LM, initial recenter.
|
2026-03-12 19:54:58 +00:00
|
|
|
if (!_bootstrap()) {
|
|
|
|
|
console.log(string.concat('{"candidate_id":"', candidateId, '","fitness":0,"error":"bootstrap_failed"}'));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
|
2026-03-13 00:25:49 +00:00
|
|
|
// Measure gas used by calculateParams with fixed representative inputs.
|
|
|
|
|
// Fixed inputs ensure fair, reproducible comparison across all candidates.
|
|
|
|
|
// Uses slot 0 = 50% staked, slot 1 = 5% avg tax rate; remaining slots = 0.
|
|
|
|
|
OptimizerInput[8] memory sampleInputs;
|
|
|
|
|
sampleInputs[0] = OptimizerInput({ mantissa: 5e17, shift: 0 });
|
|
|
|
|
sampleInputs[1] = OptimizerInput({ mantissa: 5e16, shift: 0 });
|
|
|
|
|
uint256 gasBefore = gasleft();
|
|
|
|
|
try Optimizer(optProxy).calculateParams(sampleInputs) returns (uint256, uint256, uint24, uint256) { } catch { }
|
|
|
|
|
uint256 gasForCalcParams = gasBefore - gasleft();
|
|
|
|
|
|
2026-03-13 01:05:37 +00:00
|
|
|
// Hard disqualification: candidates that exceed the production gas cap would
|
|
|
|
|
// unconditionally produce bear defaults from getLiquidityParams() in every
|
|
|
|
|
// recenter call — equivalent to deploying the bear-defaults optimizer.
|
|
|
|
|
// Score them as 0 so the evolution pipeline never selects a program that is
|
|
|
|
|
// functionally dead on-chain.
|
|
|
|
|
if (gasForCalcParams > CALCULATE_PARAMS_GAS_LIMIT) {
|
|
|
|
|
console.log(
|
|
|
|
|
string.concat(
|
|
|
|
|
'{"candidate_id":"', candidateId,
|
|
|
|
|
'","fitness":0,"error":"gas_over_limit","gas_used":', _uint2str(gasForCalcParams),
|
|
|
|
|
"}"
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
// Score: sum lm_eth_total across all attack sequences.
|
|
|
|
|
uint256 totalFitness = 0;
|
|
|
|
|
Vm.DirEntry[] memory entries = vm.readDir(attacksDir);
|
|
|
|
|
for (uint256 i = 0; i < entries.length; i++) {
|
|
|
|
|
if (entries[i].isDir || !_endsWith(entries[i].path, ".jsonl")) continue;
|
|
|
|
|
|
|
|
|
|
uint256 atkSnap = vm.snapshot();
|
|
|
|
|
uint256 score = _runAttack(entries[i].path);
|
|
|
|
|
totalFitness += score;
|
|
|
|
|
vm.revertTo(atkSnap);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-13 00:25:49 +00:00
|
|
|
// Apply soft gas penalty: fitness = score - (gasUsed * GAS_PENALTY_FACTOR).
|
|
|
|
|
// Leaner programs win ties; programs at the hard-cap boundary incur ~2 ETH penalty.
|
|
|
|
|
uint256 gasPenalty = gasForCalcParams * GAS_PENALTY_FACTOR;
|
|
|
|
|
uint256 adjustedFitness = totalFitness > gasPenalty ? totalFitness - gasPenalty : 0;
|
|
|
|
|
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
// Emit score as a JSON line (parsed by batch-eval.sh).
|
2026-03-13 00:25:49 +00:00
|
|
|
console.log(
|
|
|
|
|
string.concat(
|
|
|
|
|
'{"candidate_id":"', candidateId,
|
|
|
|
|
'","fitness":', _uint2str(adjustedFitness),
|
|
|
|
|
',"gas_used":', _uint2str(gasForCalcParams),
|
|
|
|
|
"}"
|
|
|
|
|
)
|
|
|
|
|
);
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close manifest files.
|
|
|
|
|
vm.closeFile(idsFile);
|
|
|
|
|
vm.closeFile(bytecodesFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Deployment ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @notice Deploy the full KRAIKEN stack (mirrors DeployLocal.sol).
|
|
|
|
|
* @dev All contracts are deployed as address(this) (the test contract),
|
|
|
|
|
* which becomes the UUPS admin for the Optimizer proxy.
|
|
|
|
|
*/
|
|
|
|
|
function _deploy() internal {
|
|
|
|
|
// Deploy Kraiken token.
|
|
|
|
|
Kraiken kraiken = new Kraiken("Kraiken", "KRK");
|
|
|
|
|
krkAddr = address(kraiken);
|
|
|
|
|
token0isWeth = WETH_ADDR < krkAddr;
|
|
|
|
|
|
|
|
|
|
// Deploy Stake.
|
|
|
|
|
Stake stake = new Stake(krkAddr, FEE_DEST);
|
|
|
|
|
stakeAddr = address(stake);
|
|
|
|
|
kraiken.setStakingPool(stakeAddr);
|
|
|
|
|
|
|
|
|
|
// Get or create Uniswap V3 pool.
|
|
|
|
|
IUniswapV3Factory factory = IUniswapV3Factory(V3_FACTORY);
|
|
|
|
|
address poolAddr = factory.getPool(WETH_ADDR, krkAddr, POOL_FEE);
|
|
|
|
|
if (poolAddr == address(0)) {
|
|
|
|
|
poolAddr = factory.createPool(WETH_ADDR, krkAddr, POOL_FEE);
|
|
|
|
|
}
|
|
|
|
|
pool = IUniswapV3Pool(poolAddr);
|
|
|
|
|
|
|
|
|
|
// Initialize pool at 1-cent price if not already initialized.
|
|
|
|
|
(uint160 sqrtPriceX96,,,,,,) = pool.slot0();
|
|
|
|
|
if (sqrtPriceX96 == 0) {
|
|
|
|
|
pool.initializePoolFor1Cent(token0isWeth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deploy Optimizer implementation + UUPS proxy.
|
|
|
|
|
// address(this) (test contract) becomes the UUPS admin via initialize.
|
|
|
|
|
Optimizer optimizerImpl = new Optimizer();
|
|
|
|
|
bytes memory initData = abi.encodeWithSignature("initialize(address,address)", krkAddr, stakeAddr);
|
|
|
|
|
ERC1967Proxy proxy = new ERC1967Proxy(address(optimizerImpl), initData);
|
|
|
|
|
optProxy = address(proxy);
|
|
|
|
|
|
|
|
|
|
// Deploy LiquidityManager.
|
|
|
|
|
LiquidityManager lm = new LiquidityManager(V3_FACTORY, WETH_ADDR, krkAddr, optProxy);
|
|
|
|
|
lmAddr = address(lm);
|
|
|
|
|
|
|
|
|
|
// Wire contracts together.
|
|
|
|
|
lm.setFeeDestination(FEE_DEST);
|
|
|
|
|
kraiken.setLiquidityManager(lmAddr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Bootstrap ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @notice Bootstrap LM state for a candidate evaluation (mirrors fitness.sh bootstrap).
|
|
|
|
|
*
|
|
|
|
|
* Steps (same order as fitness.sh):
|
2026-03-13 22:32:53 +00:00
|
|
|
* a. Fund adversary account and wrap ETH → WETH.
|
|
|
|
|
* b. Transfer 1000 WETH to LM.
|
|
|
|
|
* c. Wrap 9000 WETH for adversary trades + set approvals.
|
|
|
|
|
* d. Initial recenter (callable by anyone: cooldown passes because block.timestamp on a
|
|
|
|
|
* Base fork is a large value >> 60; TWAP passes because the pool has existing history).
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
*/
|
2026-03-12 19:54:58 +00:00
|
|
|
function _bootstrap() internal returns (bool) {
|
2026-03-13 22:32:53 +00:00
|
|
|
// a. Fund adversary with ETH.
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
vm.deal(advAddr, 10_000 ether);
|
|
|
|
|
|
|
|
|
|
// c. Wrap 1000 ETH → WETH and send to LM.
|
|
|
|
|
vm.startPrank(advAddr);
|
|
|
|
|
IWETH9(WETH_ADDR).deposit{ value: 1_000 ether }();
|
|
|
|
|
IERC20(WETH_ADDR).transfer(lmAddr, 1_000 ether);
|
|
|
|
|
vm.stopPrank();
|
|
|
|
|
|
|
|
|
|
// d. Wrap remaining 9000 ETH for trade operations + set approvals.
|
|
|
|
|
vm.startPrank(advAddr);
|
|
|
|
|
IWETH9(WETH_ADDR).deposit{ value: 9_000 ether }();
|
|
|
|
|
IERC20(WETH_ADDR).approve(SWAP_ROUTER, type(uint256).max);
|
|
|
|
|
IERC20(WETH_ADDR).approve(NPM_ADDR, type(uint256).max);
|
|
|
|
|
IERC20(krkAddr).approve(SWAP_ROUTER, type(uint256).max);
|
|
|
|
|
IERC20(krkAddr).approve(stakeAddr, type(uint256).max);
|
|
|
|
|
IERC20(krkAddr).approve(NPM_ADDR, type(uint256).max);
|
|
|
|
|
vm.stopPrank();
|
|
|
|
|
|
2026-03-13 22:32:53 +00:00
|
|
|
// d. Initial recenter: no ANCHOR position exists yet so amplitude check is skipped.
|
|
|
|
|
// Cooldown passes (Base fork timestamp >> 60). TWAP passes (existing pool history).
|
2026-03-12 12:31:11 +00:00
|
|
|
// If all retries fail, revert with a clear message — silent failure would make every
|
|
|
|
|
// candidate score identically (all lm_eth_total = free WETH only, no positions).
|
|
|
|
|
bool recentered = false;
|
|
|
|
|
for (uint256 _attempt = 0; _attempt < 5; _attempt++) {
|
2026-03-15 01:50:54 +00:00
|
|
|
if (_attempt > 0) {
|
|
|
|
|
vm.roll(block.number + 50);
|
|
|
|
|
vm.warp(block.timestamp + 600);
|
|
|
|
|
}
|
2026-03-12 12:31:11 +00:00
|
|
|
vm.prank(recenterAddr);
|
|
|
|
|
try ILM(lmAddr).recenter() returns (bool) {
|
|
|
|
|
recentered = true;
|
|
|
|
|
break;
|
2026-03-12 19:54:58 +00:00
|
|
|
} catch (bytes memory reason) {
|
|
|
|
|
console.log(string.concat("recenter attempt ", vm.toString(_attempt), " failed"));
|
|
|
|
|
console.logBytes(reason);
|
|
|
|
|
}
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
}
|
2026-03-12 19:54:58 +00:00
|
|
|
if (!recentered) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Attack execution ─────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @notice Execute one attack sequence and return the final lm_eth_total.
|
|
|
|
|
* @param attackFile Path to the *.jsonl attack file.
|
|
|
|
|
*/
|
|
|
|
|
function _runAttack(string memory attackFile) internal returns (uint256) {
|
|
|
|
|
// Reset file read position so each call to _runAttack starts from line 1.
|
|
|
|
|
vm.closeFile(attackFile);
|
2026-03-12 12:31:11 +00:00
|
|
|
// vm.revertTo() reverts all EVM state including test contract storage, so these
|
|
|
|
|
// arrays are already empty after revert. Explicit delete is a defensive reset
|
|
|
|
|
// for the first attack (no preceding revert) and any future call-path changes.
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
delete _stakedPositionIds;
|
2026-03-12 12:31:11 +00:00
|
|
|
delete _mintedNpmTokenIds;
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
|
|
|
|
|
string memory line = vm.readLine(attackFile);
|
|
|
|
|
while (bytes(line).length > 0) {
|
2026-03-22 10:53:07 +00:00
|
|
|
// Skip comment lines (e.g. "// schema-version: 1" header).
|
|
|
|
|
if (bytes(line).length >= 2 && bytes(line)[0] == 0x2F && bytes(line)[1] == 0x2F) {
|
|
|
|
|
line = vm.readLine(attackFile);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
_executeOp(line);
|
|
|
|
|
line = vm.readLine(attackFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _computeLmEthTotal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @notice Execute a single attack operation (mirrors AttackRunner._execute).
|
|
|
|
|
*/
|
|
|
|
|
function _executeOp(string memory line) internal {
|
|
|
|
|
string memory op = vm.parseJsonString(line, ".op");
|
|
|
|
|
|
|
|
|
|
if (_eq(op, "buy")) {
|
|
|
|
|
uint256 amount = vm.parseUint(vm.parseJsonString(line, ".amount"));
|
|
|
|
|
vm.prank(advAddr);
|
2026-03-12 19:54:58 +00:00
|
|
|
try ISwapRouter02(SWAP_ROUTER).exactInputSingle(
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
ISwapRouter02.ExactInputSingleParams({
|
|
|
|
|
tokenIn: WETH_ADDR,
|
|
|
|
|
tokenOut: krkAddr,
|
|
|
|
|
fee: POOL_FEE,
|
|
|
|
|
recipient: advAddr,
|
|
|
|
|
amountIn: amount,
|
|
|
|
|
amountOutMinimum: 0,
|
|
|
|
|
sqrtPriceLimitX96: 0
|
|
|
|
|
})
|
2026-03-12 19:54:58 +00:00
|
|
|
) { } catch { }
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
} else if (_eq(op, "sell")) {
|
|
|
|
|
string memory amtStr = vm.parseJsonString(line, ".amount");
|
|
|
|
|
uint256 amount = _eq(amtStr, "all") ? IERC20(krkAddr).balanceOf(advAddr) : vm.parseUint(amtStr);
|
|
|
|
|
if (amount == 0) return;
|
|
|
|
|
vm.prank(advAddr);
|
2026-03-12 19:54:58 +00:00
|
|
|
try ISwapRouter02(SWAP_ROUTER).exactInputSingle(
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
ISwapRouter02.ExactInputSingleParams({
|
|
|
|
|
tokenIn: krkAddr,
|
|
|
|
|
tokenOut: WETH_ADDR,
|
|
|
|
|
fee: POOL_FEE,
|
|
|
|
|
recipient: advAddr,
|
|
|
|
|
amountIn: amount,
|
|
|
|
|
amountOutMinimum: 0,
|
|
|
|
|
sqrtPriceLimitX96: 0
|
|
|
|
|
})
|
2026-03-12 19:54:58 +00:00
|
|
|
) { } catch { }
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
} else if (_eq(op, "recenter")) {
|
|
|
|
|
vm.prank(recenterAddr);
|
|
|
|
|
try ILM(lmAddr).recenter() { } catch { }
|
|
|
|
|
} else if (_eq(op, "stake")) {
|
|
|
|
|
uint256 amount = vm.parseUint(vm.parseJsonString(line, ".amount"));
|
|
|
|
|
uint32 taxRate = uint32(vm.parseJsonUint(line, ".taxRateIndex"));
|
|
|
|
|
vm.prank(advAddr);
|
2026-03-12 19:54:58 +00:00
|
|
|
try IStake(stakeAddr).snatch(amount, advAddr, taxRate, new uint256[](0)) returns (uint256 posId) {
|
|
|
|
|
_stakedPositionIds.push(posId);
|
|
|
|
|
} catch { }
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
} else if (_eq(op, "unstake")) {
|
|
|
|
|
uint256 posIndex = vm.parseJsonUint(line, ".positionId");
|
2026-03-12 19:54:58 +00:00
|
|
|
if (posIndex < 1 || posIndex > _stakedPositionIds.length) return;
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
vm.prank(advAddr);
|
2026-03-12 19:54:58 +00:00
|
|
|
try IStake(stakeAddr).exitPosition(_stakedPositionIds[posIndex - 1]) { } catch { }
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
} else if (_eq(op, "mine")) {
|
|
|
|
|
uint256 blocks = vm.parseJsonUint(line, ".blocks");
|
|
|
|
|
vm.roll(block.number + blocks);
|
|
|
|
|
} else if (_eq(op, "mint_lp")) {
|
|
|
|
|
int24 tickLower = int24(vm.parseJsonInt(line, ".tickLower"));
|
|
|
|
|
int24 tickUpper = int24(vm.parseJsonInt(line, ".tickUpper"));
|
|
|
|
|
uint256 amount0 = vm.parseUint(vm.parseJsonString(line, ".amount0"));
|
|
|
|
|
uint256 amount1 = vm.parseUint(vm.parseJsonString(line, ".amount1"));
|
|
|
|
|
(address t0, address t1) = token0isWeth ? (WETH_ADDR, krkAddr) : (krkAddr, WETH_ADDR);
|
|
|
|
|
vm.prank(advAddr);
|
2026-03-12 12:31:11 +00:00
|
|
|
// Track the returned tokenId so burn_lp can reference it by 1-based index,
|
|
|
|
|
// making attack files fork-block-independent (NPM tokenIds depend on fork tip).
|
2026-03-20 00:23:58 +00:00
|
|
|
try INonfungiblePositionManager(NPM_ADDR).mint(
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
INonfungiblePositionManager.MintParams({
|
|
|
|
|
token0: t0,
|
|
|
|
|
token1: t1,
|
|
|
|
|
fee: POOL_FEE,
|
|
|
|
|
tickLower: tickLower,
|
|
|
|
|
tickUpper: tickUpper,
|
|
|
|
|
amount0Desired: amount0,
|
|
|
|
|
amount1Desired: amount1,
|
|
|
|
|
amount0Min: 0,
|
|
|
|
|
amount1Min: 0,
|
|
|
|
|
recipient: advAddr,
|
|
|
|
|
deadline: block.timestamp + 3600
|
|
|
|
|
})
|
2026-03-20 00:23:58 +00:00
|
|
|
) returns (uint256 mintedTokenId, uint128, uint256, uint256) {
|
|
|
|
|
_mintedNpmTokenIds.push(mintedTokenId);
|
|
|
|
|
} catch { }
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
} else if (_eq(op, "burn_lp")) {
|
2026-03-12 12:31:11 +00:00
|
|
|
// .tokenId in the attack file is a 1-based index into _mintedNpmTokenIds
|
|
|
|
|
// (positions created by mint_lp ops in this run), not a raw NPM tokenId.
|
|
|
|
|
// This mirrors the stake/unstake index pattern and avoids fork-block sensitivity.
|
|
|
|
|
uint256 tokenIndex = vm.parseJsonUint(line, ".tokenId");
|
2026-03-20 00:23:58 +00:00
|
|
|
if (tokenIndex < 1 || tokenIndex > _mintedNpmTokenIds.length) return;
|
2026-03-12 12:31:11 +00:00
|
|
|
uint256 tokenId = _mintedNpmTokenIds[tokenIndex - 1];
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
(,,,,,, , uint128 liquidity,,,,) = INonfungiblePositionManager(NPM_ADDR).positions(tokenId);
|
|
|
|
|
if (liquidity == 0) return;
|
|
|
|
|
vm.startPrank(advAddr);
|
2026-03-20 00:23:58 +00:00
|
|
|
try INonfungiblePositionManager(NPM_ADDR).decreaseLiquidity(
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
INonfungiblePositionManager.DecreaseLiquidityParams({
|
|
|
|
|
tokenId: tokenId,
|
|
|
|
|
liquidity: liquidity,
|
|
|
|
|
amount0Min: 0,
|
|
|
|
|
amount1Min: 0,
|
|
|
|
|
deadline: block.timestamp + 3600
|
|
|
|
|
})
|
2026-03-20 00:23:58 +00:00
|
|
|
) {
|
|
|
|
|
try INonfungiblePositionManager(NPM_ADDR).collect(
|
|
|
|
|
INonfungiblePositionManager.CollectParams({
|
|
|
|
|
tokenId: tokenId,
|
|
|
|
|
recipient: advAddr,
|
|
|
|
|
amount0Max: type(uint128).max,
|
|
|
|
|
amount1Max: type(uint128).max
|
|
|
|
|
})
|
|
|
|
|
) { } catch { }
|
|
|
|
|
} catch { }
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
vm.stopPrank();
|
|
|
|
|
}
|
|
|
|
|
// Unknown ops are silently ignored (mirrors AttackRunner behaviour).
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Score computation ────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-13 09:11:31 +00:00
|
|
|
* @notice Compute lm_eth_total = free ETH + free WETH + sum(WETH in each position).
|
|
|
|
|
* Only counts real ETH reserves — KRK token value is excluded to prevent
|
|
|
|
|
* evolution from gaming the metric by inflating token price through positioning.
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
*/
|
|
|
|
|
function _computeLmEthTotal() internal view returns (uint256) {
|
|
|
|
|
(uint160 sqrtPriceX96,,,,,,) = pool.slot0();
|
|
|
|
|
|
|
|
|
|
uint256 lmEthFree = lmAddr.balance;
|
|
|
|
|
uint256 lmWethFree = IERC20(WETH_ADDR).balanceOf(lmAddr);
|
|
|
|
|
|
|
|
|
|
(uint128 fLiq, int24 fLo, int24 fHi) = ILM(lmAddr).positions(0); // FLOOR
|
|
|
|
|
(uint128 aLiq, int24 aLo, int24 aHi) = ILM(lmAddr).positions(1); // ANCHOR
|
|
|
|
|
(uint128 dLiq, int24 dLo, int24 dHi) = ILM(lmAddr).positions(2); // DISCOVERY
|
|
|
|
|
|
|
|
|
|
return lmEthFree
|
|
|
|
|
+ lmWethFree
|
2026-03-13 09:11:31 +00:00
|
|
|
+ _positionEthOnly(sqrtPriceX96, fLo, fHi, fLiq)
|
|
|
|
|
+ _positionEthOnly(sqrtPriceX96, aLo, aHi, aLiq)
|
|
|
|
|
+ _positionEthOnly(sqrtPriceX96, dLo, dHi, dLiq);
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-13 09:11:31 +00:00
|
|
|
* @notice WETH-only component of a Uniswap V3 position at the current price.
|
|
|
|
|
* Ignores KRK token value entirely — counts only the actual ETH backing.
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
*/
|
2026-03-13 09:11:31 +00:00
|
|
|
function _positionEthOnly(
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
uint160 sqrtPriceX96,
|
|
|
|
|
int24 tickLower,
|
|
|
|
|
int24 tickUpper,
|
|
|
|
|
uint128 liquidity
|
|
|
|
|
)
|
|
|
|
|
internal
|
|
|
|
|
view
|
|
|
|
|
returns (uint256)
|
|
|
|
|
{
|
|
|
|
|
if (liquidity == 0) return 0;
|
|
|
|
|
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower);
|
|
|
|
|
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper);
|
|
|
|
|
(uint256 amount0, uint256 amount1) =
|
|
|
|
|
LiquidityAmounts.getAmountsForLiquidity(sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, liquidity);
|
|
|
|
|
|
2026-03-13 09:11:31 +00:00
|
|
|
return token0isWeth ? amount0 : amount1;
|
fix: feat: revm-based fitness evaluator for evolution at scale (#604)
Replace per-candidate Anvil+forge-script pipeline with in-process EVM
execution using Foundry's native revm backend, achieving 10-100× speedup
for evolutionary search at scale.
New files:
- onchain/test/FitnessEvaluator.t.sol — Forge test that forks Base once,
deploys the full KRAIKEN stack, then for each candidate uses vm.etch to
inject the compiled optimizer bytecode, UUPS-upgrades the proxy, runs all
attack sequences with in-memory vm.snapshot/revertTo (no RPC overhead),
and emits one {"candidate_id","fitness"} JSON line per candidate.
Skips gracefully when BASE_RPC_URL is unset (CI-safe).
- tools/push3-evolution/revm-evaluator/batch-eval.sh — Wrapper that
transpiles+compiles each candidate sequentially, writes a two-file
manifest (ids.txt + bytecodes.txt), then invokes FitnessEvaluator.t.sol
in a single forge test run and parses the score JSON from stdout.
Modified:
- tools/push3-evolution/evolve.sh — Adds EVAL_MODE env var (anvil|revm).
When EVAL_MODE=revm, batch-scores every candidate in a generation with
one batch-eval.sh call instead of N sequential fitness.sh processes;
scores are looked up from the JSONL output in the per-candidate loop.
Default remains EVAL_MODE=anvil for backward compatibility.
Key design decisions:
- Per-candidate Solidity compilation is unavoidable (each Push3 candidate
produces different Solidity); the speedup is in the evaluation phase.
- vm.snapshot/revertTo in forge test are O(1) memory operations (true
revm), not RPC calls — this is the core speedup vs Anvil.
- recenterAccess is set in bootstrap so TWAP stability checks are bypassed
during attack sequences (mirrors the existing fitness.sh bootstrap).
- Test skips cleanly when BASE_RPC_URL is absent, keeping CI green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 11:54:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Utilities ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
function _eq(string memory a, string memory b) internal pure returns (bool) {
|
|
|
|
|
return keccak256(bytes(a)) == keccak256(bytes(b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _endsWith(string memory str, string memory suffix) internal pure returns (bool) {
|
|
|
|
|
bytes memory bStr = bytes(str);
|
|
|
|
|
bytes memory bSuf = bytes(suffix);
|
|
|
|
|
if (bStr.length < bSuf.length) return false;
|
|
|
|
|
uint256 offset = bStr.length - bSuf.length;
|
|
|
|
|
for (uint256 i = 0; i < bSuf.length; i++) {
|
|
|
|
|
if (bStr[offset + i] != bSuf[i]) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _uint2str(uint256 n) internal pure returns (string memory) {
|
|
|
|
|
if (n == 0) return "0";
|
|
|
|
|
uint256 temp = n;
|
|
|
|
|
uint256 digits;
|
|
|
|
|
while (temp != 0) {
|
|
|
|
|
digits++;
|
|
|
|
|
temp /= 10;
|
|
|
|
|
}
|
|
|
|
|
bytes memory buffer = new bytes(digits);
|
|
|
|
|
while (n != 0) {
|
|
|
|
|
digits--;
|
|
|
|
|
buffer[digits] = bytes1(uint8(48 + (n % 10)));
|
|
|
|
|
n /= 10;
|
|
|
|
|
}
|
|
|
|
|
return string(buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|