Some checks failed
Deploy to Phoenix / validate (push) Successful in 1m10s
Deploy to Phoenix / deploy (push) Successful in 46s
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Has been cancelled
Deploy to Phoenix / cloudflare (push) Has been cancelled
phoenix-deploy Deployed to atomic-swap-dapp-live
130 lines
6.2 KiB
Bash
Executable File
130 lines
6.2 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)
|
|
#
|
|
# Prerequisites (same as a live send):
|
|
# - WETH9 on 138: allowance(signer, bridge) >= amount (bridge pulls WETH via transferFrom).
|
|
# - If feeToken() is LINK: allowance(signer, bridge) >= calculateFee(...) (bridge pulls LINK for CCIP fee).
|
|
# - On Besu Core RPC, gas estimation often fails; set e.g. GAS_LIMIT=800000 GAS_PRICE=2000000000.
|
|
#
|
|
# Dry-run: uses eth_call with --from <signer> so msg.sender matches the real transaction (do not use
|
|
# --private-key on cast call; it does not set msg.sender and simulations revert spuriously).
|
|
# Version: 2026-04-30
|
|
|
|
set -euo pipefail
|
|
[[ "${DEBUG:-0}" = "1" ]] && set -x
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=scripts/lib/load-project-env.sh
|
|
source "${SCRIPT_DIR}/../lib/load-project-env.sh"
|
|
|
|
[[ -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]}"
|
|
SENDER="$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || true)"
|
|
[[ -n "$SENDER" ]] || { echo "ERROR: could not derive signer from PRIVATE_KEY"; exit 1; }
|
|
RECIPIENT="${ARGS[1]:-$SENDER}"
|
|
|
|
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}"
|
|
|
|
WETH138="${WETH9_CHAIN138:-${WETH9:-${WETH9_ADDRESS:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}}}"
|
|
|
|
# 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 -r -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"
|
|
|
|
_allowance_dec() {
|
|
local token="$1" raw
|
|
raw=$(cast call "${token,,}" "allowance(address,address)(uint256)" "${SENDER,,}" "${BRIDGE,,}" --rpc-url "$RPC" 2>/dev/null | awk '{print $1}')
|
|
cast --to-dec "$raw" 2>/dev/null || echo "0"
|
|
}
|
|
allowance_weth() { _allowance_dec "$WETH138"; }
|
|
allowance_fee_token() {
|
|
[[ "$FEE_TOKEN" = "0x0000000000000000000000000000000000000000" ]] && echo "native" && return
|
|
_allowance_dec "$FEE_TOKEN"
|
|
}
|
|
_uint_ge() {
|
|
command -v python3 &>/dev/null || return 0
|
|
python3 -c "import sys; sys.exit(0 if int(sys.argv[1]) >= int(sys.argv[2]) else 1)" "$1" "$2"
|
|
}
|
|
|
|
print_allowance_hints() {
|
|
local w_allow fee_allow
|
|
w_allow=$(allowance_weth)
|
|
fee_allow=$(allowance_fee_token)
|
|
echo "Allowances (signer=$SENDER, bridge=$BRIDGE):"
|
|
echo " WETH $WETH138 -> bridge: ${w_allow:-0} (need >= $AMOUNT_WEI for this send)"
|
|
if [[ "$fee_allow" != "native" ]]; then
|
|
echo " feeToken ${FEE_TOKEN} -> bridge: ${fee_allow:-0} (need >= $FEE_WEI wei for this send)"
|
|
else
|
|
echo " feeToken: native ETH (fee wei from calculateFee / --value when zero address)"
|
|
fi
|
|
echo "Example approves (Chain 138, legacy gas; adjust gas as needed):"
|
|
echo " cast send ${WETH138,,} \"approve(address,uint256)\" ${BRIDGE,,} $AMOUNT_WEI --rpc-url \"\$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\" --legacy --gas-limit 120000 --gas-price 2000000000"
|
|
if [[ "$FEE_TOKEN" != "0x0000000000000000000000000000000000000000" ]]; then
|
|
echo " cast send ${FEE_TOKEN,,} \"approve(address,uint256)\" ${BRIDGE,,} $FEE_WEI --rpc-url \"\$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\" --legacy --gas-limit 120000 --gas-price 2000000000"
|
|
fi
|
|
}
|
|
|
|
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"
|
|
print_allowance_hints
|
|
echo "Simulation: eth_call as signer (msg.sender=$SENDER) ..."
|
|
# shellcheck disable=SC2086 # $V is optional --value <wei> for native fee
|
|
if cast call "$BRIDGE" "sendCrossChain(uint64,address,uint256)" "$DEST_SELECTOR" "$RECIPIENT" "$AMOUNT_WEI" \
|
|
--rpc-url "$RPC" --from "$SENDER" $V 2>/dev/null; then
|
|
echo "Simulation: OK"
|
|
else
|
|
echo "Simulation: FAILED (fix WETH/LINK allowances above, or set GAS_LIMIT for live send only)"
|
|
exit 1
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
# Preflight before live broadcast
|
|
print_allowance_hints
|
|
w_allow=$(allowance_weth)
|
|
if ! _uint_ge "$w_allow" "$AMOUNT_WEI"; then
|
|
echo "ERROR: WETH allowance to bridge is below send amount. Approve WETH to the bridge first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$FEE_TOKEN" != "0x0000000000000000000000000000000000000000" ]]; then
|
|
fee_allow=$(allowance_fee_token)
|
|
if ! _uint_ge "$fee_allow" "$FEE_WEI"; then
|
|
echo "ERROR: feeToken (LINK) allowance to bridge is below CCIP fee. Approve LINK to the bridge first." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$GAS_LIMIT" ]] && [[ "$RPC" == *"192.168.11.211"* || "$RPC" == *"192.168.11.211:8545"* ]]; then
|
|
echo "[WARN] GAS_LIMIT unset while using Core Besu RPC; if broadcast fails estimation, retry with GAS_LIMIT=800000 GAS_PRICE=2000000000" >&2
|
|
fi
|
|
|
|
# Real execution: broadcasts sendCrossChain transaction (no --dry-run)
|
|
# shellcheck disable=SC2086 # $V / $GAS_OPT optional CLI fragments
|
|
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
|