julien/lovepublic Fork 0
2f48480
Commits
Clone
git clone https://git.rickub.com/julien/love.git
git clone ssh://git@rickub.com/julien/love.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

check two chains with different weth

julienbrg committed 2026-09-17T23:41:32+02:00 Browse files
2f48480 parent: a34c2e8
modified script/multichain-check.sh +74 -17
@@ -1,13 +1,22 @@
11 #!/usr/bin/env bash
22 #
3-# Deploys Love on two local anvil nodes with different chain ids and checks
4-# that CREATE2 gives the token the same address on both. The address embeds the
5-# wETH address, so this only holds for chains sharing one wETH deployment —
6-# both nodes here are given the same WETH.
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.
716 #
817 # Usage: ./script/multichain-check.sh
918 #
10-# Env overrides: PORT_A, PORT_B, CHAIN_ID_A, CHAIN_ID_B, SALT, WETH
19+# Env overrides: PORT_A, PORT_B, CHAIN_ID_A, CHAIN_ID_B, SALT
1120
1221 set -euo pipefail
1322
@@ -20,7 +29,15 @@ CHAIN_ID_B=${CHAIN_ID_B:-31338}
2029 ANVIL_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
2130 CREATE2_FACTORY=0x4e59b44847b379578588920cA78FbF26c0B4956C
2231
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+
2337 ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
38+FIXTURE_A="$ROOT/test/fixtures/weth9-op-legacy.hex"
39+FIXTURE_B="$ROOT/test/fixtures/weth9-canonical.hex"
40+
2441 RPC_A="http://127.0.0.1:$PORT_A"
2542 RPC_B="http://127.0.0.1:$PORT_B"
2643 TMP=$(mktemp -d)
@@ -53,9 +70,29 @@ start_node() {
5370 exit 1
5471 }
5572
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+
5692 deploy() {
57- local rpc=$1 label=$2
58- forge script script/Love.s.sol:LoveScript \
93+ local rpc=$1 label=$2 weth=$3
94+
95+ WETH=$weth forge script script/Love.s.sol:LoveScript \
5996 --rpc-url "$rpc" \
6097 --private-key "$ANVIL_KEY" \
6198 --broadcast \
@@ -72,6 +109,7 @@ deploy() {
72109 report() {
73110 local rpc=$1 addr=$2 label=$3
74111 local code
112+
75113 code=$(cast code "$addr" --rpc-url "$rpc")
76114
77115 if [[ ${#code} -le 2 ]]; then
@@ -79,11 +117,12 @@ report() {
79117 exit 1
80118 fi
81119
82- printf '%s %s %s bytes %s / %s weth %s\n' \
120+ printf '%s %s %s bytes %s / %s\n' \
83121 "$label" "$addr" "$(((${#code} - 2) / 2))" \
84122 "$(cast call "$addr" 'name()(string)' --rpc-url "$rpc")" \
85- "$(cast call "$addr" 'symbol()(string)' --rpc-url "$rpc")" \
86- "$(cast call "$addr" 'WETH()(address)' --rpc-url "$rpc")"
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")"
87126 }
88127
89128 cd "$ROOT"
@@ -106,19 +145,37 @@ for pair in "A|$RPC_A" "B|$RPC_B"; do
106145 fi
107146 done
108147
109-forge build >/dev/null
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
110155
111-ADDR_A=$(deploy "$RPC_A" A)
112-ADDR_B=$(deploy "$RPC_B" B)
156+ADDR_A=$(deploy "$RPC_A" A "$WETH_A")
157+ADDR_B=$(deploy "$RPC_B" B "$WETH_B")
113158
114-echo "chain $CHAIN_ID_A"
159+echo "chain $CHAIN_ID_A — OP Stack legacy WETH9 at $WETH_A"
115160 report "$RPC_A" "$ADDR_A" " A"
116-echo "chain $CHAIN_ID_B"
161+echo "chain $CHAIN_ID_B — canonical WETH9 at $WETH_B"
117162 report "$RPC_B" "$ADDR_B" " B"
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
118175
119-if [[ "$ADDR_A" != "$ADDR_B" ]]; then
176+if [[ $(lower "$ADDR_A") != "$(lower "$ADDR_B")" ]]; then
120177 echo "FAIL: addresses differ" >&2
121178 exit 1
122179 fi
123180
124-echo "OK: same address on both chains"
181+echo "OK: same LOVE address on both chains, backed by different wETH"
@@ -1,13 +1,22 @@
1 #!/usr/bin/env bash1 #!/usr/bin/env bash
2 #2 #
3-# Deploys Love on two local anvil nodes with different chain ids and checks3+# Proves the claim the registry exists to make: LOVE lands at the same address
4-# that CREATE2 gives the token the same address on both. The address embeds the4+# on two chains that have nothing about their wETH in common.
5-# wETH address, so this only holds for chains sharing one wETH deployment —5+#
6-# both nodes here are given the same WETH.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.
7 #16 #
8 # Usage: ./script/multichain-check.sh17 # Usage: ./script/multichain-check.sh
9 #18 #
10-# Env overrides: PORT_A, PORT_B, CHAIN_ID_A, CHAIN_ID_B, SALT, WETH19+# Env overrides: PORT_A, PORT_B, CHAIN_ID_A, CHAIN_ID_B, SALT
11 20
12 set -euo pipefail21 set -euo pipefail
13 22
@@ -20,7 +29,15 @@ CHAIN_ID_B=${CHAIN_ID_B:-31338}
20 ANVIL_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff8029 ANVIL_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
21 CREATE2_FACTORY=0x4e59b44847b379578588920cA78FbF26c0B4956C30 CREATE2_FACTORY=0x4e59b44847b379578588920cA78FbF26c0B4956C
22 31
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+
23 ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)37 ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
38+FIXTURE_A="$ROOT/test/fixtures/weth9-op-legacy.hex"
39+FIXTURE_B="$ROOT/test/fixtures/weth9-canonical.hex"
40+
24 RPC_A="http://127.0.0.1:$PORT_A"41 RPC_A="http://127.0.0.1:$PORT_A"
25 RPC_B="http://127.0.0.1:$PORT_B"42 RPC_B="http://127.0.0.1:$PORT_B"
26 TMP=$(mktemp -d)43 TMP=$(mktemp -d)
@@ -53,9 +70,29 @@ start_node() {
53 exit 170 exit 1
54 }71 }
55 72
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+
56 deploy() {92 deploy() {
57- local rpc=$1 label=$293+ local rpc=$1 label=$2 weth=$3
58- forge script script/Love.s.sol:LoveScript \94+
95+ WETH=$weth forge script script/Love.s.sol:LoveScript \
59 --rpc-url "$rpc" \96 --rpc-url "$rpc" \
60 --private-key "$ANVIL_KEY" \97 --private-key "$ANVIL_KEY" \
61 --broadcast \98 --broadcast \
@@ -72,6 +109,7 @@ deploy() {
72 report() {109 report() {
73 local rpc=$1 addr=$2 label=$3110 local rpc=$1 addr=$2 label=$3
74 local code111 local code
112+
75 code=$(cast code "$addr" --rpc-url "$rpc")113 code=$(cast code "$addr" --rpc-url "$rpc")
76 114
77 if [[ ${#code} -le 2 ]]; then115 if [[ ${#code} -le 2 ]]; then
@@ -79,11 +117,12 @@ report() {
79 exit 1117 exit 1
80 fi118 fi
81 119
82- printf '%s %s %s bytes %s / %s weth %s\n' \120+ printf '%s %s %s bytes %s / %s\n' \
83 "$label" "$addr" "$(((${#code} - 2) / 2))" \121 "$label" "$addr" "$(((${#code} - 2) / 2))" \
84 "$(cast call "$addr" 'name()(string)' --rpc-url "$rpc")" \122 "$(cast call "$addr" 'name()(string)' --rpc-url "$rpc")" \
85- "$(cast call "$addr" 'symbol()(string)' --rpc-url "$rpc")" \123+ "$(cast call "$addr" 'symbol()(string)' --rpc-url "$rpc")"
86- "$(cast call "$addr" 'WETH()(address)' --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")"
87 }126 }
88 127
89 cd "$ROOT"128 cd "$ROOT"
@@ -106,19 +145,37 @@ for pair in "A|$RPC_A" "B|$RPC_B"; do
106 fi145 fi
107 done146 done
108 147
109-forge build >/dev/null148+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
110 155
111-ADDR_A=$(deploy "$RPC_A" A)156+ADDR_A=$(deploy "$RPC_A" A "$WETH_A")
112-ADDR_B=$(deploy "$RPC_B" B)157+ADDR_B=$(deploy "$RPC_B" B "$WETH_B")
113 158
114-echo "chain $CHAIN_ID_A"159+echo "chain $CHAIN_ID_A — OP Stack legacy WETH9 at $WETH_A"
115 report "$RPC_A" "$ADDR_A" " A"160 report "$RPC_A" "$ADDR_A" " A"
116-echo "chain $CHAIN_ID_B"161+echo "chain $CHAIN_ID_B — canonical WETH9 at $WETH_B"
117 report "$RPC_B" "$ADDR_B" " B"162 report "$RPC_B" "$ADDR_B" " B"
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
118 175
119-if [[ "$ADDR_A" != "$ADDR_B" ]]; then176+if [[ $(lower "$ADDR_A") != "$(lower "$ADDR_B")" ]]; then
120 echo "FAIL: addresses differ" >&2177 echo "FAIL: addresses differ" >&2
121 exit 1178 exit 1
122 fi179 fi
123 180
124-echo "OK: same address on both chains"181+echo "OK: same LOVE address on both chains, backed by different wETH"