add weth bytecode survey script
5a80e08 parent: 6b4a40d added
script/weth-chains.tsv +36 -0 | new file mode 100644 | ||
| @@ -0,0 +1,36 @@ | ||
| 1 | +# The wETH deployment on each chain, and a public RPC to read it from. | |
| 2 | +# | |
| 3 | +# Input to ./weth-codehashes.sh, which hashes the code at each address to find | |
| 4 | +# out how many distinct wETH implementations are actually in use. Entries are | |
| 5 | +# claims, not facts: the script calls name/symbol/decimals on each one and | |
| 6 | +# flags anything that does not answer like wETH, so a wrong address here shows | |
| 7 | +# up as a bad row rather than as a bogus hash in the allowlist. | |
| 8 | +# | |
| 9 | +# chain_id name weth rpc | |
| 10 | +1 ethereum 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 https://ethereum-rpc.publicnode.com | |
| 11 | +10 optimism 0x4200000000000000000000000000000000000006 https://optimism-rpc.publicnode.com | |
| 12 | +56 bnb-chain 0x2170Ed0880ac9A755fd29B2688956BD959F933F8 https://bsc-rpc.publicnode.com | |
| 13 | +100 gnosis 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1 https://gnosis-rpc.publicnode.com | |
| 14 | +130 unichain 0x4200000000000000000000000000000000000006 https://unichain-rpc.publicnode.com | |
| 15 | +137 polygon 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619 https://polygon-bor-rpc.publicnode.com | |
| 16 | +204 opbnb 0xE7798f023fC62146e8Aa1b36Da45fb70855a77Ea https://opbnb-rpc.publicnode.com | |
| 17 | +252 fraxtal 0xFC00000000000000000000000000000000000006 https://fraxtal.drpc.org | |
| 18 | +324 zksync-era 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 https://mainnet.era.zksync.io | |
| 19 | +480 world-chain 0x4200000000000000000000000000000000000006 https://worldchain-mainnet.g.alchemy.com/public | |
| 20 | +1088 metis 0x420000000000000000000000000000000000000A https://andromeda.metis.io/?owner=1088 | |
| 21 | +1101 polygon-zkevm 0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9 https://polygon-zkevm.drpc.org | |
| 22 | +1135 lisk 0x4200000000000000000000000000000000000006 https://rpc.api.lisk.com | |
| 23 | +1868 soneium 0x4200000000000000000000000000000000000006 https://rpc.soneium.org | |
| 24 | +5000 mantle 0xdEAddEaDdeadDEadDEADDEAdDEADDEaDDEAD1111 https://rpc.mantle.xyz | |
| 25 | +8453 base 0x4200000000000000000000000000000000000006 https://base-rpc.publicnode.com | |
| 26 | +34443 mode 0x4200000000000000000000000000000000000006 https://mode.drpc.org | |
| 27 | +42161 arbitrum-one 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1 https://arbitrum-one-rpc.publicnode.com | |
| 28 | +42220 celo 0xD221812de1BD094f35587EE8E174B07B6167D9Af https://forno.celo.org | |
| 29 | +43114 avalanche 0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB https://avalanche-c-chain-rpc.publicnode.com | |
| 30 | +57073 ink 0x4200000000000000000000000000000000000006 https://ink.drpc.org | |
| 31 | +59144 linea 0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f https://rpc.linea.build | |
| 32 | +80094 berachain 0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590 https://rpc.berachain.com | |
| 33 | +81457 blast 0x4300000000000000000000000000000000000004 https://blast.drpc.org | |
| 34 | +167000 taiko 0xA51894664A773981C6C112C43ce576f315d5b1B6 https://rpc.mainnet.taiko.xyz | |
| 35 | +534352 scroll 0x5300000000000000000000000000000000000004 https://scroll.drpc.org | |
| 36 | +7777777 zora 0x4200000000000000000000000000000000000006 https://rpc.zora.energy | |
| new file mode 100644 | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | +# The wETH deployment on each chain, and a public RPC to read it from. | ||
| 2 | +# | ||
| 3 | +# Input to ./weth-codehashes.sh, which hashes the code at each address to find | ||
| 4 | +# out how many distinct wETH implementations are actually in use. Entries are | ||
| 5 | +# claims, not facts: the script calls name/symbol/decimals on each one and | ||
| 6 | +# flags anything that does not answer like wETH, so a wrong address here shows | ||
| 7 | +# up as a bad row rather than as a bogus hash in the allowlist. | ||
| 8 | +# | ||
| 9 | +# chain_id name weth rpc | ||
| 10 | +1 ethereum 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 https://ethereum-rpc.publicnode.com | ||
| 11 | +10 optimism 0x4200000000000000000000000000000000000006 https://optimism-rpc.publicnode.com | ||
| 12 | +56 bnb-chain 0x2170Ed0880ac9A755fd29B2688956BD959F933F8 https://bsc-rpc.publicnode.com | ||
| 13 | +100 gnosis 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1 https://gnosis-rpc.publicnode.com | ||
| 14 | +130 unichain 0x4200000000000000000000000000000000000006 https://unichain-rpc.publicnode.com | ||
| 15 | +137 polygon 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619 https://polygon-bor-rpc.publicnode.com | ||
| 16 | +204 opbnb 0xE7798f023fC62146e8Aa1b36Da45fb70855a77Ea https://opbnb-rpc.publicnode.com | ||
| 17 | +252 fraxtal 0xFC00000000000000000000000000000000000006 https://fraxtal.drpc.org | ||
| 18 | +324 zksync-era 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 https://mainnet.era.zksync.io | ||
| 19 | +480 world-chain 0x4200000000000000000000000000000000000006 https://worldchain-mainnet.g.alchemy.com/public | ||
| 20 | +1088 metis 0x420000000000000000000000000000000000000A https://andromeda.metis.io/?owner=1088 | ||
| 21 | +1101 polygon-zkevm 0x4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9 https://polygon-zkevm.drpc.org | ||
| 22 | +1135 lisk 0x4200000000000000000000000000000000000006 https://rpc.api.lisk.com | ||
| 23 | +1868 soneium 0x4200000000000000000000000000000000000006 https://rpc.soneium.org | ||
| 24 | +5000 mantle 0xdEAddEaDdeadDEadDEADDEAdDEADDEaDDEAD1111 https://rpc.mantle.xyz | ||
| 25 | +8453 base 0x4200000000000000000000000000000000000006 https://base-rpc.publicnode.com | ||
| 26 | +34443 mode 0x4200000000000000000000000000000000000006 https://mode.drpc.org | ||
| 27 | +42161 arbitrum-one 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1 https://arbitrum-one-rpc.publicnode.com | ||
| 28 | +42220 celo 0xD221812de1BD094f35587EE8E174B07B6167D9Af https://forno.celo.org | ||
| 29 | +43114 avalanche 0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB https://avalanche-c-chain-rpc.publicnode.com | ||
| 30 | +57073 ink 0x4200000000000000000000000000000000000006 https://ink.drpc.org | ||
| 31 | +59144 linea 0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f https://rpc.linea.build | ||
| 32 | +80094 berachain 0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590 https://rpc.berachain.com | ||
| 33 | +81457 blast 0x4300000000000000000000000000000000000004 https://blast.drpc.org | ||
| 34 | +167000 taiko 0xA51894664A773981C6C112C43ce576f315d5b1B6 https://rpc.mainnet.taiko.xyz | ||
| 35 | +534352 scroll 0x5300000000000000000000000000000000000004 https://scroll.drpc.org | ||
| 36 | +7777777 zora 0x4200000000000000000000000000000000000006 https://rpc.zora.energy | ||
added
script/weth-codehashes.sh +163 -0 | new file mode 100755 | ||
| @@ -0,0 +1,163 @@ | ||
| 1 | +#!/usr/bin/env bash | |
| 2 | +# | |
| 3 | +# Surveys the wETH deployment on every chain in weth-chains.tsv and groups them | |
| 4 | +# by implementation, to answer the question the allowlist in | |
| 5 | +# src/WETHRegistry.sol depends on: how many distinct wETH implementations are | |
| 6 | +# actually out there? | |
| 7 | +# | |
| 8 | +# For each chain it reports three things: | |
| 9 | +# | |
| 10 | +# codehash keccak256 of the runtime code — exactly what EXTCODEHASH returns | |
| 11 | +# and what the registry compares against, so these values are the | |
| 12 | +# allowlist entries verbatim. | |
| 13 | +# family the same hash with solc's trailing metadata blob removed. That | |
| 14 | +# blob is never executed and embeds a hash of the source path and | |
| 15 | +# comments, so one implementation compiled twice gets two | |
| 16 | +# codehashes but one family. Grouping by family is how the exact | |
| 17 | +# hashes get reviewed together; the registry itself never strips. | |
| 18 | +# kind whether the code carries the ERC-20 and wrapper entrypoints | |
| 19 | +# itself. A bridged-ETH token has no deposit/withdraw, and a proxy | |
| 20 | +# has no entrypoints at all — its codehash commits to a forwarder, | |
| 21 | +# not to behaviour, so it must never be allowlisted. | |
| 22 | +# | |
| 23 | +# Chains are read with eth_getCode and hashed locally rather than with | |
| 24 | +# eth_getProof, which several public RPCs do not serve. | |
| 25 | +# | |
| 26 | +# Usage: ./script/weth-codehashes.sh [chains.tsv] | |
| 27 | +# | |
| 28 | +# Exits non-zero if any row is unreachable, so a partial survey cannot be | |
| 29 | +# mistaken for a clean one. | |
| 30 | + | |
| 31 | +set -uo pipefail | |
| 32 | + | |
| 33 | +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) | |
| 34 | +CHAINS=${1:-$ROOT/script/weth-chains.tsv} | |
| 35 | + | |
| 36 | +for cmd in cast; do | |
| 37 | + command -v "$cmd" >/dev/null || { | |
| 38 | + echo "$cmd not found — install foundry" >&2 | |
| 39 | + exit 1 | |
| 40 | + } | |
| 41 | +done | |
| 42 | + | |
| 43 | +[[ -f $CHAINS ]] || { | |
| 44 | + echo "no chain table at $CHAINS" >&2 | |
| 45 | + exit 1 | |
| 46 | +} | |
| 47 | + | |
| 48 | +# ERC-20 plus the two wrapper entrypoints. Code missing any of these is not a | |
| 49 | +# wETH this project can peg to, whatever it is called. | |
| 50 | +SELECTORS=( | |
| 51 | + d0e30db0 # deposit() | |
| 52 | + 2e1a7d4d # withdraw(uint256) | |
| 53 | + a9059cbb # transfer(address,uint256) | |
| 54 | + 70a08231 # balanceOf(address) | |
| 55 | + 095ea7b3 # approve(address,uint256) | |
| 56 | + 23b872dd # transferFrom(address,address,uint256) | |
| 57 | +) | |
| 58 | + | |
| 59 | +# Drops solc's metadata blob: the last two bytes hold its length, and the blob | |
| 60 | +# itself starts with a CBOR map (0xa1, 0xa2, …). Anything that fails those two | |
| 61 | +# sanity checks is left whole rather than guessed at. | |
| 62 | +strip_metadata() { | |
| 63 | + local hex=$1 len total marker | |
| 64 | + total=$((${#hex} / 2)) | |
| 65 | + [[ ${#hex} -gt 4 ]] || { | |
| 66 | + printf '%s' "$hex" | |
| 67 | + return | |
| 68 | + } | |
| 69 | + | |
| 70 | + len=$((16#${hex: -4})) | |
| 71 | + if ((len + 2 >= total || len == 0)); then | |
| 72 | + printf '%s' "$hex" | |
| 73 | + return | |
| 74 | + fi | |
| 75 | + | |
| 76 | + marker=${hex:$((${#hex} - (len + 2) * 2)):2} | |
| 77 | + if [[ $marker != a* ]]; then | |
| 78 | + printf '%s' "$hex" | |
| 79 | + return | |
| 80 | + fi | |
| 81 | + | |
| 82 | + printf '%s' "${hex:0:$((${#hex} - (len + 2) * 2))}" | |
| 83 | +} | |
| 84 | + | |
| 85 | +rows=() | |
| 86 | +unreachable=0 | |
| 87 | + | |
| 88 | +printf '%-16s %-9s %-8s %-12s %s\n' CHAIN ID BYTES KIND CODEHASH | |
| 89 | + | |
| 90 | +while IFS=$'\t' read -r chain_id name weth rpc; do | |
| 91 | + [[ -z ${chain_id:-} || $chain_id == \#* ]] && continue | |
| 92 | + | |
| 93 | + code=$(cast code "$weth" --rpc-url "$rpc" 2>/dev/null) | |
| 94 | + | |
| 95 | + if [[ ! $code == 0x* || ${#code} -le 4 ]]; then | |
| 96 | + printf '%-16s %-9s %-8s %-12s %s\n' "$name" "$chain_id" "-" "-" "UNREACHABLE or no code" | |
| 97 | + unreachable=$((unreachable + 1)) | |
| 98 | + continue | |
| 99 | + fi | |
| 100 | + | |
| 101 | + hex=${code#0x} | |
| 102 | + body=$(strip_metadata "$hex") | |
| 103 | + | |
| 104 | + codehash=$(cast keccak "0x$hex") | |
| 105 | + family=$(cast keccak "0x$body") | |
| 106 | + | |
| 107 | + kind=wrapper | |
| 108 | + for selector in "${SELECTORS[@]}"; do | |
| 109 | + [[ $body == *"$selector"* ]] || { | |
| 110 | + kind=not-wETH | |
| 111 | + break | |
| 112 | + } | |
| 113 | + done | |
| 114 | + | |
| 115 | + printf '%-16s %-9s %-8s %-12s %s\n' "$name" "$chain_id" "$((${#hex} / 2))" "$kind" "$codehash" | |
| 116 | + rows+=("$family|$codehash|$name|$kind|$((${#hex} / 2))") | |
| 117 | +done <"$CHAINS" | |
| 118 | + | |
| 119 | +echo | |
| 120 | +echo "families — one block per implementation, indented lines are the allowlist entries" | |
| 121 | +echo | |
| 122 | + | |
| 123 | +wrapper_chains=0 | |
| 124 | +wrapper_hashes=0 | |
| 125 | +wrapper_families=0 | |
| 126 | + | |
| 127 | +for family in $(printf '%s\n' ${rows[@]+"${rows[@]}"} | cut -d'|' -f1 | sort -u); do | |
| 128 | + block=$(printf '%s\n' ${rows[@]+"${rows[@]}"} | grep "^$family|") | |
| 129 | + kinds=$(printf '%s\n' "$block" | cut -d'|' -f4 | sort -u | paste -sd, -) | |
| 130 | + size=$(printf '%s\n' "$block" | head -1 | cut -d'|' -f5) | |
| 131 | + chains=$(printf '%s\n' "$block" | wc -l | tr -d ' ') | |
| 132 | + | |
| 133 | + printf '%s %s bytes %s chain(s) %s\n' "${family:0:18}…" "$size" "$chains" "$kinds" | |
| 134 | + | |
| 135 | + for codehash in $(printf '%s\n' "$block" | cut -d'|' -f2 | sort -u); do | |
| 136 | + names=$(printf '%s\n' "$block" | grep "|$codehash|" | cut -d'|' -f3 | sort | paste -sd' ' -) | |
| 137 | + printf ' %s %s\n' "$codehash" "$names" | |
| 138 | + | |
| 139 | + if [[ $kinds == wrapper ]]; then | |
| 140 | + wrapper_hashes=$((wrapper_hashes + 1)) | |
| 141 | + fi | |
| 142 | + done | |
| 143 | + | |
| 144 | + if [[ $kinds == wrapper ]]; then | |
| 145 | + wrapper_families=$((wrapper_families + 1)) | |
| 146 | + wrapper_chains=$((wrapper_chains + chains)) | |
| 147 | + fi | |
| 148 | + | |
| 149 | + echo | |
| 150 | +done | |
| 151 | + | |
| 152 | +printf '%s chains read, %s unreachable\n' "${#rows[@]}" "$unreachable" | |
| 153 | +printf 'wrappers: %s chains, %s families, %s exact codehashes to allowlist\n' \ | |
| 154 | + "$wrapper_chains" "$wrapper_families" "$wrapper_hashes" | |
| 155 | +echo | |
| 156 | +echo "A wrapper family is a candidate, not an entry — read its source before" | |
| 157 | +echo "adding the hash. Proxies and bridged-ETH tokens show up as not-wETH." | |
| 158 | + | |
| 159 | +[[ $unreachable -eq 0 ]] || { | |
| 160 | + echo >&2 | |
| 161 | + echo "$unreachable row(s) unreachable — survey is incomplete" >&2 | |
| 162 | + exit 1 | |
| 163 | +} | |
| new file mode 100755 | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# | ||
| 3 | +# Surveys the wETH deployment on every chain in weth-chains.tsv and groups them | ||
| 4 | +# by implementation, to answer the question the allowlist in | ||
| 5 | +# src/WETHRegistry.sol depends on: how many distinct wETH implementations are | ||
| 6 | +# actually out there? | ||
| 7 | +# | ||
| 8 | +# For each chain it reports three things: | ||
| 9 | +# | ||
| 10 | +# codehash keccak256 of the runtime code — exactly what EXTCODEHASH returns | ||
| 11 | +# and what the registry compares against, so these values are the | ||
| 12 | +# allowlist entries verbatim. | ||
| 13 | +# family the same hash with solc's trailing metadata blob removed. That | ||
| 14 | +# blob is never executed and embeds a hash of the source path and | ||
| 15 | +# comments, so one implementation compiled twice gets two | ||
| 16 | +# codehashes but one family. Grouping by family is how the exact | ||
| 17 | +# hashes get reviewed together; the registry itself never strips. | ||
| 18 | +# kind whether the code carries the ERC-20 and wrapper entrypoints | ||
| 19 | +# itself. A bridged-ETH token has no deposit/withdraw, and a proxy | ||
| 20 | +# has no entrypoints at all — its codehash commits to a forwarder, | ||
| 21 | +# not to behaviour, so it must never be allowlisted. | ||
| 22 | +# | ||
| 23 | +# Chains are read with eth_getCode and hashed locally rather than with | ||
| 24 | +# eth_getProof, which several public RPCs do not serve. | ||
| 25 | +# | ||
| 26 | +# Usage: ./script/weth-codehashes.sh [chains.tsv] | ||
| 27 | +# | ||
| 28 | +# Exits non-zero if any row is unreachable, so a partial survey cannot be | ||
| 29 | +# mistaken for a clean one. | ||
| 30 | + | ||
| 31 | +set -uo pipefail | ||
| 32 | + | ||
| 33 | +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) | ||
| 34 | +CHAINS=${1:-$ROOT/script/weth-chains.tsv} | ||
| 35 | + | ||
| 36 | +for cmd in cast; do | ||
| 37 | + command -v "$cmd" >/dev/null || { | ||
| 38 | + echo "$cmd not found — install foundry" >&2 | ||
| 39 | + exit 1 | ||
| 40 | + } | ||
| 41 | +done | ||
| 42 | + | ||
| 43 | +[[ -f $CHAINS ]] || { | ||
| 44 | + echo "no chain table at $CHAINS" >&2 | ||
| 45 | + exit 1 | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +# ERC-20 plus the two wrapper entrypoints. Code missing any of these is not a | ||
| 49 | +# wETH this project can peg to, whatever it is called. | ||
| 50 | +SELECTORS=( | ||
| 51 | + d0e30db0 # deposit() | ||
| 52 | + 2e1a7d4d # withdraw(uint256) | ||
| 53 | + a9059cbb # transfer(address,uint256) | ||
| 54 | + 70a08231 # balanceOf(address) | ||
| 55 | + 095ea7b3 # approve(address,uint256) | ||
| 56 | + 23b872dd # transferFrom(address,address,uint256) | ||
| 57 | +) | ||
| 58 | + | ||
| 59 | +# Drops solc's metadata blob: the last two bytes hold its length, and the blob | ||
| 60 | +# itself starts with a CBOR map (0xa1, 0xa2, …). Anything that fails those two | ||
| 61 | +# sanity checks is left whole rather than guessed at. | ||
| 62 | +strip_metadata() { | ||
| 63 | + local hex=$1 len total marker | ||
| 64 | + total=$((${#hex} / 2)) | ||
| 65 | + [[ ${#hex} -gt 4 ]] || { | ||
| 66 | + printf '%s' "$hex" | ||
| 67 | + return | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + len=$((16#${hex: -4})) | ||
| 71 | + if ((len + 2 >= total || len == 0)); then | ||
| 72 | + printf '%s' "$hex" | ||
| 73 | + return | ||
| 74 | + fi | ||
| 75 | + | ||
| 76 | + marker=${hex:$((${#hex} - (len + 2) * 2)):2} | ||
| 77 | + if [[ $marker != a* ]]; then | ||
| 78 | + printf '%s' "$hex" | ||
| 79 | + return | ||
| 80 | + fi | ||
| 81 | + | ||
| 82 | + printf '%s' "${hex:0:$((${#hex} - (len + 2) * 2))}" | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +rows=() | ||
| 86 | +unreachable=0 | ||
| 87 | + | ||
| 88 | +printf '%-16s %-9s %-8s %-12s %s\n' CHAIN ID BYTES KIND CODEHASH | ||
| 89 | + | ||
| 90 | +while IFS=$'\t' read -r chain_id name weth rpc; do | ||
| 91 | + [[ -z ${chain_id:-} || $chain_id == \#* ]] && continue | ||
| 92 | + | ||
| 93 | + code=$(cast code "$weth" --rpc-url "$rpc" 2>/dev/null) | ||
| 94 | + | ||
| 95 | + if [[ ! $code == 0x* || ${#code} -le 4 ]]; then | ||
| 96 | + printf '%-16s %-9s %-8s %-12s %s\n' "$name" "$chain_id" "-" "-" "UNREACHABLE or no code" | ||
| 97 | + unreachable=$((unreachable + 1)) | ||
| 98 | + continue | ||
| 99 | + fi | ||
| 100 | + | ||
| 101 | + hex=${code#0x} | ||
| 102 | + body=$(strip_metadata "$hex") | ||
| 103 | + | ||
| 104 | + codehash=$(cast keccak "0x$hex") | ||
| 105 | + family=$(cast keccak "0x$body") | ||
| 106 | + | ||
| 107 | + kind=wrapper | ||
| 108 | + for selector in "${SELECTORS[@]}"; do | ||
| 109 | + [[ $body == *"$selector"* ]] || { | ||
| 110 | + kind=not-wETH | ||
| 111 | + break | ||
| 112 | + } | ||
| 113 | + done | ||
| 114 | + | ||
| 115 | + printf '%-16s %-9s %-8s %-12s %s\n' "$name" "$chain_id" "$((${#hex} / 2))" "$kind" "$codehash" | ||
| 116 | + rows+=("$family|$codehash|$name|$kind|$((${#hex} / 2))") | ||
| 117 | +done <"$CHAINS" | ||
| 118 | + | ||
| 119 | +echo | ||
| 120 | +echo "families — one block per implementation, indented lines are the allowlist entries" | ||
| 121 | +echo | ||
| 122 | + | ||
| 123 | +wrapper_chains=0 | ||
| 124 | +wrapper_hashes=0 | ||
| 125 | +wrapper_families=0 | ||
| 126 | + | ||
| 127 | +for family in $(printf '%s\n' ${rows[@]+"${rows[@]}"} | cut -d'|' -f1 | sort -u); do | ||
| 128 | + block=$(printf '%s\n' ${rows[@]+"${rows[@]}"} | grep "^$family|") | ||
| 129 | + kinds=$(printf '%s\n' "$block" | cut -d'|' -f4 | sort -u | paste -sd, -) | ||
| 130 | + size=$(printf '%s\n' "$block" | head -1 | cut -d'|' -f5) | ||
| 131 | + chains=$(printf '%s\n' "$block" | wc -l | tr -d ' ') | ||
| 132 | + | ||
| 133 | + printf '%s %s bytes %s chain(s) %s\n' "${family:0:18}…" "$size" "$chains" "$kinds" | ||
| 134 | + | ||
| 135 | + for codehash in $(printf '%s\n' "$block" | cut -d'|' -f2 | sort -u); do | ||
| 136 | + names=$(printf '%s\n' "$block" | grep "|$codehash|" | cut -d'|' -f3 | sort | paste -sd' ' -) | ||
| 137 | + printf ' %s %s\n' "$codehash" "$names" | ||
| 138 | + | ||
| 139 | + if [[ $kinds == wrapper ]]; then | ||
| 140 | + wrapper_hashes=$((wrapper_hashes + 1)) | ||
| 141 | + fi | ||
| 142 | + done | ||
| 143 | + | ||
| 144 | + if [[ $kinds == wrapper ]]; then | ||
| 145 | + wrapper_families=$((wrapper_families + 1)) | ||
| 146 | + wrapper_chains=$((wrapper_chains + chains)) | ||
| 147 | + fi | ||
| 148 | + | ||
| 149 | + echo | ||
| 150 | +done | ||
| 151 | + | ||
| 152 | +printf '%s chains read, %s unreachable\n' "${#rows[@]}" "$unreachable" | ||
| 153 | +printf 'wrappers: %s chains, %s families, %s exact codehashes to allowlist\n' \ | ||
| 154 | + "$wrapper_chains" "$wrapper_families" "$wrapper_hashes" | ||
| 155 | +echo | ||
| 156 | +echo "A wrapper family is a candidate, not an entry — read its source before" | ||
| 157 | +echo "adding the hash. Proxies and bridged-ETH tokens show up as not-wETH." | ||
| 158 | + | ||
| 159 | +[[ $unreachable -eq 0 ]] || { | ||
| 160 | + echo >&2 | ||
| 161 | + echo "$unreachable row(s) unreachable — survey is incomplete" >&2 | ||
| 162 | + exit 1 | ||
| 163 | +} | ||