From 13f406b5a9010e63ef19f6d7226ee865376e3802 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 18 Mar 2026 07:33:54 +0000 Subject: [PATCH] fix: red-team.sh and AttackRunner.s.sol still use Base mainnet addresses (#939) Co-Authored-By: Claude Opus 4.6 --- onchain/script/backtesting/AttackRunner.s.sol | 32 +++++++++++-------- scripts/harb-evaluator/red-team.sh | 32 ++++++++++++++++--- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/onchain/script/backtesting/AttackRunner.s.sol b/onchain/script/backtesting/AttackRunner.s.sol index 7b80451..deb1867 100644 --- a/onchain/script/backtesting/AttackRunner.s.sol +++ b/onchain/script/backtesting/AttackRunner.s.sol @@ -151,8 +151,8 @@ contract AttackRunner is Script { uint24 internal constant POOL_FEE = 10_000; address internal constant WETH = 0x4200000000000000000000000000000000000006; - address internal constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481; - address internal constant NPM_ADDR = 0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3; + address internal constant DEFAULT_SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481; + address internal constant DEFAULT_NPM_ADDR = 0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3; address internal constant V3_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD; // Base mainnet // ─── Anvil test accounts ────────────────────────────────────────────────── @@ -164,6 +164,8 @@ contract AttackRunner is Script { // ─── Runtime state (populated in run()) ────────────────────────────────── + address internal swapRouter; + address internal npmAddr; address internal advAddr; address internal recenterAddr; address internal lmAddr; @@ -189,6 +191,10 @@ contract AttackRunner is Script { // ─── Entry point ───────────────────────────────────────────────────────── function run() external { + // Resolve periphery addresses from environment, falling back to mainnet defaults. + swapRouter = vm.envOr("SWAP_ROUTER", DEFAULT_SWAP_ROUTER); + npmAddr = vm.envOr("NPM_ADDR", DEFAULT_NPM_ADDR); + // Load deployment addresses before broadcast. string memory deploymentsPath = _deploymentsPath(); string memory deployJson = vm.readFile(deploymentsPath); @@ -235,11 +241,11 @@ contract AttackRunner is Script { // Wrap most of the adversary's ETH (leave 1 ETH for gas). // The adversary starts with 10 000 ETH; wrapping 9 000 covers the heaviest buy sequences. IWETH9(WETH).deposit{ value: 9_000 ether }(); - IERC20(WETH).approve(SWAP_ROUTER, type(uint256).max); - IERC20(WETH).approve(NPM_ADDR, type(uint256).max); - IERC20(krkAddr).approve(SWAP_ROUTER, type(uint256).max); + IERC20(WETH).approve(swapRouter, type(uint256).max); + IERC20(WETH).approve(npmAddr, type(uint256).max); + IERC20(krkAddr).approve(swapRouter, type(uint256).max); IERC20(krkAddr).approve(stakeAddr, type(uint256).max); - IERC20(krkAddr).approve(NPM_ADDR, type(uint256).max); + IERC20(krkAddr).approve(npmAddr, type(uint256).max); vm.stopBroadcast(); } @@ -292,7 +298,7 @@ contract AttackRunner is Script { function _executeBuy(string memory line) internal { uint256 amount = vm.parseUint(vm.parseJsonString(line, ".amount")); vm.startBroadcast(ADV_PK); - ISwapRouter02(SWAP_ROUTER).exactInputSingle( + ISwapRouter02(swapRouter).exactInputSingle( ISwapRouter02.ExactInputSingleParams({ tokenIn: WETH, tokenOut: krkAddr, @@ -321,7 +327,7 @@ contract AttackRunner is Script { for (uint256 i = 0; i < count; i++) { // Buy WETH→KRK. vm.startBroadcast(ADV_PK); - ISwapRouter02(SWAP_ROUTER).exactInputSingle( + ISwapRouter02(swapRouter).exactInputSingle( ISwapRouter02.ExactInputSingleParams({ tokenIn: WETH, tokenOut: krkAddr, @@ -353,7 +359,7 @@ contract AttackRunner is Script { uint256 amount = _eq(amtStr, "all") ? IERC20(krkAddr).balanceOf(advAddr) : vm.parseUint(amtStr); if (amount == 0) return; vm.startBroadcast(ADV_PK); - ISwapRouter02(SWAP_ROUTER).exactInputSingle( + ISwapRouter02(swapRouter).exactInputSingle( ISwapRouter02.ExactInputSingleParams({ tokenIn: krkAddr, tokenOut: WETH, @@ -407,7 +413,7 @@ contract AttackRunner is Script { (address t0, address t1) = token0isWeth ? (WETH, krkAddr) : (krkAddr, WETH); vm.startBroadcast(ADV_PK); - INonfungiblePositionManager(NPM_ADDR).mint( + INonfungiblePositionManager(npmAddr).mint( INonfungiblePositionManager.MintParams({ token0: t0, token1: t1, @@ -430,11 +436,11 @@ contract AttackRunner is Script { uint256 tokenId = vm.parseJsonUint(line, ".tokenId"); // Read current liquidity for this token. - (,,,,,,,uint128 liquidity,,,,) = INonfungiblePositionManager(NPM_ADDR).positions(tokenId); + (,,,,,,,uint128 liquidity,,,,) = INonfungiblePositionManager(npmAddr).positions(tokenId); if (liquidity == 0) return; vm.startBroadcast(ADV_PK); - INonfungiblePositionManager(NPM_ADDR).decreaseLiquidity( + INonfungiblePositionManager(npmAddr).decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: tokenId, liquidity: liquidity, @@ -443,7 +449,7 @@ contract AttackRunner is Script { deadline: block.timestamp + 3600 }) ); - INonfungiblePositionManager(NPM_ADDR).collect( + INonfungiblePositionManager(npmAddr).collect( INonfungiblePositionManager.CollectParams({ tokenId: tokenId, recipient: advAddr, diff --git a/scripts/harb-evaluator/red-team.sh b/scripts/harb-evaluator/red-team.sh index 7f4b7c1..5790dee 100755 --- a/scripts/harb-evaluator/red-team.sh +++ b/scripts/harb-evaluator/red-team.sh @@ -43,12 +43,31 @@ RECENTER_PK=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a # ── Infrastructure constants ─────────────────────────────────────────────────── WETH=0x4200000000000000000000000000000000000006 -# Base Sepolia SwapRouter02 — https://sepolia.basescan.org/address/0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4 -SWAP_ROUTER=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4 -# Base Sepolia NonfungiblePositionManager — https://sepolia.basescan.org/address/0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2 -NPM=0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2 +# SwapRouter02 and NonfungiblePositionManager — resolved by detect_periphery() after Anvil is verified +SWAP_ROUTER_SEPOLIA=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4 +SWAP_ROUTER_MAINNET=0x2626664c2603336E57B271c5C0b26F421741e481 +NPM_SEPOLIA=0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2 +NPM_MAINNET=0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3 +SWAP_ROUTER="" +NPM="" POOL_FEE=10000 +# Detect chain ID and select the correct periphery addresses (mirrors bootstrap-common.sh). +# Must be called after Anvil is verified to be accessible. +detect_periphery() { + local chain_id + chain_id=$("$CAST" chain-id --rpc-url "$RPC_URL" 2>/dev/null || echo "") + if [[ "$chain_id" == "8453" ]]; then + SWAP_ROUTER="$SWAP_ROUTER_MAINNET" + NPM="$NPM_MAINNET" + log "Detected Base mainnet (chain ID 8453) — using mainnet periphery addresses" + else + SWAP_ROUTER="$SWAP_ROUTER_SEPOLIA" + NPM="$NPM_SEPOLIA" + log "Using Base Sepolia periphery addresses (chain ID: ${chain_id:-unknown})" + fi +} + # ── Logging helpers ──────────────────────────────────────────────────────────── log() { echo "[red-team] $*"; } die() { echo "[red-team] ERROR: $*" >&2; exit 2; } @@ -69,6 +88,9 @@ bash "$SCRIPT_DIR/bootstrap-light.sh" || die "bootstrap-light failed" "$CAST" chain-id --rpc-url "$RPC_URL" >/dev/null 2>&1 \ || die "Anvil not accessible at $RPC_URL after bootstrap-light" +# Select network-appropriate periphery addresses +detect_periphery + # ── 2. Read contract addresses ───────────────────────────────────────────────── [[ -f "$DEPLOYMENTS" ]] || die "deployments-local.json not found at $DEPLOYMENTS (bootstrap not complete)" @@ -695,6 +717,8 @@ if [[ $EXPORT_EXIT -eq 0 && -f "$ATTACK_EXPORT" && -s "$ATTACK_EXPORT" ]]; then (cd "$REPO_ROOT/onchain" && \ ATTACK_FILE="$ATTACK_EXPORT" \ DEPLOYMENTS_FILE="deployments-local.json" \ + SWAP_ROUTER="$SWAP_ROUTER" \ + NPM_ADDR="$NPM" \ "$FORGE" script script/backtesting/AttackRunner.s.sol \ --rpc-url "$RPC_URL" --broadcast 2>&1 \ | grep '^{' >"$ATTACK_SNAPSHOTS")