fix: red-team.sh and AttackRunner.s.sol still use Base mainnet addresses (#939)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
79b98feef9
commit
13f406b5a9
2 changed files with 47 additions and 17 deletions
|
|
@ -151,8 +151,8 @@ contract AttackRunner is Script {
|
||||||
|
|
||||||
uint24 internal constant POOL_FEE = 10_000;
|
uint24 internal constant POOL_FEE = 10_000;
|
||||||
address internal constant WETH = 0x4200000000000000000000000000000000000006;
|
address internal constant WETH = 0x4200000000000000000000000000000000000006;
|
||||||
address internal constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
|
address internal constant DEFAULT_SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
|
||||||
address internal constant NPM_ADDR = 0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3;
|
address internal constant DEFAULT_NPM_ADDR = 0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3;
|
||||||
address internal constant V3_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD; // Base mainnet
|
address internal constant V3_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD; // Base mainnet
|
||||||
|
|
||||||
// ─── Anvil test accounts ──────────────────────────────────────────────────
|
// ─── Anvil test accounts ──────────────────────────────────────────────────
|
||||||
|
|
@ -164,6 +164,8 @@ contract AttackRunner is Script {
|
||||||
|
|
||||||
// ─── Runtime state (populated in run()) ──────────────────────────────────
|
// ─── Runtime state (populated in run()) ──────────────────────────────────
|
||||||
|
|
||||||
|
address internal swapRouter;
|
||||||
|
address internal npmAddr;
|
||||||
address internal advAddr;
|
address internal advAddr;
|
||||||
address internal recenterAddr;
|
address internal recenterAddr;
|
||||||
address internal lmAddr;
|
address internal lmAddr;
|
||||||
|
|
@ -189,6 +191,10 @@ contract AttackRunner is Script {
|
||||||
// ─── Entry point ─────────────────────────────────────────────────────────
|
// ─── Entry point ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function run() external {
|
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.
|
// Load deployment addresses before broadcast.
|
||||||
string memory deploymentsPath = _deploymentsPath();
|
string memory deploymentsPath = _deploymentsPath();
|
||||||
string memory deployJson = vm.readFile(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).
|
// 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.
|
// The adversary starts with 10 000 ETH; wrapping 9 000 covers the heaviest buy sequences.
|
||||||
IWETH9(WETH).deposit{ value: 9_000 ether }();
|
IWETH9(WETH).deposit{ value: 9_000 ether }();
|
||||||
IERC20(WETH).approve(SWAP_ROUTER, type(uint256).max);
|
IERC20(WETH).approve(swapRouter, type(uint256).max);
|
||||||
IERC20(WETH).approve(NPM_ADDR, type(uint256).max);
|
IERC20(WETH).approve(npmAddr, type(uint256).max);
|
||||||
IERC20(krkAddr).approve(SWAP_ROUTER, type(uint256).max);
|
IERC20(krkAddr).approve(swapRouter, type(uint256).max);
|
||||||
IERC20(krkAddr).approve(stakeAddr, 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();
|
vm.stopBroadcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,7 +298,7 @@ contract AttackRunner is Script {
|
||||||
function _executeBuy(string memory line) internal {
|
function _executeBuy(string memory line) internal {
|
||||||
uint256 amount = vm.parseUint(vm.parseJsonString(line, ".amount"));
|
uint256 amount = vm.parseUint(vm.parseJsonString(line, ".amount"));
|
||||||
vm.startBroadcast(ADV_PK);
|
vm.startBroadcast(ADV_PK);
|
||||||
ISwapRouter02(SWAP_ROUTER).exactInputSingle(
|
ISwapRouter02(swapRouter).exactInputSingle(
|
||||||
ISwapRouter02.ExactInputSingleParams({
|
ISwapRouter02.ExactInputSingleParams({
|
||||||
tokenIn: WETH,
|
tokenIn: WETH,
|
||||||
tokenOut: krkAddr,
|
tokenOut: krkAddr,
|
||||||
|
|
@ -321,7 +327,7 @@ contract AttackRunner is Script {
|
||||||
for (uint256 i = 0; i < count; i++) {
|
for (uint256 i = 0; i < count; i++) {
|
||||||
// Buy WETH→KRK.
|
// Buy WETH→KRK.
|
||||||
vm.startBroadcast(ADV_PK);
|
vm.startBroadcast(ADV_PK);
|
||||||
ISwapRouter02(SWAP_ROUTER).exactInputSingle(
|
ISwapRouter02(swapRouter).exactInputSingle(
|
||||||
ISwapRouter02.ExactInputSingleParams({
|
ISwapRouter02.ExactInputSingleParams({
|
||||||
tokenIn: WETH,
|
tokenIn: WETH,
|
||||||
tokenOut: krkAddr,
|
tokenOut: krkAddr,
|
||||||
|
|
@ -353,7 +359,7 @@ contract AttackRunner is Script {
|
||||||
uint256 amount = _eq(amtStr, "all") ? IERC20(krkAddr).balanceOf(advAddr) : vm.parseUint(amtStr);
|
uint256 amount = _eq(amtStr, "all") ? IERC20(krkAddr).balanceOf(advAddr) : vm.parseUint(amtStr);
|
||||||
if (amount == 0) return;
|
if (amount == 0) return;
|
||||||
vm.startBroadcast(ADV_PK);
|
vm.startBroadcast(ADV_PK);
|
||||||
ISwapRouter02(SWAP_ROUTER).exactInputSingle(
|
ISwapRouter02(swapRouter).exactInputSingle(
|
||||||
ISwapRouter02.ExactInputSingleParams({
|
ISwapRouter02.ExactInputSingleParams({
|
||||||
tokenIn: krkAddr,
|
tokenIn: krkAddr,
|
||||||
tokenOut: WETH,
|
tokenOut: WETH,
|
||||||
|
|
@ -407,7 +413,7 @@ contract AttackRunner is Script {
|
||||||
(address t0, address t1) = token0isWeth ? (WETH, krkAddr) : (krkAddr, WETH);
|
(address t0, address t1) = token0isWeth ? (WETH, krkAddr) : (krkAddr, WETH);
|
||||||
|
|
||||||
vm.startBroadcast(ADV_PK);
|
vm.startBroadcast(ADV_PK);
|
||||||
INonfungiblePositionManager(NPM_ADDR).mint(
|
INonfungiblePositionManager(npmAddr).mint(
|
||||||
INonfungiblePositionManager.MintParams({
|
INonfungiblePositionManager.MintParams({
|
||||||
token0: t0,
|
token0: t0,
|
||||||
token1: t1,
|
token1: t1,
|
||||||
|
|
@ -430,11 +436,11 @@ contract AttackRunner is Script {
|
||||||
uint256 tokenId = vm.parseJsonUint(line, ".tokenId");
|
uint256 tokenId = vm.parseJsonUint(line, ".tokenId");
|
||||||
|
|
||||||
// Read current liquidity for this token.
|
// Read current liquidity for this token.
|
||||||
(,,,,,,,uint128 liquidity,,,,) = INonfungiblePositionManager(NPM_ADDR).positions(tokenId);
|
(,,,,,,,uint128 liquidity,,,,) = INonfungiblePositionManager(npmAddr).positions(tokenId);
|
||||||
if (liquidity == 0) return;
|
if (liquidity == 0) return;
|
||||||
|
|
||||||
vm.startBroadcast(ADV_PK);
|
vm.startBroadcast(ADV_PK);
|
||||||
INonfungiblePositionManager(NPM_ADDR).decreaseLiquidity(
|
INonfungiblePositionManager(npmAddr).decreaseLiquidity(
|
||||||
INonfungiblePositionManager.DecreaseLiquidityParams({
|
INonfungiblePositionManager.DecreaseLiquidityParams({
|
||||||
tokenId: tokenId,
|
tokenId: tokenId,
|
||||||
liquidity: liquidity,
|
liquidity: liquidity,
|
||||||
|
|
@ -443,7 +449,7 @@ contract AttackRunner is Script {
|
||||||
deadline: block.timestamp + 3600
|
deadline: block.timestamp + 3600
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
INonfungiblePositionManager(NPM_ADDR).collect(
|
INonfungiblePositionManager(npmAddr).collect(
|
||||||
INonfungiblePositionManager.CollectParams({
|
INonfungiblePositionManager.CollectParams({
|
||||||
tokenId: tokenId,
|
tokenId: tokenId,
|
||||||
recipient: advAddr,
|
recipient: advAddr,
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,31 @@ RECENTER_PK=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
|
||||||
|
|
||||||
# ── Infrastructure constants ───────────────────────────────────────────────────
|
# ── Infrastructure constants ───────────────────────────────────────────────────
|
||||||
WETH=0x4200000000000000000000000000000000000006
|
WETH=0x4200000000000000000000000000000000000006
|
||||||
# Base Sepolia SwapRouter02 — https://sepolia.basescan.org/address/0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4
|
# SwapRouter02 and NonfungiblePositionManager — resolved by detect_periphery() after Anvil is verified
|
||||||
SWAP_ROUTER=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4
|
SWAP_ROUTER_SEPOLIA=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4
|
||||||
# Base Sepolia NonfungiblePositionManager — https://sepolia.basescan.org/address/0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2
|
SWAP_ROUTER_MAINNET=0x2626664c2603336E57B271c5C0b26F421741e481
|
||||||
NPM=0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2
|
NPM_SEPOLIA=0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2
|
||||||
|
NPM_MAINNET=0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3
|
||||||
|
SWAP_ROUTER=""
|
||||||
|
NPM=""
|
||||||
POOL_FEE=10000
|
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 ────────────────────────────────────────────────────────────
|
# ── Logging helpers ────────────────────────────────────────────────────────────
|
||||||
log() { echo "[red-team] $*"; }
|
log() { echo "[red-team] $*"; }
|
||||||
die() { echo "[red-team] ERROR: $*" >&2; exit 2; }
|
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 \
|
"$CAST" chain-id --rpc-url "$RPC_URL" >/dev/null 2>&1 \
|
||||||
|| die "Anvil not accessible at $RPC_URL after bootstrap-light"
|
|| die "Anvil not accessible at $RPC_URL after bootstrap-light"
|
||||||
|
|
||||||
|
# Select network-appropriate periphery addresses
|
||||||
|
detect_periphery
|
||||||
|
|
||||||
# ── 2. Read contract addresses ─────────────────────────────────────────────────
|
# ── 2. Read contract addresses ─────────────────────────────────────────────────
|
||||||
[[ -f "$DEPLOYMENTS" ]] || die "deployments-local.json not found at $DEPLOYMENTS (bootstrap not complete)"
|
[[ -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" && \
|
(cd "$REPO_ROOT/onchain" && \
|
||||||
ATTACK_FILE="$ATTACK_EXPORT" \
|
ATTACK_FILE="$ATTACK_EXPORT" \
|
||||||
DEPLOYMENTS_FILE="deployments-local.json" \
|
DEPLOYMENTS_FILE="deployments-local.json" \
|
||||||
|
SWAP_ROUTER="$SWAP_ROUTER" \
|
||||||
|
NPM_ADDR="$NPM" \
|
||||||
"$FORGE" script script/backtesting/AttackRunner.s.sol \
|
"$FORGE" script script/backtesting/AttackRunner.s.sol \
|
||||||
--rpc-url "$RPC_URL" --broadcast 2>&1 \
|
--rpc-url "$RPC_URL" --broadcast 2>&1 \
|
||||||
| grep '^{' >"$ATTACK_SNAPSHOTS")
|
| grep '^{' >"$ATTACK_SNAPSHOTS")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue