| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 1 | #!/usr/bin/env bash |
| 2 | # |
| check two chains with different weth 2f48480 julienbrg 4h ago | 3 | # Proves the claim the registry exists to make: LOVE lands at the same address |
| 4 | # on two chains that have nothing about their wETH in common. |
| 5 | # |
| 6 | # Two anvil nodes with different chain ids get different wETH implementations, |
| 7 | # at different addresses — node A the OP Stack predeploy, node B canonical |
| 8 | # WETH9 at its mainnet address — using the same runtime bytecode the tests |
| 9 | # etch. Each node is then brought up the way a real chain would be: registry, |
| 10 | # registration, token. If the two token addresses match, wETH is genuinely out |
| 11 | # of the creation code. |
| 12 | # |
| 13 | # The previous version of this script gave both nodes the same wETH, because |
| 14 | # back then the address was a constructor argument and matching was only |
| 15 | # possible when the argument matched. That is exactly what changed. |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 16 | # |
| 17 | # Usage: ./script/multichain-check.sh |
| 18 | # |
| check two chains with different weth 2f48480 julienbrg 4h ago | 19 | # Env overrides: PORT_A, PORT_B, CHAIN_ID_A, CHAIN_ID_B, SALT |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 20 | |
| 21 | set -euo pipefail |
| 22 | |
| 23 | PORT_A=${PORT_A:-8545} |
| 24 | PORT_B=${PORT_B:-8546} |
| 25 | CHAIN_ID_A=${CHAIN_ID_A:-31337} |
| 26 | CHAIN_ID_B=${CHAIN_ID_B:-31338} |
| 27 | |
| 28 | # First anvil dev account — local only, never use this key anywhere else. |
| 29 | ANVIL_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 |
| 30 | CREATE2_FACTORY=0x4e59b44847b379578588920cA78FbF26c0B4956C |
| 31 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 32 | # Node A: the OP Stack predeploy. Node B: canonical WETH9, where it sits on |
| 33 | # Ethereum. Different implementations, different addresses, both allowlisted. |
| 34 | WETH_A=0x4200000000000000000000000000000000000006 |
| 35 | WETH_B=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
| 36 | |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 37 | ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) |
| check two chains with different weth 2f48480 julienbrg 4h ago | 38 | FIXTURE_A="$ROOT/test/fixtures/weth9-op-legacy.hex" |
| 39 | FIXTURE_B="$ROOT/test/fixtures/weth9-canonical.hex" |
| 40 | |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 41 | RPC_A="http://127.0.0.1:$PORT_A" |
| 42 | RPC_B="http://127.0.0.1:$PORT_B" |
| 43 | TMP=$(mktemp -d) |
| 44 | PIDS=() |
| 45 | |
| 46 | cleanup() { |
| 47 | for pid in ${PIDS[@]+"${PIDS[@]}"}; do |
| 48 | kill "$pid" 2>/dev/null || true |
| 49 | done |
| 50 | rm -rf "$TMP" |
| 51 | rm -rf "$ROOT/broadcast/Love.s.sol/$CHAIN_ID_A" "$ROOT/broadcast/Love.s.sol/$CHAIN_ID_B" |
| 52 | rmdir "$ROOT/broadcast/Love.s.sol" "$ROOT/broadcast" 2>/dev/null || true |
| 53 | } |
| 54 | trap cleanup EXIT |
| 55 | |
| 56 | start_node() { |
| 57 | local port=$1 chain_id=$2 rpc=$3 |
| 58 | anvil --port "$port" --chain-id "$chain_id" --silent >"$TMP/anvil-$port.log" 2>&1 & |
| 59 | PIDS+=($!) |
| 60 | |
| 61 | for _ in $(seq 1 50); do |
| 62 | if cast chain-id --rpc-url "$rpc" >/dev/null 2>&1; then |
| 63 | return 0 |
| 64 | fi |
| 65 | sleep 0.2 |
| 66 | done |
| 67 | |
| 68 | echo "anvil on port $port never came up:" >&2 |
| 69 | cat "$TMP/anvil-$port.log" >&2 |
| 70 | exit 1 |
| 71 | } |
| 72 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 73 | # Puts real wETH runtime bytecode at an address, the way the chain itself would |
| 74 | # have it. The registry checks EXTCODEHASH, so only genuine bytecode passes — |
| 75 | # there is no test-only path through the allowlist. |
| 76 | place_weth() { |
| 77 | local rpc=$1 addr=$2 fixture=$3 label=$4 |
| 78 | |
| 79 | [[ -f $fixture ]] || { |
| 80 | echo "missing fixture $fixture" >&2 |
| 81 | exit 1 |
| 82 | } |
| 83 | |
| 84 | cast rpc anvil_setCode "$addr" "$(tr -d '\n' <"$fixture")" --rpc-url "$rpc" >/dev/null |
| 85 | |
| 86 | if [[ $(cast code "$addr" --rpc-url "$rpc") == "0x" ]]; then |
| 87 | echo "$label: failed to place wETH at $addr" >&2 |
| 88 | exit 1 |
| 89 | fi |
| 90 | } |
| 91 | |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 92 | deploy() { |
| check two chains with different weth 2f48480 julienbrg 4h ago | 93 | local rpc=$1 label=$2 weth=$3 |
| 94 | |
| 95 | WETH=$weth forge script script/Love.s.sol:LoveScript \ |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 96 | --rpc-url "$rpc" \ |
| 97 | --private-key "$ANVIL_KEY" \ |
| 98 | --broadcast \ |
| 99 | --skip-simulation >"$TMP/deploy-$label.log" 2>&1 || |
| 100 | { |
| 101 | echo "deploy on $label failed:" >&2 |
| 102 | cat "$TMP/deploy-$label.log" >&2 |
| 103 | exit 1 |
| 104 | } |
| 105 | |
| 106 | grep -oE 'predicted +0x[0-9a-fA-F]{40}' "$TMP/deploy-$label.log" | tail -1 | grep -oE '0x[0-9a-fA-F]{40}' |
| 107 | } |
| 108 | |
| 109 | report() { |
| 110 | local rpc=$1 addr=$2 label=$3 |
| 111 | local code |
| check two chains with different weth 2f48480 julienbrg 4h ago | 112 | |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 113 | code=$(cast code "$addr" --rpc-url "$rpc") |
| 114 | |
| 115 | if [[ ${#code} -le 2 ]]; then |
| 116 | echo "$label: no code at $addr" >&2 |
| 117 | exit 1 |
| 118 | fi |
| 119 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 120 | printf '%s %s %s bytes %s / %s\n' \ |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 121 | "$label" "$addr" "$(((${#code} - 2) / 2))" \ |
| 122 | "$(cast call "$addr" 'name()(string)' --rpc-url "$rpc")" \ |
| check two chains with different weth 2f48480 julienbrg 4h ago | 123 | "$(cast call "$addr" 'symbol()(string)' --rpc-url "$rpc")" |
| 124 | printf '%s registry %s\n' "$label" "$(cast call "$addr" 'REGISTRY()(address)' --rpc-url "$rpc")" |
| 125 | printf '%s weth %s\n' "$label" "$(cast call "$addr" 'WETH()(address)' --rpc-url "$rpc")" |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 126 | } |
| 127 | |
| 128 | cd "$ROOT" |
| 129 | |
| 130 | for cmd in anvil cast forge; do |
| 131 | command -v "$cmd" >/dev/null || { |
| 132 | echo "$cmd not found — install foundry" >&2 |
| 133 | exit 1 |
| 134 | } |
| 135 | done |
| 136 | |
| 137 | start_node "$PORT_A" "$CHAIN_ID_A" "$RPC_A" |
| 138 | start_node "$PORT_B" "$CHAIN_ID_B" "$RPC_B" |
| 139 | |
| 140 | for pair in "A|$RPC_A" "B|$RPC_B"; do |
| 141 | IFS='|' read -r label rpc <<<"$pair" |
| 142 | if [[ $(cast code "$CREATE2_FACTORY" --rpc-url "$rpc") == "0x" ]]; then |
| 143 | echo "$label: deterministic deployer missing at $CREATE2_FACTORY" >&2 |
| 144 | exit 1 |
| 145 | fi |
| 146 | done |
| 147 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 148 | forge build >"$TMP/build.log" 2>&1 || { |
| 149 | cat "$TMP/build.log" >&2 |
| 150 | exit 1 |
| 151 | } |
| 152 | |
| 153 | place_weth "$RPC_A" "$WETH_A" "$FIXTURE_A" A |
| 154 | place_weth "$RPC_B" "$WETH_B" "$FIXTURE_B" B |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 155 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 156 | ADDR_A=$(deploy "$RPC_A" A "$WETH_A") |
| 157 | ADDR_B=$(deploy "$RPC_B" B "$WETH_B") |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 158 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 159 | echo "chain $CHAIN_ID_A — OP Stack legacy WETH9 at $WETH_A" |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 160 | report "$RPC_A" "$ADDR_A" " A" |
| check two chains with different weth 2f48480 julienbrg 4h ago | 161 | echo "chain $CHAIN_ID_B — canonical WETH9 at $WETH_B" |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 162 | report "$RPC_B" "$ADDR_B" " B" |
| check two chains with different weth 2f48480 julienbrg 4h ago | 163 | echo |
| 164 | |
| 165 | # macOS ships bash 3.2, which has no ${var,,}. |
| 166 | lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; } |
| 167 | |
| 168 | WETH_ON_A=$(cast call "$ADDR_A" 'WETH()(address)' --rpc-url "$RPC_A") |
| 169 | WETH_ON_B=$(cast call "$ADDR_B" 'WETH()(address)' --rpc-url "$RPC_B") |
| 170 | |
| 171 | if [[ $(lower "$WETH_ON_A") == "$(lower "$WETH_ON_B")" ]]; then |
| 172 | echo "FAIL: both chains ended up on the same wETH — this proves nothing" >&2 |
| 173 | exit 1 |
| 174 | fi |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 175 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 176 | if [[ $(lower "$ADDR_A") != "$(lower "$ADDR_B")" ]]; then |
| Merge 2-multichain-check into main 7c6b16b rickub 6h ago | 177 | echo "FAIL: addresses differ" >&2 |
| 178 | exit 1 |
| 179 | fi |
| 180 | |
| check two chains with different weth 2f48480 julienbrg 4h ago | 181 | echo "OK: same LOVE address on both chains, backed by different wETH" |