- Institutional / JVMTM / reserve-provenance / GRU transport + standards JSON - Validation and verify scripts (Blockscout labels, x402, GRU preflight, P1 local path) - Wormhole wiring in AGENTS, MCP_SETUP, MASTER_INDEX, 04-configuration README - Meta docs, integration gaps, live verification log, architecture updates - CI validate-config workflow updates Operator/LAN items, submodule working trees, and public token-aggregation edge routes remain follow-up (see TODOS_CONSOLIDATED P1). Made-with: Cursor
93 lines
4.1 KiB
Bash
Executable File
93 lines
4.1 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)}"
|
|
|
|
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'
|
|
}
|
|
|
|
lower() {
|
|
echo "$1" | tr '[:upper:]' '[:lower:]'
|
|
}
|
|
|
|
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")"
|
|
AVALANCHE_SELECTOR_VALUE="${AVALANCHE_SELECTOR:-6433500567565415381}"
|
|
|
|
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)
|
|
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")
|
|
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
|