- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
322 lines
11 KiB
Bash
Executable File
322 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Audit live cross-network funding routes for Chain 138 WETH9.
|
|
#
|
|
# This script distinguishes:
|
|
# - configured destination mappings on Chain 138
|
|
# - practical first-hop routes that actually work with the current relay model
|
|
# - mainnet-hub destinations that become useful only after mainnet is funded
|
|
#
|
|
# Important:
|
|
# - Chain 138 uses a custom router that emits MessageSent events but does not
|
|
# natively deliver into public-chain bridges.
|
|
# - A native-mapped destination on Chain 138 is therefore configuration signal,
|
|
# not proof of a live direct route.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
cd "$PROJECT_ROOT"
|
|
|
|
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
|
|
load_deployment_env --repo-root "$PROJECT_ROOT"
|
|
elif [[ -f "$PROJECT_ROOT/.env" ]]; then
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
source "$PROJECT_ROOT/.env"
|
|
set +a
|
|
fi
|
|
|
|
need_var() {
|
|
local name="$1"
|
|
[[ -n "${!name:-}" ]] || {
|
|
echo "Error: $name is required" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
need_var PRIVATE_KEY
|
|
need_var RPC_URL_138
|
|
need_var ETHEREUM_MAINNET_RPC
|
|
need_var CCIPWETH9_BRIDGE_CHAIN138
|
|
need_var MAINNET_CCIP_WETH9_BRIDGE
|
|
need_var ETH_MAINNET_SELECTOR
|
|
|
|
DEPLOYER="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || true)"
|
|
[[ -n "$DEPLOYER" ]] || {
|
|
echo "Error: could not derive deployer from PRIVATE_KEY" >&2
|
|
exit 1
|
|
}
|
|
|
|
CHAIN138_SELECTOR_VALUE="${CHAIN138_SELECTOR:-138}"
|
|
CURRENT_138_WETH9="${CCIPWETH9_BRIDGE_CHAIN138,,}"
|
|
LEGACY_138_WETH9="${CCIPWETH9_BRIDGE_DIRECT_LEGACY:-0x971cD9D156f193df8051E48043C476e53ECd4693}"
|
|
LEGACY_138_WETH9="${LEGACY_138_WETH9,,}"
|
|
MAINNET_WETH9="$(cast call "$MAINNET_CCIP_WETH9_BRIDGE" 'weth9()(address)' --rpc-url "$ETHEREUM_MAINNET_RPC" | tail -n1)"
|
|
MAINNET_FEE_TOKEN="$(cast call "$MAINNET_CCIP_WETH9_BRIDGE" 'feeToken()(address)' --rpc-url "$ETHEREUM_MAINNET_RPC" | tail -n1)"
|
|
|
|
fmt_ether() {
|
|
local raw="${1:-0}"
|
|
raw="$(echo "$raw" | awk '{print $1}')"
|
|
cast from-wei "$raw" ether 2>/dev/null || echo "$raw"
|
|
}
|
|
|
|
contains_true() {
|
|
[[ "$1" == *", true)"* ]]
|
|
}
|
|
|
|
extract_addr() {
|
|
echo "$1" | grep -oE '0x[a-fA-F0-9]{40}' | head -n1 | tr '[:upper:]' '[:lower:]'
|
|
}
|
|
|
|
extract_enabled_addr() {
|
|
local raw="$1"
|
|
local addr
|
|
addr="$(extract_addr "$raw")"
|
|
if contains_true "$raw" && [[ -n "$addr" ]]; then
|
|
echo "$addr"
|
|
else
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
query_dest() {
|
|
local rpc="$1"
|
|
local bridge="$2"
|
|
local selector="$3"
|
|
if [[ -z "$rpc" || -z "$bridge" || -z "$selector" ]]; then
|
|
echo "UNSET"
|
|
return 0
|
|
fi
|
|
cast call "$bridge" 'destinations(uint64)((uint64,address,bool))' "$selector" --rpc-url "$rpc" 2>/dev/null || echo "ERROR"
|
|
}
|
|
|
|
quote_fee_state() {
|
|
local rpc="$1"
|
|
local bridge="$2"
|
|
local selector="$3"
|
|
if [[ -z "$rpc" || -z "$bridge" || -z "$selector" ]]; then
|
|
echo "n/a"
|
|
return 0
|
|
fi
|
|
if cast call "$bridge" 'calculateFee(uint64,uint256)(uint256)' "$selector" 4000000000000000 --rpc-url "$rpc" >/dev/null 2>&1; then
|
|
echo "quoted"
|
|
else
|
|
echo "blocked"
|
|
fi
|
|
}
|
|
|
|
balance_of() {
|
|
local rpc="$1"
|
|
local token="$2"
|
|
local wallet="$3"
|
|
if [[ -z "$rpc" || -z "$token" || -z "$wallet" ]]; then
|
|
echo "0"
|
|
return 0
|
|
fi
|
|
cast call "$token" 'balanceOf(address)(uint256)' "$wallet" --rpc-url "$rpc" 2>/dev/null | awk '{print $1}' || echo "0"
|
|
}
|
|
|
|
native_balance() {
|
|
local rpc="$1"
|
|
if [[ -z "$rpc" ]]; then
|
|
echo "0"
|
|
return 0
|
|
fi
|
|
cast balance "$DEPLOYER" --rpc-url "$rpc" 2>/dev/null || echo "0"
|
|
}
|
|
|
|
configured_class() {
|
|
local source_addr="$1"
|
|
local expected_bridge="$2"
|
|
if [[ -z "${expected_bridge,,}" ]]; then
|
|
echo "deploy-first"
|
|
elif [[ "$source_addr" == "${expected_bridge,,}" ]]; then
|
|
echo "native-mapped"
|
|
elif [[ -n "$source_addr" ]]; then
|
|
echo "relay-or-other"
|
|
else
|
|
echo "disabled"
|
|
fi
|
|
}
|
|
|
|
practical_route() {
|
|
local name="$1"
|
|
local hub_quote="$2"
|
|
case "$name" in
|
|
mainnet|bsc|avalanche) echo "relay-backed-first-hop" ;;
|
|
wemix) echo "deploy-first" ;;
|
|
*)
|
|
if [[ "$hub_quote" == "blocked" ]]; then
|
|
echo "mainnet-hub-blocked"
|
|
else
|
|
echo "via-mainnet-hub"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
relay_inventory_state() {
|
|
local balance="$1"
|
|
if [[ "$(echo "$balance > 0" | bc)" == "1" ]]; then
|
|
echo "present"
|
|
else
|
|
echo "empty"
|
|
fi
|
|
}
|
|
|
|
detail_line() {
|
|
local name="$1"
|
|
local configured="$2"
|
|
local mainnet_to_remote="$3"
|
|
local mainnet_quote="$4"
|
|
local remote_to_138="$5"
|
|
local relay_balance="$6"
|
|
local source_addr="$7"
|
|
|
|
local parts=()
|
|
case "$configured" in
|
|
native-mapped) parts+=("138 mapping matches native bridge") ;;
|
|
relay-or-other) parts+=("138 mapping points at non-native receiver $source_addr") ;;
|
|
deploy-first) parts+=("bridge not deployed in env") ;;
|
|
*) parts+=("138 mapping missing or disabled") ;;
|
|
esac
|
|
|
|
if contains_true "$mainnet_to_remote"; then
|
|
if [[ "$mainnet_quote" == "blocked" ]]; then
|
|
parts+=("mainnet hub mapping enabled but source fee quote reverts")
|
|
else
|
|
parts+=("mainnet hub mapping enabled")
|
|
fi
|
|
else
|
|
parts+=("mainnet hub mapping missing or disabled")
|
|
fi
|
|
|
|
local back_addr
|
|
back_addr="$(extract_enabled_addr "$remote_to_138")"
|
|
if [[ -n "$back_addr" ]]; then
|
|
if [[ "$back_addr" == "$CURRENT_138_WETH9" ]]; then
|
|
parts+=("return path points at current 138 bridge")
|
|
elif [[ "$back_addr" == "$LEGACY_138_WETH9" ]]; then
|
|
parts+=("return path still points at legacy 138 bridge")
|
|
else
|
|
parts+=("return path points at unexpected 138 bridge $back_addr")
|
|
fi
|
|
fi
|
|
|
|
case "$(practical_route "$name" "$mainnet_quote")" in
|
|
relay-backed-first-hop)
|
|
parts+=("practical first hop")
|
|
if [[ -n "$relay_balance" ]]; then
|
|
parts+=("relay inventory $(relay_inventory_state "$relay_balance")")
|
|
fi
|
|
;;
|
|
via-mainnet-hub)
|
|
parts+=("use after mainnet bootstrap")
|
|
;;
|
|
mainnet-hub-blocked)
|
|
parts+=("mainnet WETH9 public fan-out currently blocked at source bridge/router")
|
|
;;
|
|
deploy-first)
|
|
parts+=("deploy and seed before use")
|
|
;;
|
|
esac
|
|
|
|
local IFS='; '
|
|
echo "${parts[*]}"
|
|
}
|
|
|
|
echo "=== Chain 138 Funding Route Audit (WETH9 rail) ==="
|
|
echo "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
|
echo "Deployer: $DEPLOYER"
|
|
echo "Current Chain 138 WETH9 bridge: $CURRENT_138_WETH9"
|
|
echo "Legacy Chain 138 WETH9 bridge: $LEGACY_138_WETH9"
|
|
echo "Mainnet WETH9 bridge: ${MAINNET_CCIP_WETH9_BRIDGE,,}"
|
|
echo ""
|
|
|
|
printf '%-10s | %-14s | %-14s | %-14s | %-16s | %-16s | %-22s | %-14s\n' \
|
|
"Chain" "Native" "Wrapped" "LINK" "138 mapping" "Mainnet hub" "Practical route" "Relay inv."
|
|
printf '%s\n' "--------------------------------------------------------------------------------------------------------------------------------------------"
|
|
|
|
CHAINS=(
|
|
"mainnet|$ETHEREUM_MAINNET_RPC|$ETH_MAINNET_SELECTOR|$MAINNET_CCIP_WETH9_BRIDGE|$MAINNET_WETH9|$MAINNET_FEE_TOKEN|${CCIP_RELAY_BRIDGE_MAINNET:-}"
|
|
"bsc|$BSC_MAINNET_RPC|$BSC_SELECTOR|$CCIPWETH9_BRIDGE_BSC|$WETH9_BSC|$LINK_TOKEN_BSC|0x886C6A4ABC064dbf74E7caEc460b7eeC31F1b78C"
|
|
"avalanche|$AVALANCHE_MAINNET_RPC|$AVALANCHE_SELECTOR|$CCIPWETH9_BRIDGE_AVALANCHE|$WETH9_AVALANCHE|$LINK_TOKEN_AVALANCHE|0x3f8C409C6072a2B6a4Ff17071927bA70F80c725F"
|
|
"gnosis|$GNOSIS_RPC|$GNOSIS_SELECTOR|$CCIPWETH9_BRIDGE_GNOSIS|$WETH9_GNOSIS|$LINK_TOKEN_GNOSIS|"
|
|
"cronos|$CRONOS_RPC|$CRONOS_SELECTOR|$CCIPWETH9_BRIDGE_CRONOS|$WETH9_CRONOS|$LINK_TOKEN_CRONOS|"
|
|
"celo|$CELO_RPC|${CELO_SELECTOR:-1346049177634351622}|$CCIPWETH9_BRIDGE_CELO|$WETH9_CELO|$LINK_TOKEN_CELO|"
|
|
"polygon|$POLYGON_MAINNET_RPC|$POLYGON_SELECTOR|$CCIPWETH9_BRIDGE_POLYGON|$WETH9_POLYGON|$LINK_TOKEN_POLYGON|"
|
|
"arbitrum|$ARBITRUM_MAINNET_RPC|$ARBITRUM_SELECTOR|$CCIPWETH9_BRIDGE_ARBITRUM|$WETH9_ARBITRUM|$LINK_TOKEN_ARBITRUM|"
|
|
"optimism|$OPTIMISM_MAINNET_RPC|$OPTIMISM_SELECTOR|$CCIPWETH9_BRIDGE_OPTIMISM|$WETH9_OPTIMISM|$LINK_TOKEN_OPTIMISM|"
|
|
"base|$BASE_MAINNET_RPC|$BASE_SELECTOR|$CCIPWETH9_BRIDGE_BASE|$WETH9_BASE|$LINK_TOKEN_BASE|"
|
|
"wemix|$WEMIX_RPC|${WEMIX_SELECTOR:-5142893604156789321}|$CCIPWETH9_BRIDGE_WEMIX|$WETH9_WEMIX|$LINK_TOKEN_WEMIX|"
|
|
)
|
|
|
|
for entry in "${CHAINS[@]}"; do
|
|
IFS='|' read -r name rpc selector expected_bridge wrapped_token link_token relay_bridge <<< "$entry"
|
|
|
|
native_raw="$(native_balance "$rpc")"
|
|
wrapped_raw="$(balance_of "$rpc" "$wrapped_token" "$DEPLOYER")"
|
|
link_raw="$(balance_of "$rpc" "$link_token" "$DEPLOYER")"
|
|
|
|
source_dest="$(query_dest "$RPC_URL_138" "$CCIPWETH9_BRIDGE_CHAIN138" "$selector")"
|
|
source_addr="$(extract_enabled_addr "$source_dest")"
|
|
mainnet_to_remote="$(query_dest "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
|
mainnet_quote="n/a"
|
|
if [[ "$name" != "mainnet" ]]; then
|
|
mainnet_quote="$(quote_fee_state "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
|
fi
|
|
mainnet_hub_flag="disabled"
|
|
if [[ "$name" == "mainnet" ]]; then
|
|
mainnet_hub_flag="n/a"
|
|
elif contains_true "$mainnet_to_remote"; then
|
|
mainnet_hub_flag="enabled/$mainnet_quote"
|
|
fi
|
|
|
|
relay_inv="n/a"
|
|
if [[ -n "$relay_bridge" && -n "$wrapped_token" ]]; then
|
|
relay_inv="$(fmt_ether "$(balance_of "$rpc" "$wrapped_token" "$relay_bridge")")"
|
|
fi
|
|
|
|
printf '%-10s | %-14s | %-14s | %-14s | %-16s | %-16s | %-22s | %-14s\n' \
|
|
"$name" \
|
|
"$(fmt_ether "$native_raw")" \
|
|
"$(fmt_ether "$wrapped_raw")" \
|
|
"$(fmt_ether "$link_raw")" \
|
|
"$(configured_class "$source_addr" "$expected_bridge")" \
|
|
"$mainnet_hub_flag" \
|
|
"$(practical_route "$name" "$mainnet_quote")" \
|
|
"$relay_inv"
|
|
done
|
|
|
|
echo ""
|
|
echo "Detail Notes"
|
|
echo "------------"
|
|
|
|
for entry in "${CHAINS[@]}"; do
|
|
IFS='|' read -r name rpc selector expected_bridge wrapped_token _link_token relay_bridge <<< "$entry"
|
|
source_dest="$(query_dest "$RPC_URL_138" "$CCIPWETH9_BRIDGE_CHAIN138" "$selector")"
|
|
source_addr="$(extract_enabled_addr "$source_dest")"
|
|
mainnet_to_remote="$(query_dest "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
|
mainnet_quote="n/a"
|
|
if [[ "$name" != "mainnet" ]]; then
|
|
mainnet_quote="$(quote_fee_state "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
|
fi
|
|
remote_to_138="$(query_dest "$rpc" "$expected_bridge" "$CHAIN138_SELECTOR_VALUE")"
|
|
relay_balance=""
|
|
if [[ -n "$relay_bridge" && -n "$wrapped_token" ]]; then
|
|
relay_balance="$(balance_of "$rpc" "$wrapped_token" "$relay_bridge")"
|
|
fi
|
|
echo "- $name: $(detail_line "$name" "$(configured_class "$source_addr" "$expected_bridge")" "$mainnet_to_remote" "$mainnet_quote" "$remote_to_138" "$relay_balance" "${source_addr:-disabled}")"
|
|
done
|
|
|
|
echo ""
|
|
echo "Important"
|
|
echo "---------"
|
|
echo "- This audit intentionally uses the WETH9 rail. Live WETH10 destination mappings are currently drifted from env and the mainnet WETH10 bridge is not wired to the public-chain destinations."
|
|
echo "- 'native-mapped' means the live Chain 138 destination address matches the expected native bridge address."
|
|
echo "- Because Chain 138 uses a custom event-emitting router, 'native-mapped' is configuration signal only, not proof of a direct live first-hop route."
|
|
echo "- Practical first hops today are the relay-backed lanes. The other public chains are best treated as mainnet-hub destinations unless a relay is added for them."
|