Files
proxmox/scripts/bridge/run-send-cross-chain.sh
defiQUG dbd517b279 Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains
- Omit embedded publish git dirs and empty placeholders from index

Made-with: Cursor
2026-04-12 06:12:20 -07:00

206 lines
7.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Send WETH cross-chain via CCIP (Chain 138 → destination chain).
# Usage: ./scripts/bridge/run-send-cross-chain.sh <amount_eth> [recipient] [--dry-run]
# Env: CCIP_DEST_CHAIN_SELECTOR, GAS_PRICE, GAS_LIMIT, CONFIRM_ABOVE_ETH (prompt above this amount)
# Version: 2026-03-30
set -euo pipefail
[[ "${DEBUG:-0}" = "1" ]] && set -x
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
had_nounset=0
if [[ $- == *u* ]]; then
had_nounset=1
set +u
fi
source "${SCRIPT_DIR}/../lib/load-project-env.sh"
(( had_nounset )) && set -u
[[ -z "${PRIVATE_KEY:-}" ]] && { echo "PRIVATE_KEY required"; exit 1; }
[[ -z "${CCIPWETH9_BRIDGE_CHAIN138:-}" ]] && { echo "CCIPWETH9_BRIDGE_CHAIN138 required"; exit 1; }
command -v cast &>/dev/null || { echo "ERROR: cast not found (install Foundry)"; exit 1; }
DRY_RUN=false
ARGS=()
for a in "$@"; do
[[ "$a" = "--dry-run" ]] && DRY_RUN=true || ARGS+=("$a")
done
AMOUNT_ETH="${ARGS[0]:?Usage: $0 amount_eth [recipient] [--dry-run]}"
RECIPIENT="${ARGS[1]:-$(cast wallet address "$PRIVATE_KEY" 2>/dev/null)}"
SENDER_ADDR="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null)"
DEST_SELECTOR="${CCIP_DEST_CHAIN_SELECTOR:-5009297550715157269}"
GAS_PRICE="${GAS_PRICE:-1000000000}"
GAS_LIMIT="${GAS_LIMIT:-}"
RPC="${RPC_URL_138:-$CHAIN138_RPC}"
[[ -z "$RPC" ]] && { echo "ERROR: RPC_URL_138 or CHAIN138_RPC required"; exit 1; }
BRIDGE="${CCIPWETH9_BRIDGE_CHAIN138}"
extract_first_address() {
echo "$1" | grep -oE '0x[a-fA-F0-9]{40}' | sed -n '1p'
}
raw_uint() {
echo "$1" | awk '{print $1}'
}
lower() {
echo "$1" | tr '[:upper:]' '[:lower:]'
}
MAINNET_SELECTOR_VALUE="${MAINNET_SELECTOR:-5009297550715157269}"
BSC_SELECTOR_VALUE="${BSC_SELECTOR:-11344663589394136015}"
AVALANCHE_SELECTOR_VALUE="${AVALANCHE_SELECTOR:-6433500567565415381}"
assert_supported_direct_first_hop() {
case "$DEST_SELECTOR" in
"$MAINNET_SELECTOR_VALUE"|"${BSC_SELECTOR_VALUE}"|"${AVALANCHE_SELECTOR_VALUE}")
return 0
;;
esac
if [[ "${ALLOW_UNSUPPORTED_DIRECT_FIRST_HOP:-0}" = "1" ]]; then
echo "WARNING: proceeding with unsupported direct first hop for selector $DEST_SELECTOR because ALLOW_UNSUPPORTED_DIRECT_FIRST_HOP=1"
return 0
fi
cat <<EOF
ERROR: selector $DEST_SELECTOR is not a proven direct first hop from the current Chain 138 router.
The supported direct first-hop lanes today are:
- Mainnet ($MAINNET_SELECTOR_VALUE)
- BSC ($BSC_SELECTOR_VALUE)
- Avalanche ($AVALANCHE_SELECTOR_VALUE)
For Gnosis, Cronos, Celo, Polygon, Arbitrum, Optimism, and Base, use the Mainnet hub:
1. bridge 138 -> Mainnet
2. then bridge Mainnet -> destination
Set ALLOW_UNSUPPORTED_DIRECT_FIRST_HOP=1 only if you are intentionally testing an unsupported path
and accept that the source send may succeed without destination delivery.
EOF
exit 1
}
DEST_RAW="$(cast call "$BRIDGE" 'destinations(uint64)((uint64,address,bool))' "$DEST_SELECTOR" --rpc-url "$RPC" 2>/dev/null || echo "")"
DEST_ADDR="$(extract_first_address "$DEST_RAW")"
assert_supported_direct_first_hop
if [[ "$DEST_SELECTOR" == "$AVALANCHE_SELECTOR_VALUE" ]]; then
AVALANCHE_NATIVE_BRIDGE="${CCIPWETH9_BRIDGE_AVALANCHE:-}"
if [[ -n "$AVALANCHE_NATIVE_BRIDGE" ]] && [[ "$(lower "$DEST_ADDR")" == "$(lower "$AVALANCHE_NATIVE_BRIDGE")" ]] && [[ "${ALLOW_UNSUPPORTED_AVAX_NATIVE:-0}" != "1" ]]; then
cat <<EOF
ERROR: current Avalanche destination mapping points at the native AVAX WETH9 bridge ($DEST_ADDR).
That path is not live from the current Chain 138 router. On 2026-03-30, a live test message to the
native AVAX bridge remained unprocessed because the Chain 138 router emits MessageSent events but
the AVAX native bridge only accepts ccipReceive from its own trusted AVAX router.
Use the relay-backed AVAX receiver instead, or set ALLOW_UNSUPPORTED_AVAX_NATIVE=1 if you are
intentionally testing the unsupported native path.
EOF
exit 1
fi
if [[ -n "$DEST_ADDR" ]]; then
echo "Info: Avalanche send will use current mapped receiver $DEST_ADDR"
fi
fi
# Confirmation for large amounts
CONFIRM_ABOVE="${CONFIRM_ABOVE_ETH:-1}"
if [[ -n "$CONFIRM_ABOVE" ]] && awk -v a="$AMOUNT_ETH" -v b="$CONFIRM_ABOVE" 'BEGIN{exit !(a+0>=b+0)}' 2>/dev/null; then
read -p "Send $AMOUNT_ETH ETH to $RECIPIENT? [y/N] " r
[[ "${r,,}" != "y" ]] && [[ "${r,,}" != "yes" ]] && exit 0
fi
AMOUNT_WEI=$(cast --to-wei "$AMOUNT_ETH" ether)
ALLOWANCE_WETH_RAW="$(cast call "$WETH9" "allowance(address,address)(uint256)" "$SENDER_ADDR" "$BRIDGE" --rpc-url "$RPC" 2>/dev/null || echo "0")"
ALLOWANCE_WETH="$(raw_uint "$ALLOWANCE_WETH_RAW")"
if [[ "$ALLOWANCE_WETH" =~ ^[0-9]+$ ]] && (( ALLOWANCE_WETH < AMOUNT_WEI )); then
cat <<EOF
ERROR: insufficient WETH allowance for source bridge pull.
Current allowance:
token: $WETH9
owner: $SENDER_ADDR
spender: $BRIDGE
allowance_raw: $ALLOWANCE_WETH
amount_raw: $AMOUNT_WEI
Approve WETH to the Chain 138 bridge first, for example:
cast send "$WETH9" "approve(address,uint256)" "$BRIDGE" "$AMOUNT_WEI" --rpc-url "$RPC" --private-key "\$PRIVATE_KEY" --gas-price "$GAS_PRICE" --legacy
EOF
exit 1
fi
FEE_WEI=$(cast call "$BRIDGE" "calculateFee(uint64,uint256)" "$DEST_SELECTOR" "$AMOUNT_WEI" --rpc-url "$RPC" 2>/dev/null | cast --to-dec || echo "0")
FEE_TOKEN=$(cast call "$BRIDGE" "feeToken()(address)" --rpc-url "$RPC" 2>/dev/null || echo "0x0")
if [[ "$FEE_TOKEN" != "0x0000000000000000000000000000000000000000" ]] && [[ -n "$FEE_WEI" ]] && [[ "$FEE_WEI" != "0" ]]; then
ALLOWANCE_FEE_RAW="$(cast call "$FEE_TOKEN" "allowance(address,address)(uint256)" "$SENDER_ADDR" "$BRIDGE" --rpc-url "$RPC" 2>/dev/null || echo "0")"
ALLOWANCE_FEE="$(raw_uint "$ALLOWANCE_FEE_RAW")"
if [[ "$ALLOWANCE_FEE" =~ ^[0-9]+$ ]] && (( ALLOWANCE_FEE < FEE_WEI )); then
cat <<EOF
ERROR: insufficient fee-token allowance for bridge fee pull.
Current allowance:
token: $FEE_TOKEN
owner: $SENDER_ADDR
spender: $BRIDGE
allowance_raw: $ALLOWANCE_FEE
required_fee_raw: $FEE_WEI
EOF
exit 1
fi
fi
DEST_RPC=""
DEST_WETH=""
case "$DEST_SELECTOR" in
"$MAINNET_SELECTOR_VALUE")
DEST_RPC="${ETHEREUM_MAINNET_RPC:-}"
DEST_WETH="${WETH9_MAINNET:-}"
;;
"$BSC_SELECTOR_VALUE")
DEST_RPC="${BSC_RPC_URL:-${BSC_MAINNET_RPC:-}}"
DEST_WETH="${WETH9_BSC:-}"
;;
"$AVALANCHE_SELECTOR_VALUE")
DEST_RPC="${AVALANCHE_RPC_URL:-${AVALANCHE_MAINNET_RPC:-}}"
DEST_WETH="${WETH9_AVALANCHE:-}"
;;
esac
if [[ -n "$DEST_ADDR" && -n "$DEST_RPC" && -n "$DEST_WETH" ]]; then
DEST_BALANCE_RAW="$(cast call "$DEST_WETH" "balanceOf(address)(uint256)" "$DEST_ADDR" --rpc-url "$DEST_RPC" 2>/dev/null || echo "0")"
DEST_BALANCE="$(raw_uint "$DEST_BALANCE_RAW")"
if [[ "$DEST_BALANCE" =~ ^[0-9]+$ ]] && (( DEST_BALANCE < AMOUNT_WEI )); then
SHORTFALL=$(( AMOUNT_WEI - DEST_BALANCE ))
cat <<EOF
ERROR: insufficient destination relay inventory for this direct first hop.
Destination receiver: $DEST_ADDR
Destination token: $DEST_WETH
Destination balance: $DEST_BALANCE
Requested amount: $AMOUNT_WEI
Shortfall: $SHORTFALL
Top up the destination relay inventory first, or reduce the send amount.
EOF
exit 1
fi
fi
V=""; [[ "$FEE_TOKEN" = "0x0000000000000000000000000000000000000000" ]] && [[ -n "$FEE_WEI" ]] && [[ "$FEE_WEI" != "0" ]] && V="--value $FEE_WEI"
GAS_OPT=""; [[ -n "$GAS_LIMIT" ]] && GAS_OPT="--gas-limit $GAS_LIMIT"
if [[ "$DRY_RUN" = true ]]; then
echo "DRY-RUN: cast send $BRIDGE sendCrossChain($DEST_SELECTOR,$RECIPIENT,$AMOUNT_WEI) --gas-price $GAS_PRICE --legacy $V $GAS_OPT"
cast call "$BRIDGE" "sendCrossChain(uint64,address,uint256)" "$DEST_SELECTOR" "$RECIPIENT" "$AMOUNT_WEI" --rpc-url "$RPC" --private-key "$PRIVATE_KEY" $V 2>/dev/null && echo "Simulation: OK" || echo "Simulation: (check params)"
exit 0
fi
# Real execution: broadcasts sendCrossChain transaction (no --dry-run)
cast send "$BRIDGE" "sendCrossChain(uint64,address,uint256)" "$DEST_SELECTOR" "$RECIPIENT" "$AMOUNT_WEI" --rpc-url "$RPC" --private-key "$PRIVATE_KEY" --gas-price "$GAS_PRICE" --legacy $V $GAS_OPT