feat: bridges, PMM, flash workflow, token-aggregation, and deployment docs
- 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
This commit is contained in:
321
scripts/deployment/audit-funding-bootstrap-routes.sh
Executable file
321
scripts/deployment/audit-funding-bootstrap-routes.sh
Executable file
@@ -0,0 +1,321 @@
|
||||
#!/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."
|
||||
@@ -50,10 +50,28 @@ for k in PRIVATE_KEY RPC_URL RPC_URL_138; do
|
||||
check "$k" && echo " OK $k" || echo " MISS $k"
|
||||
done
|
||||
|
||||
# PRIVATE_KEY format: 64 hex chars (no value printed)
|
||||
# PRIVATE_KEY format: 64 hex chars (no value printed). Use last assignment (dotenv override). Full value after first '='.
|
||||
if check "PRIVATE_KEY"; then
|
||||
len=$(awk -F= '/^PRIVATE_KEY=/ { v=$2; gsub(/^0x/,"",v); print length(v) }' "$ENV_FILE" 2>/dev/null || echo "0")
|
||||
[ "$len" = "64" ] && echo " PRIVATE_KEY format: 64-char hex" || echo " PRIVATE_KEY format: WARN (length=$len, expected 64)"
|
||||
v="$(awk '
|
||||
/^PRIVATE_KEY=/ { v=$0; sub(/^PRIVATE_KEY=/, "", v); last=v }
|
||||
/^export[ \t]+PRIVATE_KEY=/ { v=$0; sub(/^export[ \t]+PRIVATE_KEY=/, "", v); last=v }
|
||||
END { print last }
|
||||
' "$ENV_FILE" 2>/dev/null || true)"
|
||||
v="${v//$'\r'/}"
|
||||
v="${v#\"}"
|
||||
v="${v%\"}"
|
||||
v="${v#\'}"
|
||||
v="${v%\'}"
|
||||
v="$(printf '%s' "$v" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
||||
v="${v#0x}"
|
||||
len=${#v}
|
||||
if printf '%s' "$v" | grep -q '\${'; then
|
||||
echo " PRIVATE_KEY format: SKIP (value references another var in file; length=$len — check resolved key after source)"
|
||||
elif [ "$len" = "64" ] && printf '%s' "$v" | grep -qE '^[0-9a-fA-F]{64}$'; then
|
||||
echo " PRIVATE_KEY format: 64-char hex"
|
||||
else
|
||||
echo " PRIVATE_KEY format: WARN (length=$len, expected 64 hex chars after optional 0x)"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
||||
937
scripts/deployment/complete-nonprefunded-avax-cutover.sh
Executable file
937
scripts/deployment/complete-nonprefunded-avax-cutover.sh
Executable file
@@ -0,0 +1,937 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
|
||||
DRY_RUN=false
|
||||
START_RELAYS=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--start-relays) START_RELAYS=true ;;
|
||||
*)
|
||||
echo "Unknown option: $arg" >&2
|
||||
echo "Usage: $0 [--dry-run] [--start-relays]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
source "$SMOM_ROOT/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
|
||||
command -v forge >/dev/null 2>&1 || { echo "forge is required" >&2; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
|
||||
|
||||
DEPLOYER_ADDRESS="$(cast wallet address "$PRIVATE_KEY")"
|
||||
CHAIN138_SEND_ROUTER="${CCIP_ROUTER_CHAIN138:-0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817}"
|
||||
CHAIN138_RELAY_ROUTER="${CCIP_RELAY_ROUTER_CHAIN138:-}"
|
||||
AVAX_SEND_ROUTER="${CCIP_AVALANCHE_ROUTER:-}"
|
||||
AVAX_WETH_RELAY_ROUTER="${CCIP_RELAY_ROUTER_AVALANCHE:-0x2a0023Ad5ce1Ac6072B454575996DfFb1BB11b16}"
|
||||
AVAX_RELAY_ROUTER="${CCIP_RELAY_ROUTER_AVALANCHE_CW:-${CCIP_RELAY_ROUTER_AVALANCHE:-}}"
|
||||
AVAX_WETH_RELAY_BRIDGE="0x3f8C409C6072a2B6a4Ff17071927bA70F80c725F"
|
||||
CHAIN138_L1_BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
|
||||
AVAX_CW_BRIDGE="${CW_BRIDGE_AVALANCHE:-}"
|
||||
CW_RESERVE_VERIFIER="${CW_RESERVE_VERIFIER_CHAIN138:-}"
|
||||
CW_STABLECOIN_RESERVE_VAULT_ADDR="${CW_STABLECOIN_RESERVE_VAULT:-${STABLECOIN_RESERVE_VAULT:-}}"
|
||||
CW_RESERVE_SYSTEM_ADDR="${CW_RESERVE_SYSTEM:-${RESERVE_SYSTEM:-}}"
|
||||
CW_CANONICAL_USDT_ADDR="${CW_CANONICAL_USDT:-${COMPLIANT_USDT_ADDRESS:-${CUSDT_ADDRESS_138:-}}}"
|
||||
CW_CANONICAL_USDC_ADDR="${CW_CANONICAL_USDC:-${COMPLIANT_USDC_ADDRESS:-${CUSDC_ADDRESS_138:-}}}"
|
||||
CW_USDT_RESERVE_ASSET_ADDR="${CW_USDT_RESERVE_ASSET:-${OFFICIAL_USDT_ADDRESS:-}}"
|
||||
CW_USDC_RESERVE_ASSET_ADDR="${CW_USDC_RESERVE_ASSET:-${OFFICIAL_USDC_ADDRESS:-}}"
|
||||
CW_MAX_OUTSTANDING_USDT_AVALANCHE="${CW_MAX_OUTSTANDING_USDT_AVALANCHE:-}"
|
||||
CW_MAX_OUTSTANDING_USDC_AVALANCHE="${CW_MAX_OUTSTANDING_USDC_AVALANCHE:-}"
|
||||
DRY_RUN_AVAX_SEND_ROUTER="0x0000000000000000000000000000000000004311"
|
||||
DRY_RUN_AVAX_RELAY_ROUTER="0x0000000000000000000000000000000000004313"
|
||||
DRY_RUN_CHAIN138_RELAY_ROUTER="0x0000000000000000000000000000000000000138"
|
||||
DRY_RUN_CHAIN138_L1_BRIDGE="0x0000000000000000000000000000000000001138"
|
||||
DRY_RUN_AVAX_CW_BRIDGE="0x0000000000000000000000000000000000004312"
|
||||
DRY_RUN_CHAIN138_RESERVE_VERIFIER="0x0000000000000000000000000000000000001139"
|
||||
LOCAL_RELAY_ROUTER_BYTECODE=""
|
||||
AVAX_WETH9_ADDRESS="${WETH9_AVALANCHE:-0xa4B9DD039565AeD9641D45b57061f99d9cA6Df08}"
|
||||
ZERO_ADDRESS="0x0000000000000000000000000000000000000000"
|
||||
|
||||
log() {
|
||||
printf '[avax-cutover] %s\n' "$*"
|
||||
}
|
||||
|
||||
redact_secrets() {
|
||||
local text="$1"
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
text="${text//${PRIVATE_KEY}/\$PRIVATE_KEY}"
|
||||
fi
|
||||
printf '%s\n' "$text"
|
||||
}
|
||||
|
||||
format_command() {
|
||||
printf '%q ' "$@"
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
run_or_echo() {
|
||||
local cmd="$*"
|
||||
if $DRY_RUN; then
|
||||
redact_secrets "$cmd"
|
||||
else
|
||||
eval "$cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
run_command() {
|
||||
if $DRY_RUN; then
|
||||
redact_secrets "$(format_command "$@")"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
is_truthy() {
|
||||
case "${1:-}" in
|
||||
1|true|TRUE|yes|YES|on|ON) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
resolve_bool() {
|
||||
local value="${1:-}"
|
||||
local fallback="${2:-false}"
|
||||
if [[ -z "$value" ]]; then
|
||||
printf '%s\n' "$fallback"
|
||||
return
|
||||
fi
|
||||
if is_truthy "$value"; then
|
||||
printf 'true\n'
|
||||
else
|
||||
printf 'false\n'
|
||||
fi
|
||||
}
|
||||
|
||||
bool_to_uint() {
|
||||
if [[ "${1:-false}" == "true" ]]; then
|
||||
printf '1\n'
|
||||
else
|
||||
printf '0\n'
|
||||
fi
|
||||
}
|
||||
|
||||
DEFAULT_REQUIRE_VAULT_BACKING=false
|
||||
if [[ -n "$CW_STABLECOIN_RESERVE_VAULT_ADDR" ]]; then
|
||||
DEFAULT_REQUIRE_VAULT_BACKING=true
|
||||
fi
|
||||
|
||||
DEFAULT_REQUIRE_RESERVE_SYSTEM_BALANCE=false
|
||||
if [[ -n "$CW_RESERVE_SYSTEM_ADDR" ]]; then
|
||||
DEFAULT_REQUIRE_RESERVE_SYSTEM_BALANCE=true
|
||||
fi
|
||||
|
||||
DEFAULT_REQUIRE_TOKEN_OWNER_MATCH_VAULT=false
|
||||
if [[ -n "$CW_STABLECOIN_RESERVE_VAULT_ADDR" ]]; then
|
||||
DEFAULT_REQUIRE_TOKEN_OWNER_MATCH_VAULT=true
|
||||
fi
|
||||
|
||||
CW_ATTACH_VERIFIER_TO_L1_BOOL="$(resolve_bool "${CW_ATTACH_VERIFIER_TO_L1:-}" "true")"
|
||||
CW_REQUIRE_VAULT_BACKING_BOOL="$(resolve_bool "${CW_REQUIRE_VAULT_BACKING:-}" "$DEFAULT_REQUIRE_VAULT_BACKING")"
|
||||
CW_REQUIRE_RESERVE_SYSTEM_BALANCE_BOOL="$(resolve_bool "${CW_REQUIRE_RESERVE_SYSTEM_BALANCE:-}" "$DEFAULT_REQUIRE_RESERVE_SYSTEM_BALANCE")"
|
||||
CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT_BOOL="$(resolve_bool "${CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT:-}" "$DEFAULT_REQUIRE_TOKEN_OWNER_MATCH_VAULT")"
|
||||
CW_FREEZE_AVAX_L2_CONFIG_BOOL="$(resolve_bool "${CW_FREEZE_AVAX_L2_CONFIG:-}" "false")"
|
||||
|
||||
has_code() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
[[ -n "$address" ]] || return 1
|
||||
local code
|
||||
code="$(cast code "$address" --rpc-url "$rpc" 2>/dev/null || true)"
|
||||
[[ -n "$code" && "$code" != "0x" ]]
|
||||
}
|
||||
|
||||
contract_call_succeeds() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
shift 2
|
||||
cast call "$address" "$@" --rpc-url "$rpc" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
is_ccip_router_contract() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
has_code "$address" "$rpc" || return 1
|
||||
contract_call_succeeds "$address" "$rpc" "feeToken()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "admin()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "supportedChains(uint64)(bool)" 138
|
||||
}
|
||||
|
||||
is_relay_router_contract() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
has_code "$address" "$rpc" || return 1
|
||||
contract_call_succeeds "$address" "$rpc" "RELAYER_ROLE()(bytes32)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "authorizedBridges(address)(bool)" "$AVAX_WETH_RELAY_BRIDGE"
|
||||
}
|
||||
|
||||
get_local_relay_router_bytecode() {
|
||||
if [[ -n "$LOCAL_RELAY_ROUTER_BYTECODE" ]]; then
|
||||
printf '%s\n' "$LOCAL_RELAY_ROUTER_BYTECODE"
|
||||
return
|
||||
fi
|
||||
|
||||
LOCAL_RELAY_ROUTER_BYTECODE="$(
|
||||
cd "$SMOM_ROOT" &&
|
||||
forge inspect contracts/relay/CCIPRelayRouter.sol:CCIPRelayRouter deployedBytecode 2>/dev/null | tr '[:upper:]' '[:lower:]'
|
||||
)"
|
||||
printf '%s\n' "$LOCAL_RELAY_ROUTER_BYTECODE"
|
||||
}
|
||||
|
||||
is_current_relay_router_contract() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
is_relay_router_contract "$address" "$rpc" || return 1
|
||||
|
||||
local local_code deployed_code
|
||||
local_code="$(get_local_relay_router_bytecode)"
|
||||
deployed_code="$(cast code "$address" --rpc-url "$rpc" 2>/dev/null | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
[[ -n "$local_code" && -n "$deployed_code" && "$local_code" == "$deployed_code" ]]
|
||||
}
|
||||
|
||||
is_cw_l1_bridge() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
has_code "$address" "$rpc" || return 1
|
||||
contract_call_succeeds "$address" "$rpc" "sendRouter()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "receiveRouter()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "feeToken()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "admin()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "supportedCanonicalToken(address)(bool)" "$COMPLIANT_USDT_ADDRESS" &&
|
||||
contract_call_succeeds "$address" "$rpc" "maxOutstanding(address,uint64)(uint256)" "$COMPLIANT_USDT_ADDRESS" "$AVALANCHE_SELECTOR" &&
|
||||
contract_call_succeeds "$address" "$rpc" "reserveVerifier()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "destinations(address,uint64)((address,bool))" "$COMPLIANT_USDT_ADDRESS" "$AVALANCHE_SELECTOR" &&
|
||||
! contract_call_succeeds "$address" "$rpc" "canonicalToMirrored(address)(address)" "$COMPLIANT_USDT_ADDRESS"
|
||||
}
|
||||
|
||||
is_cw_l2_bridge() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
has_code "$address" "$rpc" || return 1
|
||||
contract_call_succeeds "$address" "$rpc" "sendRouter()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "receiveRouter()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "feeToken()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "admin()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "tokenPairFrozen(address)(bool)" "$COMPLIANT_USDT_ADDRESS" &&
|
||||
contract_call_succeeds "$address" "$rpc" "destinationFrozen(uint64)(bool)" 138 &&
|
||||
contract_call_succeeds "$address" "$rpc" "destinations(uint64)((address,bool))" 138 &&
|
||||
contract_call_succeeds "$address" "$rpc" "canonicalToMirrored(address)(address)" "$COMPLIANT_USDT_ADDRESS"
|
||||
}
|
||||
|
||||
is_reserve_verifier_contract() {
|
||||
local address="$1"
|
||||
local rpc="$2"
|
||||
has_code "$address" "$rpc" || return 1
|
||||
contract_call_succeeds "$address" "$rpc" "bridge()(address)" &&
|
||||
contract_call_succeeds "$address" "$rpc" "OPERATOR_ROLE()(bytes32)"
|
||||
}
|
||||
|
||||
send_cast() {
|
||||
local rpc="$1"
|
||||
local to="$2"
|
||||
local signature="$3"
|
||||
shift 3
|
||||
|
||||
local cmd=(cast send)
|
||||
local gas_price
|
||||
gas_price="$(cast gas-price --rpc-url "$rpc" 2>/dev/null || true)"
|
||||
if [[ "$gas_price" =~ ^[0-9]+$ && "$gas_price" -gt 0 ]]; then
|
||||
gas_price=$((gas_price + gas_price / 5 + 1000000))
|
||||
cmd+=(--gas-price "$gas_price")
|
||||
fi
|
||||
|
||||
cmd+=(--rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy "$to" "$signature")
|
||||
if [[ "$#" -gt 0 ]]; then
|
||||
cmd+=("$@")
|
||||
fi
|
||||
|
||||
run_command "${cmd[@]}"
|
||||
}
|
||||
|
||||
set_env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local escaped_value
|
||||
escaped_value="$(printf '%s' "$value" | sed -e 's/[\\/&]/\\&/g')"
|
||||
if grep -q "^${key}=" "$file"; then
|
||||
sed -i "s/^${key}=.*/${key}=${escaped_value}/" "$file"
|
||||
else
|
||||
printf '%s=%s\n' "$key" "$value" >>"$file"
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_from_broadcast() {
|
||||
local script_file="$1"
|
||||
local script_contract="$2"
|
||||
local rpc="$3"
|
||||
local chain_id="$4"
|
||||
local extra_env="$5"
|
||||
local broadcast_path="$SMOM_ROOT/broadcast/${script_file}/${chain_id}/run-latest.json"
|
||||
|
||||
if $DRY_RUN; then
|
||||
redact_secrets "$extra_env forge script script/${script_file}:${script_contract} --rpc-url \"$rpc\" --broadcast --private-key \"$PRIVATE_KEY\" --legacy -vvv"
|
||||
return 0
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$SMOM_ROOT"
|
||||
eval "$extra_env forge script script/${script_file}:${script_contract} --rpc-url \"$rpc\" --broadcast --private-key \"\$PRIVATE_KEY\" --legacy -vvv >/tmp/${script_contract}.log"
|
||||
)
|
||||
|
||||
jq -r '.transactions[] | select(.transactionType == "CREATE") | .contractAddress' "$broadcast_path" | tail -1
|
||||
}
|
||||
|
||||
ensure_avax_router() {
|
||||
if is_ccip_router_contract "$AVAX_SEND_ROUTER" "$AVALANCHE_RPC_URL"; then
|
||||
log "Using existing AVAX send router $AVAX_SEND_ROUTER"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Deploying AVAX send router"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast \
|
||||
"DeployCCIPRouter.s.sol" \
|
||||
"DeployCCIPRouter" \
|
||||
"$AVALANCHE_RPC_URL" \
|
||||
"43114" \
|
||||
"CCIP_FEE_TOKEN=$LINK_TOKEN_AVALANCHE CCIP_BASE_FEE=0 CCIP_DATA_FEE_PER_BYTE=0"
|
||||
AVAX_SEND_ROUTER="$DRY_RUN_AVAX_SEND_ROUTER"
|
||||
return
|
||||
fi
|
||||
|
||||
local addr
|
||||
addr="$(
|
||||
deploy_from_broadcast \
|
||||
"DeployCCIPRouter.s.sol" \
|
||||
"DeployCCIPRouter" \
|
||||
"$AVALANCHE_RPC_URL" \
|
||||
"43114" \
|
||||
"CCIP_FEE_TOKEN=$LINK_TOKEN_AVALANCHE CCIP_BASE_FEE=0 CCIP_DATA_FEE_PER_BYTE=0"
|
||||
)"
|
||||
if ! $DRY_RUN; then
|
||||
AVAX_SEND_ROUTER="$addr"
|
||||
set_env_value "$SMOM_ROOT/.env" "CCIP_AVALANCHE_ROUTER" "$AVAX_SEND_ROUTER"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_avax_relay_router() {
|
||||
if is_current_relay_router_contract "$AVAX_RELAY_ROUTER" "$AVALANCHE_RPC_URL"; then
|
||||
log "Using existing AVAX cW relay router $AVAX_RELAY_ROUTER"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Deploying AVAX cW relay router"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast \
|
||||
"DeployCCIPRelayRouterOnly.s.sol" \
|
||||
"DeployCCIPRelayRouterOnly" \
|
||||
"$AVALANCHE_RPC_URL" \
|
||||
"43114" \
|
||||
"RELAYER_ADDRESS=$RELAYER_ADDRESS"
|
||||
AVAX_RELAY_ROUTER="$DRY_RUN_AVAX_RELAY_ROUTER"
|
||||
return
|
||||
fi
|
||||
|
||||
local addr
|
||||
addr="$(
|
||||
deploy_from_broadcast \
|
||||
"DeployCCIPRelayRouterOnly.s.sol" \
|
||||
"DeployCCIPRelayRouterOnly" \
|
||||
"$AVALANCHE_RPC_URL" \
|
||||
"43114" \
|
||||
"RELAYER_ADDRESS=$RELAYER_ADDRESS"
|
||||
)"
|
||||
AVAX_RELAY_ROUTER="$addr"
|
||||
set_env_value "$SMOM_ROOT/.env" "CCIP_RELAY_ROUTER_AVALANCHE_CW" "$AVAX_RELAY_ROUTER"
|
||||
}
|
||||
|
||||
ensure_chain138_relay_router() {
|
||||
if is_relay_router_contract "$CHAIN138_RELAY_ROUTER" "$RPC_URL_138"; then
|
||||
log "Using existing Chain 138 relay router $CHAIN138_RELAY_ROUTER"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Deploying Chain 138 relay router"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast \
|
||||
"DeployCCIPRelayRouterOnly.s.sol" \
|
||||
"DeployCCIPRelayRouterOnly" \
|
||||
"$RPC_URL_138" \
|
||||
"138" \
|
||||
"RELAYER_ADDRESS=$RELAYER_ADDRESS"
|
||||
CHAIN138_RELAY_ROUTER="$DRY_RUN_CHAIN138_RELAY_ROUTER"
|
||||
return
|
||||
fi
|
||||
|
||||
local addr
|
||||
addr="$(
|
||||
deploy_from_broadcast \
|
||||
"DeployCCIPRelayRouterOnly.s.sol" \
|
||||
"DeployCCIPRelayRouterOnly" \
|
||||
"$RPC_URL_138" \
|
||||
"138" \
|
||||
"RELAYER_ADDRESS=$RELAYER_ADDRESS"
|
||||
)"
|
||||
if ! $DRY_RUN; then
|
||||
CHAIN138_RELAY_ROUTER="$addr"
|
||||
set_env_value "$SMOM_ROOT/.env" "CCIP_RELAY_ROUTER_CHAIN138" "$CHAIN138_RELAY_ROUTER"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_l1_bridge() {
|
||||
if is_cw_l1_bridge "$CHAIN138_L1_BRIDGE" "$RPC_URL_138"; then
|
||||
log "Using existing Chain 138 cW L1 bridge $CHAIN138_L1_BRIDGE"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Deploying Chain 138 cW L1 bridge"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast \
|
||||
"DeployCWMultiTokenBridgeL1.s.sol" \
|
||||
"DeployCWMultiTokenBridgeL1" \
|
||||
"$RPC_URL_138" \
|
||||
"138" \
|
||||
"CW_SEND_ROUTER=$CHAIN138_SEND_ROUTER CW_RECEIVE_ROUTER=$CHAIN138_RELAY_ROUTER CW_FEE_TOKEN=$LINK_TOKEN_CHAIN138"
|
||||
CHAIN138_L1_BRIDGE="$DRY_RUN_CHAIN138_L1_BRIDGE"
|
||||
return
|
||||
fi
|
||||
|
||||
local addr
|
||||
addr="$(
|
||||
deploy_from_broadcast \
|
||||
"DeployCWMultiTokenBridgeL1.s.sol" \
|
||||
"DeployCWMultiTokenBridgeL1" \
|
||||
"$RPC_URL_138" \
|
||||
"138" \
|
||||
"CW_SEND_ROUTER=$CHAIN138_SEND_ROUTER CW_RECEIVE_ROUTER=$CHAIN138_RELAY_ROUTER CW_FEE_TOKEN=$LINK_TOKEN_CHAIN138"
|
||||
)"
|
||||
if ! $DRY_RUN; then
|
||||
CHAIN138_L1_BRIDGE="$addr"
|
||||
set_env_value "$SMOM_ROOT/.env" "CW_L1_BRIDGE_CHAIN138" "$CHAIN138_L1_BRIDGE"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_l2_bridge() {
|
||||
if is_cw_l2_bridge "$AVAX_CW_BRIDGE" "$AVALANCHE_RPC_URL"; then
|
||||
log "Using existing AVAX cW bridge $AVAX_CW_BRIDGE"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Deploying AVAX cW bridge"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast \
|
||||
"DeployCWMultiTokenBridgeL2.s.sol" \
|
||||
"DeployCWMultiTokenBridgeL2" \
|
||||
"$AVALANCHE_RPC_URL" \
|
||||
"43114" \
|
||||
"CW_SEND_ROUTER=$AVAX_SEND_ROUTER CW_RECEIVE_ROUTER=$AVAX_RELAY_ROUTER CW_FEE_TOKEN=$LINK_TOKEN_AVALANCHE"
|
||||
AVAX_CW_BRIDGE="$DRY_RUN_AVAX_CW_BRIDGE"
|
||||
return
|
||||
fi
|
||||
|
||||
local addr
|
||||
addr="$(
|
||||
deploy_from_broadcast \
|
||||
"DeployCWMultiTokenBridgeL2.s.sol" \
|
||||
"DeployCWMultiTokenBridgeL2" \
|
||||
"$AVALANCHE_RPC_URL" \
|
||||
"43114" \
|
||||
"CW_SEND_ROUTER=$AVAX_SEND_ROUTER CW_RECEIVE_ROUTER=$AVAX_RELAY_ROUTER CW_FEE_TOKEN=$LINK_TOKEN_AVALANCHE"
|
||||
)"
|
||||
if ! $DRY_RUN; then
|
||||
AVAX_CW_BRIDGE="$addr"
|
||||
set_env_value "$SMOM_ROOT/.env" "CW_BRIDGE_AVALANCHE" "$AVAX_CW_BRIDGE"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_l2_receive_router() {
|
||||
local current
|
||||
current="$(cast call "$AVAX_CW_BRIDGE" "receiveRouter()(address)" --rpc-url "$AVALANCHE_RPC_URL" 2>/dev/null || true)"
|
||||
if [[ "${current,,}" == "${AVAX_RELAY_ROUTER,,}" ]]; then
|
||||
log "AVAX cW bridge already points at relay router $AVAX_RELAY_ROUTER"
|
||||
return
|
||||
fi
|
||||
send_cast "$AVALANCHE_RPC_URL" "$AVAX_CW_BRIDGE" "setReceiveRouter(address)" "$AVAX_RELAY_ROUTER"
|
||||
}
|
||||
|
||||
ensure_supported_chain() {
|
||||
local rpc="$1"
|
||||
local router="$2"
|
||||
local selector="$3"
|
||||
local label="$4"
|
||||
local current
|
||||
current="$(cast call "$router" "supportedChains(uint64)(bool)" "$selector" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "$label already supports selector $selector"
|
||||
return
|
||||
fi
|
||||
send_cast "$rpc" "$router" "addSupportedChain(uint64)" "$selector"
|
||||
}
|
||||
|
||||
ensure_authorized_bridge() {
|
||||
local rpc="$1"
|
||||
local router="$2"
|
||||
local bridge="$3"
|
||||
local current
|
||||
current="$(cast call "$router" "authorizedBridges(address)(bool)" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "Bridge $bridge already authorized on $router"
|
||||
return
|
||||
fi
|
||||
send_cast "$rpc" "$router" "authorizeBridge(address)" "$bridge"
|
||||
}
|
||||
|
||||
ensure_relayer_role() {
|
||||
local rpc="$1"
|
||||
local router="$2"
|
||||
local role
|
||||
role="$(cast keccak "RELAYER_ROLE")"
|
||||
local current
|
||||
current="$(cast call "$router" "hasRole(bytes32,address)(bool)" "$role" "$RELAYER_ADDRESS" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "Relayer role already granted on $router"
|
||||
return
|
||||
fi
|
||||
send_cast "$rpc" "$router" "grantRelayerRole(address)" "$RELAYER_ADDRESS"
|
||||
}
|
||||
|
||||
ensure_l2_token_pair() {
|
||||
local canonical="$1"
|
||||
local mirrored="$2"
|
||||
local current
|
||||
current="$(cast call "$AVAX_CW_BRIDGE" "canonicalToMirrored(address)(address)" "$canonical" --rpc-url "$AVALANCHE_RPC_URL" 2>/dev/null || echo "0x0")"
|
||||
if [[ "${current,,}" == "${mirrored,,}" ]]; then
|
||||
log "Token pair $canonical -> $mirrored already configured"
|
||||
return
|
||||
fi
|
||||
send_cast "$AVALANCHE_RPC_URL" "$AVAX_CW_BRIDGE" "configureTokenPair(address,address)" "$canonical" "$mirrored"
|
||||
}
|
||||
|
||||
ensure_destination_l1() {
|
||||
local token="$1"
|
||||
local encoded
|
||||
encoded="$(cast call "$CHAIN138_L1_BRIDGE" "destinations(address,uint64)((address,bool))" "$token" "$AVALANCHE_SELECTOR" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
encoded="${encoded,,}"
|
||||
if [[ "$encoded" == *"${AVAX_CW_BRIDGE,,}"* && "$encoded" == *"true"* ]]; then
|
||||
log "L1 destination already configured for $token"
|
||||
return
|
||||
fi
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "configureDestination(address,uint64,address,bool)" "$token" "$AVALANCHE_SELECTOR" "$AVAX_CW_BRIDGE" true
|
||||
}
|
||||
|
||||
ensure_destination_l2() {
|
||||
local encoded
|
||||
encoded="$(cast call "$AVAX_CW_BRIDGE" "destinations(uint64)((address,bool))" 138 --rpc-url "$AVALANCHE_RPC_URL" 2>/dev/null || true)"
|
||||
encoded="${encoded,,}"
|
||||
if [[ "$encoded" == *"${CHAIN138_L1_BRIDGE,,}"* && "$encoded" == *"true"* ]]; then
|
||||
log "L2 destination back to Chain 138 already configured"
|
||||
return
|
||||
fi
|
||||
send_cast "$AVALANCHE_RPC_URL" "$AVAX_CW_BRIDGE" "configureDestination(uint64,address,bool)" 138 "$CHAIN138_L1_BRIDGE" true
|
||||
}
|
||||
|
||||
ensure_supported_canonical_token() {
|
||||
local token="$1"
|
||||
local label="$2"
|
||||
local current
|
||||
current="$(cast call "$CHAIN138_L1_BRIDGE" "supportedCanonicalToken(address)(bool)" "$token" --rpc-url "$RPC_URL_138" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "$label already allowlisted on Chain 138 cW bridge"
|
||||
return
|
||||
fi
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "configureSupportedCanonicalToken(address,bool)" "$token" true
|
||||
}
|
||||
|
||||
ensure_max_outstanding() {
|
||||
local token="$1"
|
||||
local amount="$2"
|
||||
local label="$3"
|
||||
if [[ -z "$amount" ]]; then
|
||||
log "Skipping maxOutstanding for $label: CW_MAX_OUTSTANDING not set"
|
||||
return
|
||||
fi
|
||||
|
||||
local current
|
||||
current="$(cast call "$CHAIN138_L1_BRIDGE" "maxOutstanding(address,uint64)(uint256)" "$token" "$AVALANCHE_SELECTOR" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
if [[ "$current" == "$amount" ]]; then
|
||||
log "maxOutstanding already set for $label: $amount"
|
||||
return
|
||||
fi
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "setMaxOutstanding(address,uint64,uint256)" "$token" "$AVALANCHE_SELECTOR" "$amount"
|
||||
}
|
||||
|
||||
build_verifier_deploy_env() {
|
||||
local -a env_parts=()
|
||||
env_parts+=("CW_L1_BRIDGE=$CHAIN138_L1_BRIDGE")
|
||||
env_parts+=("CW_ATTACH_VERIFIER_TO_L1=$(bool_to_uint "$CW_ATTACH_VERIFIER_TO_L1_BOOL")")
|
||||
env_parts+=("CW_CANONICAL_USDT=$CW_CANONICAL_USDT_ADDR")
|
||||
env_parts+=("CW_CANONICAL_USDC=$CW_CANONICAL_USDC_ADDR")
|
||||
env_parts+=("CW_USDT_RESERVE_ASSET=${CW_USDT_RESERVE_ASSET_ADDR:-$ZERO_ADDRESS}")
|
||||
env_parts+=("CW_USDC_RESERVE_ASSET=${CW_USDC_RESERVE_ASSET_ADDR:-$ZERO_ADDRESS}")
|
||||
env_parts+=("CW_REQUIRE_VAULT_BACKING=$(bool_to_uint "$CW_REQUIRE_VAULT_BACKING_BOOL")")
|
||||
env_parts+=("CW_REQUIRE_RESERVE_SYSTEM_BALANCE=$(bool_to_uint "$CW_REQUIRE_RESERVE_SYSTEM_BALANCE_BOOL")")
|
||||
env_parts+=("CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT=$(bool_to_uint "$CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT_BOOL")")
|
||||
|
||||
if [[ -n "$CW_STABLECOIN_RESERVE_VAULT_ADDR" ]]; then
|
||||
env_parts+=("CW_STABLECOIN_RESERVE_VAULT=$CW_STABLECOIN_RESERVE_VAULT_ADDR")
|
||||
fi
|
||||
if [[ -n "$CW_RESERVE_SYSTEM_ADDR" ]]; then
|
||||
env_parts+=("CW_RESERVE_SYSTEM=$CW_RESERVE_SYSTEM_ADDR")
|
||||
fi
|
||||
|
||||
printf '%s ' "${env_parts[@]}"
|
||||
}
|
||||
|
||||
ensure_verifier_bridge_binding() {
|
||||
local current
|
||||
current="$(cast call "$CW_RESERVE_VERIFIER" "bridge()(address)" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
if [[ "${current,,}" == "${CHAIN138_L1_BRIDGE,,}" ]]; then
|
||||
return
|
||||
fi
|
||||
send_cast "$RPC_URL_138" "$CW_RESERVE_VERIFIER" "setBridge(address)" "$CHAIN138_L1_BRIDGE"
|
||||
}
|
||||
|
||||
ensure_verifier_stablecoin_reserve_vault() {
|
||||
[[ -n "$CW_STABLECOIN_RESERVE_VAULT_ADDR" ]] || return
|
||||
local current
|
||||
current="$(cast call "$CW_RESERVE_VERIFIER" "stablecoinReserveVault()(address)" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
if [[ "${current,,}" == "${CW_STABLECOIN_RESERVE_VAULT_ADDR,,}" ]]; then
|
||||
return
|
||||
fi
|
||||
send_cast "$RPC_URL_138" "$CW_RESERVE_VERIFIER" "setStablecoinReserveVault(address)" "$CW_STABLECOIN_RESERVE_VAULT_ADDR"
|
||||
}
|
||||
|
||||
ensure_verifier_reserve_system() {
|
||||
[[ -n "$CW_RESERVE_SYSTEM_ADDR" ]] || return
|
||||
local current
|
||||
current="$(cast call "$CW_RESERVE_VERIFIER" "reserveSystem()(address)" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
if [[ "${current,,}" == "${CW_RESERVE_SYSTEM_ADDR,,}" ]]; then
|
||||
return
|
||||
fi
|
||||
send_cast "$RPC_URL_138" "$CW_RESERVE_VERIFIER" "setReserveSystem(address)" "$CW_RESERVE_SYSTEM_ADDR"
|
||||
}
|
||||
|
||||
ensure_verifier_token_config() {
|
||||
local canonical="$1"
|
||||
local reserve_asset="${2:-$ZERO_ADDRESS}"
|
||||
local label="$3"
|
||||
[[ -n "$canonical" ]] || {
|
||||
log "Skipping verifier config for $label: canonical token missing"
|
||||
return
|
||||
}
|
||||
|
||||
if [[ "$CW_REQUIRE_RESERVE_SYSTEM_BALANCE_BOOL" == "true" && "$reserve_asset" == "$ZERO_ADDRESS" ]]; then
|
||||
log "Skipping verifier config for $label: reserve asset missing while reserve-system balance checks are enabled"
|
||||
return
|
||||
fi
|
||||
|
||||
local current normalized
|
||||
current="$(cast call "$CW_RESERVE_VERIFIER" "tokenConfigs(address)(bool,address,bool,bool,bool)" "$canonical" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
normalized="$(printf '%s' "$current" | tr '\n' ' ' | xargs 2>/dev/null || true)"
|
||||
|
||||
local enabled current_reserve current_require_vault current_require_reserve current_require_owner
|
||||
read -r enabled current_reserve current_require_vault current_require_reserve current_require_owner <<<"$normalized"
|
||||
|
||||
if [[ "$enabled" == "true" &&
|
||||
"${current_reserve,,}" == "${reserve_asset,,}" &&
|
||||
"$current_require_vault" == "$CW_REQUIRE_VAULT_BACKING_BOOL" &&
|
||||
"$current_require_reserve" == "$CW_REQUIRE_RESERVE_SYSTEM_BALANCE_BOOL" &&
|
||||
"$current_require_owner" == "$CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT_BOOL" ]]; then
|
||||
log "Reserve verifier already configured for $label"
|
||||
return
|
||||
fi
|
||||
|
||||
send_cast \
|
||||
"$RPC_URL_138" \
|
||||
"$CW_RESERVE_VERIFIER" \
|
||||
"configureToken(address,address,bool,bool,bool)" \
|
||||
"$canonical" \
|
||||
"$reserve_asset" \
|
||||
"$CW_REQUIRE_VAULT_BACKING_BOOL" \
|
||||
"$CW_REQUIRE_RESERVE_SYSTEM_BALANCE_BOOL" \
|
||||
"$CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT_BOOL"
|
||||
}
|
||||
|
||||
ensure_reserve_verifier() {
|
||||
local current
|
||||
current="$(cast call "$CHAIN138_L1_BRIDGE" "reserveVerifier()(address)" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
|
||||
if is_reserve_verifier_contract "$current" "$RPC_URL_138"; then
|
||||
CW_RESERVE_VERIFIER="$current"
|
||||
log "Using existing Chain 138 reserve verifier $CW_RESERVE_VERIFIER"
|
||||
elif is_reserve_verifier_contract "$CW_RESERVE_VERIFIER" "$RPC_URL_138"; then
|
||||
log "Using configured Chain 138 reserve verifier $CW_RESERVE_VERIFIER"
|
||||
else
|
||||
log "Deploying Chain 138 reserve verifier"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast \
|
||||
"DeployCWReserveVerifier.s.sol" \
|
||||
"DeployCWReserveVerifier" \
|
||||
"$RPC_URL_138" \
|
||||
"138" \
|
||||
"$(build_verifier_deploy_env)"
|
||||
CW_RESERVE_VERIFIER="$DRY_RUN_CHAIN138_RESERVE_VERIFIER"
|
||||
return
|
||||
fi
|
||||
|
||||
local addr
|
||||
addr="$(
|
||||
deploy_from_broadcast \
|
||||
"DeployCWReserveVerifier.s.sol" \
|
||||
"DeployCWReserveVerifier" \
|
||||
"$RPC_URL_138" \
|
||||
"138" \
|
||||
"$(build_verifier_deploy_env)"
|
||||
)"
|
||||
CW_RESERVE_VERIFIER="$addr"
|
||||
fi
|
||||
|
||||
if ! $DRY_RUN; then
|
||||
set_env_value "$SMOM_ROOT/.env" "CW_RESERVE_VERIFIER_CHAIN138" "$CW_RESERVE_VERIFIER"
|
||||
fi
|
||||
|
||||
if [[ "$CW_ATTACH_VERIFIER_TO_L1_BOOL" == "true" && "${current,,}" != "${CW_RESERVE_VERIFIER,,}" ]]; then
|
||||
send_cast "$RPC_URL_138" "$CHAIN138_L1_BRIDGE" "setReserveVerifier(address)" "$CW_RESERVE_VERIFIER"
|
||||
fi
|
||||
|
||||
ensure_verifier_bridge_binding
|
||||
ensure_verifier_stablecoin_reserve_vault
|
||||
ensure_verifier_reserve_system
|
||||
ensure_verifier_token_config "$CW_CANONICAL_USDT_ADDR" "${CW_USDT_RESERVE_ASSET_ADDR:-$ZERO_ADDRESS}" "cUSDT"
|
||||
ensure_verifier_token_config "$CW_CANONICAL_USDC_ADDR" "${CW_USDC_RESERVE_ASSET_ADDR:-$ZERO_ADDRESS}" "cUSDC"
|
||||
}
|
||||
|
||||
ensure_l2_token_pair_frozen() {
|
||||
local canonical="$1"
|
||||
local label="$2"
|
||||
local current
|
||||
current="$(cast call "$AVAX_CW_BRIDGE" "tokenPairFrozen(address)(bool)" "$canonical" --rpc-url "$AVALANCHE_RPC_URL" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "$label token pair already frozen on Avalanche"
|
||||
return
|
||||
fi
|
||||
send_cast "$AVALANCHE_RPC_URL" "$AVAX_CW_BRIDGE" "freezeTokenPair(address)" "$canonical"
|
||||
}
|
||||
|
||||
ensure_l2_destination_frozen() {
|
||||
local current
|
||||
current="$(cast call "$AVAX_CW_BRIDGE" "destinationFrozen(uint64)(bool)" 138 --rpc-url "$AVALANCHE_RPC_URL" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "Avalanche destination back to Chain 138 already frozen"
|
||||
return
|
||||
fi
|
||||
send_cast "$AVALANCHE_RPC_URL" "$AVAX_CW_BRIDGE" "freezeDestination(uint64)" 138
|
||||
}
|
||||
|
||||
ensure_role() {
|
||||
local rpc="$1"
|
||||
local token="$2"
|
||||
local role_name="$3"
|
||||
local grantee="$4"
|
||||
local role
|
||||
role="$(cast keccak "$role_name")"
|
||||
local current
|
||||
current="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$role" "$grantee" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
||||
if [[ "$current" == "true" ]]; then
|
||||
log "$role_name already granted on $token"
|
||||
return
|
||||
fi
|
||||
send_cast "$rpc" "$token" "grantRole(bytes32,address)" "$role" "$grantee"
|
||||
}
|
||||
|
||||
write_relay_profiles() {
|
||||
local avax_profile="$SMOM_ROOT/services/relay/.env.avax"
|
||||
local avax_cw_profile="$SMOM_ROOT/services/relay/.env.avax-cw"
|
||||
local reverse_profile="$SMOM_ROOT/services/relay/.env.avax-to-138"
|
||||
|
||||
if $DRY_RUN; then
|
||||
cat <<EOF
|
||||
# Would update $avax_profile:
|
||||
DEST_RELAY_ROUTER=${AVAX_WETH_RELAY_ROUTER}
|
||||
DEST_RELAY_BRIDGE_ALLOWLIST=${AVAX_WETH_RELAY_BRIDGE}
|
||||
|
||||
# Would write $avax_cw_profile:
|
||||
RPC_URL_138=${RPC_URL_138}
|
||||
CCIP_ROUTER_CHAIN138=${CHAIN138_SEND_ROUTER}
|
||||
CCIPWETH9_BRIDGE_CHAIN138=${CHAIN138_L1_BRIDGE}
|
||||
SOURCE_CHAIN_SELECTOR=138
|
||||
DEST_CHAIN_NAME=Avalanche
|
||||
DEST_CHAIN_ID=43114
|
||||
DEST_RPC_URL=${AVALANCHE_RPC_URL}
|
||||
DEST_CHAIN_SELECTOR=${AVALANCHE_SELECTOR}
|
||||
DEST_RELAY_ROUTER=${AVAX_RELAY_ROUTER}
|
||||
DEST_RELAY_BRIDGE=${AVAX_CW_BRIDGE}
|
||||
DEST_WETH9_ADDRESS=${AVAX_WETH9_ADDRESS}
|
||||
RELAYER_PRIVATE_KEY=\${PRIVATE_KEY}
|
||||
RELAYER_ADDRESS=${RELAYER_ADDRESS}
|
||||
START_BLOCK=latest
|
||||
POLL_INTERVAL=5000
|
||||
CONFIRMATION_BLOCKS=1
|
||||
MAX_RETRIES=3
|
||||
RETRY_DELAY=5000
|
||||
LOG_LEVEL=info
|
||||
DEST_RELAY_BRIDGE_ALLOWLIST=${AVAX_CW_BRIDGE}
|
||||
|
||||
# Would write $reverse_profile:
|
||||
SOURCE_CHAIN_NAME=Avalanche
|
||||
SOURCE_CHAIN_ID=43114
|
||||
SOURCE_CHAIN_SELECTOR=${AVALANCHE_SELECTOR}
|
||||
SOURCE_RPC_URL=https://api.avax.network/ext/bc/C/rpc
|
||||
SOURCE_ROUTER_ADDRESS=${AVAX_SEND_ROUTER}
|
||||
DEST_CHAIN_NAME=Chain 138
|
||||
DEST_CHAIN_ID=138
|
||||
DEST_CHAIN_SELECTOR=138
|
||||
DEST_RPC_URL=${RPC_URL_138}
|
||||
DEST_RELAY_ROUTER=${CHAIN138_RELAY_ROUTER}
|
||||
DEST_RELAY_BRIDGE=${CHAIN138_L1_BRIDGE}
|
||||
DEST_RELAY_BRIDGE_ALLOWLIST=${CHAIN138_L1_BRIDGE}
|
||||
RELAYER_PRIVATE_KEY=\${PRIVATE_KEY}
|
||||
RELAYER_ADDRESS=${RELAYER_ADDRESS}
|
||||
RELAY_DEST_LEGACY_TX=1
|
||||
RELAY_DEST_GAS_PRICE_BUMP_PCT=20
|
||||
RELAY_DEST_GAS_PRICE_BUMP_WEI=1000000
|
||||
EOF
|
||||
return
|
||||
fi
|
||||
|
||||
set_env_value "$avax_profile" "DEST_RELAY_ROUTER" "$AVAX_WETH_RELAY_ROUTER"
|
||||
set_env_value "$avax_profile" "DEST_RELAY_BRIDGE_ALLOWLIST" "$AVAX_WETH_RELAY_BRIDGE"
|
||||
|
||||
cat >"$avax_cw_profile" <<EOF
|
||||
# Forward relay profile for non-prefunded AVAX cW minting.
|
||||
RPC_URL_138=${RPC_URL_138}
|
||||
CCIP_ROUTER_CHAIN138=${CHAIN138_SEND_ROUTER}
|
||||
CCIPWETH9_BRIDGE_CHAIN138=${CHAIN138_L1_BRIDGE}
|
||||
SOURCE_CHAIN_SELECTOR=138
|
||||
|
||||
DEST_CHAIN_NAME=Avalanche
|
||||
DEST_CHAIN_ID=43114
|
||||
DEST_RPC_URL=${AVALANCHE_RPC_URL}
|
||||
DEST_CHAIN_SELECTOR=${AVALANCHE_SELECTOR}
|
||||
DEST_RELAY_ROUTER=${AVAX_RELAY_ROUTER}
|
||||
DEST_RELAY_BRIDGE=${AVAX_CW_BRIDGE}
|
||||
DEST_WETH9_ADDRESS=${AVAX_WETH9_ADDRESS}
|
||||
|
||||
RELAYER_PRIVATE_KEY=\${PRIVATE_KEY}
|
||||
RELAYER_ADDRESS=${RELAYER_ADDRESS}
|
||||
|
||||
START_BLOCK=latest
|
||||
POLL_INTERVAL=5000
|
||||
CONFIRMATION_BLOCKS=1
|
||||
MAX_RETRIES=3
|
||||
RETRY_DELAY=5000
|
||||
LOG_LEVEL=info
|
||||
DEST_RELAY_BRIDGE_ALLOWLIST=${AVAX_CW_BRIDGE}
|
||||
EOF
|
||||
|
||||
cat >"$reverse_profile" <<EOF
|
||||
# Reverse relay profile for AVAX cW burns back to Chain 138.
|
||||
SOURCE_CHAIN_NAME=Avalanche
|
||||
SOURCE_CHAIN_ID=43114
|
||||
SOURCE_CHAIN_SELECTOR=${AVALANCHE_SELECTOR}
|
||||
SOURCE_RPC_URL=https://api.avax.network/ext/bc/C/rpc
|
||||
SOURCE_ROUTER_ADDRESS=${AVAX_SEND_ROUTER}
|
||||
|
||||
DEST_CHAIN_NAME=Chain 138
|
||||
DEST_CHAIN_ID=138
|
||||
DEST_CHAIN_SELECTOR=138
|
||||
DEST_RPC_URL=${RPC_URL_138}
|
||||
DEST_RELAY_ROUTER=${CHAIN138_RELAY_ROUTER}
|
||||
DEST_RELAY_BRIDGE=${CHAIN138_L1_BRIDGE}
|
||||
DEST_RELAY_BRIDGE_ALLOWLIST=${CHAIN138_L1_BRIDGE}
|
||||
|
||||
RELAYER_PRIVATE_KEY=\${PRIVATE_KEY}
|
||||
RELAYER_ADDRESS=${RELAYER_ADDRESS}
|
||||
|
||||
START_BLOCK=latest
|
||||
POLL_INTERVAL=5000
|
||||
CONFIRMATION_BLOCKS=1
|
||||
MAX_RETRIES=3
|
||||
RETRY_DELAY=5000
|
||||
LOG_LEVEL=info
|
||||
RELAY_DEST_LEGACY_TX=1
|
||||
RELAY_DEST_GAS_PRICE_BUMP_PCT=20
|
||||
RELAY_DEST_GAS_PRICE_BUMP_WEI=1000000
|
||||
EOF
|
||||
}
|
||||
|
||||
start_relays() {
|
||||
if ! $START_RELAYS; then
|
||||
return
|
||||
fi
|
||||
if $DRY_RUN; then
|
||||
echo "(cd \"$SMOM_ROOT/services/relay\" && RELAY_SKIP_ENV_LOCAL=1 ./start-relay.sh avax)"
|
||||
echo "(cd \"$SMOM_ROOT/services/relay\" && RELAY_SKIP_ENV_LOCAL=1 ./start-relay.sh avax-cw)"
|
||||
echo "(cd \"$SMOM_ROOT/services/relay\" && RELAY_SKIP_ENV_LOCAL=1 ./start-relay.sh avax-to-138)"
|
||||
return
|
||||
fi
|
||||
|
||||
(cd "$SMOM_ROOT/services/relay" && nohup env RELAY_SKIP_ENV_LOCAL=1 ./start-relay.sh avax >/tmp/relay-avax.log 2>&1 &)
|
||||
(cd "$SMOM_ROOT/services/relay" && nohup env RELAY_SKIP_ENV_LOCAL=1 ./start-relay.sh avax-cw >/tmp/relay-avax-cw.log 2>&1 &)
|
||||
(cd "$SMOM_ROOT/services/relay" && nohup env RELAY_SKIP_ENV_LOCAL=1 ./start-relay.sh avax-to-138 >/tmp/relay-avax-to-138.log 2>&1 &)
|
||||
}
|
||||
|
||||
log "Deployer: $DEPLOYER_ADDRESS"
|
||||
log "Dry run: $DRY_RUN"
|
||||
|
||||
ensure_avax_router
|
||||
ensure_avax_relay_router
|
||||
ensure_chain138_relay_router
|
||||
ensure_l1_bridge
|
||||
ensure_l2_bridge
|
||||
|
||||
if ! $DRY_RUN; then
|
||||
ensure_supported_chain "$AVALANCHE_RPC_URL" "$AVAX_SEND_ROUTER" 138 "AVAX router"
|
||||
fi
|
||||
|
||||
ensure_l2_receive_router
|
||||
ensure_authorized_bridge "$AVALANCHE_RPC_URL" "$AVAX_RELAY_ROUTER" "$AVAX_CW_BRIDGE"
|
||||
ensure_relayer_role "$AVALANCHE_RPC_URL" "$AVAX_RELAY_ROUTER"
|
||||
ensure_authorized_bridge "$RPC_URL_138" "$CHAIN138_RELAY_ROUTER" "$CHAIN138_L1_BRIDGE"
|
||||
ensure_relayer_role "$RPC_URL_138" "$CHAIN138_RELAY_ROUTER"
|
||||
|
||||
ensure_l2_token_pair "$COMPLIANT_USDT_ADDRESS" "$CWUSDT_AVALANCHE"
|
||||
ensure_l2_token_pair "$COMPLIANT_USDC_ADDRESS" "$CWUSDC_AVALANCHE"
|
||||
|
||||
ensure_role "$AVALANCHE_RPC_URL" "$CWUSDT_AVALANCHE" "MINTER_ROLE" "$AVAX_CW_BRIDGE"
|
||||
ensure_role "$AVALANCHE_RPC_URL" "$CWUSDT_AVALANCHE" "BURNER_ROLE" "$AVAX_CW_BRIDGE"
|
||||
ensure_role "$AVALANCHE_RPC_URL" "$CWUSDC_AVALANCHE" "MINTER_ROLE" "$AVAX_CW_BRIDGE"
|
||||
ensure_role "$AVALANCHE_RPC_URL" "$CWUSDC_AVALANCHE" "BURNER_ROLE" "$AVAX_CW_BRIDGE"
|
||||
|
||||
ensure_supported_canonical_token "$CW_CANONICAL_USDT_ADDR" "cUSDT"
|
||||
ensure_supported_canonical_token "$CW_CANONICAL_USDC_ADDR" "cUSDC"
|
||||
ensure_max_outstanding "$CW_CANONICAL_USDT_ADDR" "$CW_MAX_OUTSTANDING_USDT_AVALANCHE" "cUSDT -> Avalanche"
|
||||
ensure_max_outstanding "$CW_CANONICAL_USDC_ADDR" "$CW_MAX_OUTSTANDING_USDC_AVALANCHE" "cUSDC -> Avalanche"
|
||||
|
||||
ensure_destination_l1 "$COMPLIANT_USDT_ADDRESS"
|
||||
ensure_destination_l1 "$COMPLIANT_USDC_ADDRESS"
|
||||
ensure_destination_l2
|
||||
ensure_reserve_verifier
|
||||
|
||||
if [[ "$CW_FREEZE_AVAX_L2_CONFIG_BOOL" == "true" ]]; then
|
||||
ensure_l2_token_pair_frozen "$CW_CANONICAL_USDT_ADDR" "cUSDT"
|
||||
ensure_l2_token_pair_frozen "$CW_CANONICAL_USDC_ADDR" "cUSDC"
|
||||
ensure_l2_destination_frozen
|
||||
else
|
||||
log "Skipping Avalanche L2 freeze; set CW_FREEZE_AVAX_L2_CONFIG=1 to lock token pairs and destination"
|
||||
fi
|
||||
|
||||
write_relay_profiles
|
||||
start_relays
|
||||
|
||||
log "Summary:"
|
||||
log " AVAX send router: ${AVAX_SEND_ROUTER:-<dry-run>}"
|
||||
log " AVAX cW relay router: ${AVAX_RELAY_ROUTER:-<dry-run>}"
|
||||
log " Chain 138 relay router:${CHAIN138_RELAY_ROUTER:-<dry-run>}"
|
||||
log " Chain 138 L1 bridge: ${CHAIN138_L1_BRIDGE:-<dry-run>}"
|
||||
log " AVAX cW bridge: ${AVAX_CW_BRIDGE:-<dry-run>}"
|
||||
log " Chain 138 verifier: ${CW_RESERVE_VERIFIER:-<not-set>}"
|
||||
log " Freeze AVAX L2 config: ${CW_FREEZE_AVAX_L2_CONFIG_BOOL}"
|
||||
274
scripts/deployment/deploy-cw-stablecoin-vault-and-wire.sh
Executable file
274
scripts/deployment/deploy-cw-stablecoin-vault-and-wire.sh
Executable file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
WITH_CUTOVER=false
|
||||
DRY_RUN=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--with-cutover) WITH_CUTOVER=true ;;
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
*)
|
||||
echo "Unknown option: $arg" >&2
|
||||
echo "Usage: $0 [--dry-run] [--with-cutover]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
source "$SMOM_ROOT/scripts/load-env.sh" >/dev/null 2>&1
|
||||
|
||||
command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
|
||||
command -v forge >/dev/null 2>&1 || { echo "forge is required" >&2; exit 1; }
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
|
||||
|
||||
ENV_FILE="$SMOM_ROOT/.env"
|
||||
DEPLOYER_ADDRESS="$(cast wallet address "$PRIVATE_KEY")"
|
||||
RPC="$RPC_URL_138"
|
||||
CW_VAULT="${CW_STABLECOIN_RESERVE_VAULT:-}"
|
||||
TARGET_USDT_BACKING="${CW_VAULT_SEED_USDT_AMOUNT:-}"
|
||||
TARGET_USDC_BACKING="${CW_VAULT_SEED_USDC_AMOUNT:-}"
|
||||
|
||||
log() {
|
||||
printf '[cw-vault] %s\n' "$*"
|
||||
}
|
||||
|
||||
redact_secrets() {
|
||||
local text="$1"
|
||||
if [[ -n "${PRIVATE_KEY:-}" ]]; then
|
||||
text="${text//${PRIVATE_KEY}/\$PRIVATE_KEY}"
|
||||
fi
|
||||
printf '%s\n' "$text"
|
||||
}
|
||||
|
||||
format_command() {
|
||||
printf '%q ' "$@"
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
run_command() {
|
||||
if $DRY_RUN; then
|
||||
redact_secrets "$(format_command "$@")"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
has_code() {
|
||||
local address="$1"
|
||||
[[ -n "$address" ]] || return 1
|
||||
local code
|
||||
code="$(cast code "$address" --rpc-url "$RPC" 2>/dev/null || true)"
|
||||
[[ -n "$code" && "$code" != "0x" ]]
|
||||
}
|
||||
|
||||
send_cast() {
|
||||
local to="$1"
|
||||
local signature="$2"
|
||||
shift 2
|
||||
|
||||
local cmd=(cast send)
|
||||
local gas_price
|
||||
gas_price="$(cast gas-price --rpc-url "$RPC" 2>/dev/null || true)"
|
||||
if [[ "$gas_price" =~ ^[0-9]+$ && "$gas_price" -gt 0 ]]; then
|
||||
gas_price=$((gas_price + gas_price / 5 + 1000000))
|
||||
cmd+=(--gas-price "$gas_price")
|
||||
fi
|
||||
|
||||
cmd+=(--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy "$to" "$signature")
|
||||
if [[ "$#" -gt 0 ]]; then
|
||||
cmd+=("$@")
|
||||
fi
|
||||
|
||||
run_command "${cmd[@]}"
|
||||
}
|
||||
|
||||
set_env_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local escaped_value
|
||||
escaped_value="$(printf '%s' "$value" | sed -e 's/[\\/&]/\\&/g')"
|
||||
if grep -q "^${key}=" "$file"; then
|
||||
sed -i "s/^${key}=.*/${key}=${escaped_value}/" "$file"
|
||||
else
|
||||
printf '%s=%s\n' "$key" "$value" >>"$file"
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_from_broadcast() {
|
||||
local script_file="$1"
|
||||
local script_contract="$2"
|
||||
local extra_env="$3"
|
||||
local broadcast_path="$SMOM_ROOT/broadcast/${script_file}/138/run-latest.json"
|
||||
|
||||
if $DRY_RUN; then
|
||||
redact_secrets "$extra_env forge script script/reserve/${script_file}:${script_contract} --rpc-url \"$RPC\" --broadcast --private-key \"$PRIVATE_KEY\" --legacy -vvv"
|
||||
return 0
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$SMOM_ROOT"
|
||||
eval "$extra_env forge script script/reserve/${script_file}:${script_contract} --rpc-url \"$RPC\" --broadcast --private-key \"\$PRIVATE_KEY\" --legacy -vvv >/tmp/${script_contract}.log"
|
||||
)
|
||||
|
||||
jq -r '.transactions[] | select(.transactionType == "CREATE") | .contractAddress' "$broadcast_path" | tail -1
|
||||
}
|
||||
|
||||
call_uint() {
|
||||
cast call "$1" "$2" "${@:3}" --rpc-url "$RPC"
|
||||
}
|
||||
|
||||
call_addr() {
|
||||
cast call "$1" "$2" --rpc-url "$RPC"
|
||||
}
|
||||
|
||||
normalize_uint() {
|
||||
printf '%s\n' "$1" | awk '{print $1}'
|
||||
}
|
||||
|
||||
ensure_vault() {
|
||||
if has_code "$CW_VAULT"; then
|
||||
log "Using existing stablecoin reserve vault $CW_VAULT"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Deploying stablecoin reserve vault"
|
||||
if $DRY_RUN; then
|
||||
deploy_from_broadcast "DeployStablecoinReserveVault.s.sol" "DeployStablecoinReserveVault" ""
|
||||
CW_VAULT="0x0000000000000000000000000000000000000140"
|
||||
return
|
||||
fi
|
||||
|
||||
CW_VAULT="$(
|
||||
deploy_from_broadcast "DeployStablecoinReserveVault.s.sol" "DeployStablecoinReserveVault" ""
|
||||
)"
|
||||
set_env_value "$ENV_FILE" "CW_STABLECOIN_RESERVE_VAULT" "$CW_VAULT"
|
||||
}
|
||||
|
||||
ensure_official_balance() {
|
||||
local token="$1"
|
||||
local amount_needed="$2"
|
||||
local label="$3"
|
||||
local owner balance shortfall
|
||||
|
||||
owner="$(cast call "$token" "owner()(address)" --rpc-url "$RPC")"
|
||||
balance="$(normalize_uint "$(cast call "$token" "balanceOf(address)(uint256)" "$DEPLOYER_ADDRESS" --rpc-url "$RPC")")"
|
||||
|
||||
if [[ "$balance" =~ ^[0-9]+$ ]] && (( balance >= amount_needed )); then
|
||||
log "$label deployer balance already sufficient: $balance"
|
||||
return
|
||||
fi
|
||||
|
||||
shortfall=$((amount_needed - balance))
|
||||
if [[ "${owner,,}" != "${DEPLOYER_ADDRESS,,}" ]]; then
|
||||
echo "Deployer is not owner of $label token $token; cannot mint shortfall $shortfall" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( shortfall > 0 )); then
|
||||
send_cast "$token" "mint(address,uint256)" "$DEPLOYER_ADDRESS" "$shortfall"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_seeded_backing() {
|
||||
local official_token="$1"
|
||||
local reserve_getter="$2"
|
||||
local seed_signature="$3"
|
||||
local target_amount="$4"
|
||||
local label="$5"
|
||||
|
||||
local current_reserve shortfall
|
||||
if $DRY_RUN; then
|
||||
current_reserve=0
|
||||
else
|
||||
current_reserve="$(normalize_uint "$(cast call "$CW_VAULT" "$reserve_getter" --rpc-url "$RPC")")"
|
||||
fi
|
||||
if [[ -z "$target_amount" ]]; then
|
||||
target_amount="$(normalize_uint "$(cast call "$official_token" "totalSupply()(uint256)" --rpc-url "$RPC")")"
|
||||
else
|
||||
target_amount="$(normalize_uint "$target_amount")"
|
||||
fi
|
||||
|
||||
if (( current_reserve >= target_amount )); then
|
||||
log "$label reserve already seeded: $current_reserve"
|
||||
return
|
||||
fi
|
||||
|
||||
shortfall=$((target_amount - current_reserve))
|
||||
ensure_official_balance "$official_token" "$shortfall" "$label"
|
||||
send_cast "$official_token" "approve(address,uint256)" "$CW_VAULT" "$shortfall"
|
||||
send_cast "$CW_VAULT" "$seed_signature" "$shortfall"
|
||||
}
|
||||
|
||||
ensure_compliant_owner() {
|
||||
local token="$1"
|
||||
local label="$2"
|
||||
local owner
|
||||
if $DRY_RUN; then
|
||||
owner="$DEPLOYER_ADDRESS"
|
||||
else
|
||||
owner="$(cast call "$token" "owner()(address)" --rpc-url "$RPC")"
|
||||
fi
|
||||
if [[ "${owner,,}" == "${CW_VAULT,,}" ]]; then
|
||||
log "$label ownership already transferred to vault"
|
||||
return
|
||||
fi
|
||||
if [[ "${owner,,}" != "${DEPLOYER_ADDRESS,,}" ]]; then
|
||||
echo "$label owner is $owner, expected deployer or vault" >&2
|
||||
exit 1
|
||||
fi
|
||||
send_cast "$token" "transferOwnership(address)" "$CW_VAULT"
|
||||
}
|
||||
|
||||
verify_vault_state() {
|
||||
local usdt_owner usdc_owner usdt_ratio usdc_ratio
|
||||
usdt_owner="$(cast call "$COMPLIANT_USDT_ADDRESS" "owner()(address)" --rpc-url "$RPC")"
|
||||
usdc_owner="$(cast call "$COMPLIANT_USDC_ADDRESS" "owner()(address)" --rpc-url "$RPC")"
|
||||
usdt_ratio="$(cast call "$CW_VAULT" "getBackingRatio(address)(uint256,uint256,uint256)" "$COMPLIANT_USDT_ADDRESS" --rpc-url "$RPC" | tr '\n' ' ' | xargs)"
|
||||
usdc_ratio="$(cast call "$CW_VAULT" "getBackingRatio(address)(uint256,uint256,uint256)" "$COMPLIANT_USDC_ADDRESS" --rpc-url "$RPC" | tr '\n' ' ' | xargs)"
|
||||
log "cUSDT owner: $usdt_owner"
|
||||
log "cUSDC owner: $usdc_owner"
|
||||
log "cUSDT backing tuple: $usdt_ratio"
|
||||
log "cUSDC backing tuple: $usdc_ratio"
|
||||
}
|
||||
|
||||
log "Deployer: $DEPLOYER_ADDRESS"
|
||||
log "Dry run: $DRY_RUN"
|
||||
|
||||
if [[ -z "$TARGET_USDT_BACKING" ]]; then
|
||||
TARGET_USDT_BACKING="$(normalize_uint "$(cast call "$COMPLIANT_USDT_ADDRESS" "totalSupply()(uint256)" --rpc-url "$RPC")")"
|
||||
fi
|
||||
if [[ -z "$TARGET_USDC_BACKING" ]]; then
|
||||
TARGET_USDC_BACKING="$(normalize_uint "$(cast call "$COMPLIANT_USDC_ADDRESS" "totalSupply()(uint256)" --rpc-url "$RPC")")"
|
||||
fi
|
||||
|
||||
ensure_vault
|
||||
|
||||
if ! $DRY_RUN; then
|
||||
set_env_value "$ENV_FILE" "CW_STABLECOIN_RESERVE_VAULT" "$CW_VAULT"
|
||||
set_env_value "$ENV_FILE" "CW_REQUIRE_VAULT_BACKING" "1"
|
||||
set_env_value "$ENV_FILE" "CW_REQUIRE_TOKEN_OWNER_MATCH_VAULT" "1"
|
||||
set_env_value "$ENV_FILE" "CW_REQUIRE_RESERVE_SYSTEM_BALANCE" "0"
|
||||
fi
|
||||
|
||||
ensure_seeded_backing "$OFFICIAL_USDT_ADDRESS" "usdtReserveBalance()(uint256)" "seedUSDTReserve(uint256)" "$TARGET_USDT_BACKING" "USDT"
|
||||
ensure_seeded_backing "$OFFICIAL_USDC_ADDRESS" "usdcReserveBalance()(uint256)" "seedUSDCReserve(uint256)" "$TARGET_USDC_BACKING" "USDC"
|
||||
|
||||
ensure_compliant_owner "$COMPLIANT_USDT_ADDRESS" "cUSDT"
|
||||
ensure_compliant_owner "$COMPLIANT_USDC_ADDRESS" "cUSDC"
|
||||
|
||||
if ! $DRY_RUN; then
|
||||
verify_vault_state
|
||||
fi
|
||||
|
||||
if $WITH_CUTOVER; then
|
||||
if $DRY_RUN; then
|
||||
echo "(cd \"$SMOM_ROOT\" && ./scripts/deployment/complete-nonprefunded-avax-cutover.sh --dry-run)"
|
||||
else
|
||||
(cd "$SMOM_ROOT" && ./scripts/deployment/complete-nonprefunded-avax-cutover.sh)
|
||||
fi
|
||||
fi
|
||||
@@ -2,10 +2,89 @@
|
||||
# Deploy official DODO DVM (DVMFactory + deps) to Chain 138 via DODOEX/contractV2 Truffle,
|
||||
# then deploy DVMFactoryAdapter (createDVM -> createDODOVendingMachine) via Forge and update .env.
|
||||
# Requires: smom-dbis-138/.env with PRIVATE_KEY, RPC_URL_138
|
||||
#
|
||||
# Notes:
|
||||
# - The vendored DODO V2 tree contains both Solidity 0.6.9 and 0.8.x sources.
|
||||
# Truffle uses a single configured compiler, so this script temporarily hides the
|
||||
# unrelated 0.8.x contracts during compile/migrate, then restores them on exit.
|
||||
# - Use --compile-only to verify the official DVM stack compiles cleanly before broadcast.
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
DODO_DIR="$PROJECT_ROOT/lib/dodo-contractV2"
|
||||
COMPILE_ONLY=false
|
||||
TRUFFLE_RESET=false
|
||||
HIDDEN_ROOT="$DODO_DIR/.chain138-dvm-solc-excludes"
|
||||
declare -a HIDDEN_SOL_FILES=()
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: deploy-official-dvm-chain138.sh [--compile-only] [--reset]
|
||||
|
||||
Options:
|
||||
--compile-only Compile the official DODO V2 DVM stack in DVM-only mode, then exit.
|
||||
--reset Pass --reset to truffle migrate.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--compile-only)
|
||||
COMPILE_ONLY=true
|
||||
shift
|
||||
;;
|
||||
--reset)
|
||||
TRUFFLE_RESET=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
prepare_dvm_only_sources() {
|
||||
local rel_path
|
||||
rm -rf "$HIDDEN_ROOT"
|
||||
mkdir -p "$HIDDEN_ROOT"
|
||||
|
||||
while IFS= read -r rel_path; do
|
||||
[[ -z "$rel_path" ]] && continue
|
||||
mkdir -p "$HIDDEN_ROOT/$(dirname "$rel_path")"
|
||||
mv "$DODO_DIR/$rel_path" "$HIDDEN_ROOT/$rel_path"
|
||||
HIDDEN_SOL_FILES+=("$rel_path")
|
||||
done < <(
|
||||
cd "$DODO_DIR" &&
|
||||
find contracts -type f -name '*.sol' -print0 |
|
||||
xargs -0 grep -El 'pragma solidity (\^?0\.8|>=0\.8)' |
|
||||
sort
|
||||
)
|
||||
|
||||
if [[ ${#HIDDEN_SOL_FILES[@]} -gt 0 ]]; then
|
||||
echo "Temporarily hiding ${#HIDDEN_SOL_FILES[@]} Solidity 0.8.x sources for Chain 138 DVM-only compile..."
|
||||
fi
|
||||
}
|
||||
|
||||
restore_hidden_sources() {
|
||||
local rel_path
|
||||
if [[ -d "$HIDDEN_ROOT" ]]; then
|
||||
while IFS= read -r rel_path; do
|
||||
[[ -z "$rel_path" ]] && continue
|
||||
mkdir -p "$DODO_DIR/$(dirname "$rel_path")"
|
||||
mv "$HIDDEN_ROOT/$rel_path" "$DODO_DIR/$rel_path"
|
||||
done < <(cd "$HIDDEN_ROOT" && find contracts -type f -name '*.sol' | sort)
|
||||
rm -rf "$HIDDEN_ROOT"
|
||||
fi
|
||||
}
|
||||
|
||||
trap restore_hidden_sources EXIT
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
# Load .env via dotenv (RPC CR/LF trim). Fallback: raw source.
|
||||
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
|
||||
@@ -52,12 +131,27 @@ export privKey="${PRIVATE_KEY#0x}"
|
||||
export RPC_URL_138
|
||||
export GAS_PRICE_138="${GAS_PRICE_138:-1000000000}"
|
||||
|
||||
prepare_dvm_only_sources
|
||||
|
||||
echo "=== Compiling official DODO V2 DVM-only subset for Chain 138 ==="
|
||||
(cd "$DODO_DIR" && rm -rf build/contracts && npx truffle compile)
|
||||
|
||||
if [[ "$COMPILE_ONLY" == "true" ]]; then
|
||||
echo "DODO V2 DVM-only compile completed successfully."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TRUFFLE_RESET_ARGS=()
|
||||
if [[ "$TRUFFLE_RESET" == "true" ]]; then
|
||||
TRUFFLE_RESET_ARGS+=(--reset)
|
||||
fi
|
||||
|
||||
# Deploy Migrations (required for Truffle), then DVM stack only
|
||||
echo "=== Running Truffle migration 1 (Migrations) on Chain 138 ==="
|
||||
(cd "$DODO_DIR" && npx truffle migrate -f 1 --to 1 --network chain138) || true
|
||||
(cd "$DODO_DIR" && npx truffle migrate -f 1 --to 1 --network chain138 "${TRUFFLE_RESET_ARGS[@]}") || true
|
||||
|
||||
echo "=== Running Truffle migration 9 (DVM stack) on Chain 138 ==="
|
||||
(cd "$DODO_DIR" && npx truffle migrate -f 9 --to 9 --network chain138)
|
||||
(cd "$DODO_DIR" && npx truffle migrate -f 9 --to 9 --network chain138 "${TRUFFLE_RESET_ARGS[@]}")
|
||||
|
||||
# Parse DVMFactory address from Truffle build (network id 138 as string or number)
|
||||
DVM_FACTORY_ADDRESS=$(cd "$DODO_DIR" && node -e "
|
||||
@@ -84,7 +178,18 @@ forge script script/dex/DeployDVMFactoryAdapter.s.sol:DeployDVMFactoryAdapter \
|
||||
--rpc-url "$RPC_URL_138" --broadcast --private-key "$PRIVATE_KEY" --legacy
|
||||
|
||||
# Extract adapter address from broadcast (or script output)
|
||||
ADAPTER_ADDRESS=$(grep -o '"contractAddress":"0x[^"]*"' "$PROJECT_ROOT/broadcast/DeployDVMFactoryAdapter.s.sol/138/"*run-latest.json 2>/dev/null | tail -1 | sed 's/.*"0x/0x/;s/".*//') || true
|
||||
ADAPTER_BROADCAST="$PROJECT_ROOT/broadcast/DeployDVMFactoryAdapter.s.sol/138/run-latest.json"
|
||||
ADAPTER_ADDRESS=$(
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const path = process.argv[1];
|
||||
if (!fs.existsSync(path)) process.exit(1);
|
||||
const j = JSON.parse(fs.readFileSync(path, 'utf8'));
|
||||
const tx = (j.transactions || []).find((entry) => entry.contractName === 'DVMFactoryAdapter' && entry.contractAddress);
|
||||
if (!tx) process.exit(1);
|
||||
console.log(tx.contractAddress);
|
||||
" "$ADAPTER_BROADCAST" 2>/dev/null
|
||||
) || true
|
||||
if [[ -z "$ADAPTER_ADDRESS" ]]; then
|
||||
echo "Set DODO_VENDING_MACHINE_ADDRESS to the DVMFactoryAdapter address printed above."
|
||||
exit 0
|
||||
|
||||
@@ -11,6 +11,17 @@ source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
|
||||
source "$SCRIPT_DIR/../lib/deployment/prompts.sh"
|
||||
load_deployment_env
|
||||
|
||||
first_set_env() {
|
||||
local key
|
||||
for key in "$@"; do
|
||||
if [[ -n "${!key:-}" ]]; then
|
||||
printf '%s' "${!key}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
parse_chain_filter "$@"
|
||||
if [[ ${#CHAIN_FILTER[@]} -eq 0 && -n "${DEPLOY_PMM_L2S_FILTER:-}" ]]; then
|
||||
CHAIN_FILTER=()
|
||||
@@ -42,22 +53,35 @@ for entry in "${CHAINS[@]}"; do
|
||||
dvm_var="${name}_DODO_VENDING_MACHINE_ADDRESS"
|
||||
usdt_var="${name}_OFFICIAL_USDT_ADDRESS"
|
||||
usdc_var="${name}_OFFICIAL_USDC_ADDRESS"
|
||||
usdt_var_alt="OFFICIAL_USDT_${name}"
|
||||
usdc_var_alt="OFFICIAL_USDC_${name}"
|
||||
cusdt_var="${name}_COMPLIANT_USDT_ADDRESS"
|
||||
cusdc_var="${name}_COMPLIANT_USDC_ADDRESS"
|
||||
cusdt_var_alt="COMPLIANT_USDT_${name}"
|
||||
cusdc_var_alt="COMPLIANT_USDC_${name}"
|
||||
# Per-chain cUSDT/cUSDC (optional): CUSDT_ADDRESS_<chainId> / CUSDC_ADDRESS_<chainId> or POLYGON_COMPLIANT_USDT_ADDRESS etc.
|
||||
cusdt_chain="CUSDT_ADDRESS_${chain_id}"
|
||||
cusdc_chain="CUSDC_ADDRESS_${chain_id}"
|
||||
dvm="${!dvm_var:-$DODO_VENDING_MACHINE_ADDRESS}"
|
||||
usdt="${!usdt_var:-$OFFICIAL_USDT_ADDRESS}"
|
||||
usdc="${!usdc_var:-$OFFICIAL_USDC_ADDRESS}"
|
||||
compliant_usdt="${!cusdt_var:-${!cusdt_chain:-$usdt}}"
|
||||
compliant_usdc="${!cusdc_var:-${!cusdc_chain:-$usdc}}"
|
||||
dvm="$(first_set_env "$dvm_var" "DODO_VENDING_MACHINE_ADDRESS" || true)"
|
||||
usdt="$(first_set_env "$usdt_var" "$usdt_var_alt" "OFFICIAL_USDT_ADDRESS" || true)"
|
||||
usdc="$(first_set_env "$usdc_var" "$usdc_var_alt" "OFFICIAL_USDC_ADDRESS" || true)"
|
||||
compliant_usdt="$(first_set_env "$cusdt_var" "$cusdt_var_alt" "$cusdt_chain" || true)"
|
||||
compliant_usdc="$(first_set_env "$cusdc_var" "$cusdc_var_alt" "$cusdc_chain" || true)"
|
||||
compliant_usdt="${compliant_usdt:-$usdt}"
|
||||
compliant_usdc="${compliant_usdc:-$usdc}"
|
||||
|
||||
if [[ -z "$dvm" ]] || [[ -z "$usdt" ]] || [[ -z "$usdc" ]]; then
|
||||
echo "Skip $name: set ${dvm_var} (or DODO_VENDING_MACHINE_ADDRESS), ${usdt_var}, ${usdc_var} (or OFFICIAL_USDT/USDC_ADDRESS)"
|
||||
echo "Skip $name: set ${dvm_var} (or DODO_VENDING_MACHINE_ADDRESS), ${usdt_var}/${usdt_var_alt}, ${usdc_var}/${usdc_var_alt} (or OFFICIAL_USDT/USDC_ADDRESS)"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -z "${!usdt_var:-}" && -z "${!usdt_var_alt:-}" ]]; then
|
||||
echo "WARN $name: using global OFFICIAL_USDT_ADDRESS fallback; set ${usdt_var} or ${usdt_var_alt} for chain-specific safety"
|
||||
fi
|
||||
if [[ -z "${!usdc_var:-}" && -z "${!usdc_var_alt:-}" ]]; then
|
||||
echo "WARN $name: using global OFFICIAL_USDC_ADDRESS fallback; set ${usdc_var} or ${usdc_var_alt} for chain-specific safety"
|
||||
fi
|
||||
|
||||
echo "=== Deploying DODOPMMIntegration on $name (chain $chain_id) ==="
|
||||
DODO_VENDING_MACHINE_ADDRESS="$dvm" \
|
||||
OFFICIAL_USDT_ADDRESS="$usdt" \
|
||||
|
||||
@@ -77,7 +77,9 @@ done
|
||||
|
||||
# Chain ID : Name : RPC env var (primary)
|
||||
# DeployAll.s.sol supports: 1, 25, 56, 137, 100, 43114, 8453, 42161, 10. 651940 = env validation only.
|
||||
ALL_CHAINS="1:Mainnet:ETH_MAINNET_RPC_URL 25:Cronos:CRONOS_RPC_URL 56:BSC:BSC_RPC_URL 137:Polygon:POLYGON_RPC_URL 100:Gnosis:GNOSIS_RPC_URL 43114:Avalanche:AVALANCHE_RPC_URL 8453:Base:BASE_RPC_URL 42161:Arbitrum:ARBITRUM_RPC_URL 10:Optimism:OPTIMISM_RPC_URL 651940:ALL:CHAIN_651940_RPC"
|
||||
# Celo / Wemix are included here for c* / cW* deployment even though their WETH/bridge deploy
|
||||
# flow is handled separately via deploy-bridges-config-ready-chains.sh.
|
||||
ALL_CHAINS="1:Mainnet:ETH_MAINNET_RPC_URL 25:Cronos:CRONOS_RPC_URL 56:BSC:BSC_RPC_URL 137:Polygon:POLYGON_RPC_URL 100:Gnosis:GNOSIS_RPC_URL 43114:Avalanche:AVALANCHE_RPC_URL 8453:Base:BASE_RPC_URL 42161:Arbitrum:ARBITRUM_RPC_URL 10:Optimism:OPTIMISM_RPC_URL 42220:Celo:CELO_RPC 1111:Wemix:WEMIX_RPC 651940:ALL:CHAIN_651940_RPC"
|
||||
|
||||
# Fallback RPC env names (some scripts use different names)
|
||||
fallback_rpc() {
|
||||
@@ -95,6 +97,8 @@ fallback_rpc() {
|
||||
8453) rpc="${BASE_MAINNET_RPC:-}";;
|
||||
42161) rpc="${ARBITRUM_MAINNET_RPC:-}";;
|
||||
10) rpc="${OPTIMISM_MAINNET_RPC:-}";;
|
||||
42220) rpc="${CELO_RPC:-${CELO_MAINNET_RPC:-}}";;
|
||||
1111) rpc="${WEMIX_RPC:-${WEMIX_MAINNET_RPC:-}}";;
|
||||
651940) rpc="${CHAIN_651940_RPC:-${ALL_MAINNET_RPC:-}}";;
|
||||
esac
|
||||
fi
|
||||
@@ -130,6 +134,10 @@ run_deploy_all() {
|
||||
echo " Skip $name (chain 651940): DeployAll not supported; env validation only."
|
||||
return 0
|
||||
fi
|
||||
if [[ "$chain_id" == "42220" || "$chain_id" == "1111" ]]; then
|
||||
echo " Skip $name (chain $chain_id): WETH/bridge deploy handled by deploy-bridges-config-ready-chains.sh; c*/cW* phases may still run."
|
||||
return 0
|
||||
fi
|
||||
local rpc
|
||||
rpc=$(fallback_rpc "$chain_id" "$rpc_var")
|
||||
if [[ -z "$rpc" ]]; then
|
||||
@@ -230,9 +238,13 @@ run_deploy_cw() {
|
||||
rpc=$(fallback_rpc "$chain_id" "$rpc_var")
|
||||
if [[ -z "$rpc" ]]; then return 0; fi
|
||||
local bridge_var="CW_BRIDGE_${name^^}"
|
||||
local ccip_bridge_var="CCIPWETH9_BRIDGE_${name^^}"
|
||||
local bridge="${!bridge_var:-${CW_BRIDGE_ADDRESS:-}}"
|
||||
if [[ -z "$bridge" || "$bridge" == "0x0000000000000000000000000000000000000000" ]]; then
|
||||
bridge="${!ccip_bridge_var:-}"
|
||||
fi
|
||||
if [[ -z "$bridge" || "$bridge" == "0x"*"0000000000000000000000000000000000000000" ]]; then
|
||||
echo " Skip cW* on $name (chain $chain_id): set CW_BRIDGE_ADDRESS or CW_BRIDGE_${name^^} in .env."
|
||||
echo " Skip cW* on $name (chain $chain_id): set CW_BRIDGE_ADDRESS / CW_BRIDGE_${name^^} or deploy/configure CCIPWETH9_BRIDGE_${name^^}."
|
||||
return 0
|
||||
fi
|
||||
local cw_var="CWUSDT_${name^^}"
|
||||
|
||||
@@ -3,7 +3,10 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
PROJECT_ENV_LOADER="$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
ENV_FILE="$SMOM_ROOT/.env"
|
||||
ENV_SOURCE="<none>"
|
||||
RUN_FORGE_DRY_RUN=0
|
||||
RUN_TIMEOUT_SECONDS="${RUN_TIMEOUT_SECONDS:-90}"
|
||||
VERBOSITY="${VERBOSITY:--vv}"
|
||||
@@ -35,11 +38,17 @@ CHAIN138_USDT_DEFAULT="0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1"
|
||||
CHAIN138_USDC_DEFAULT="0x71D6687F38b93CCad569Fa6352c876eea967201b"
|
||||
DAI_PLACEHOLDER_DEFAULT="0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
if [[ -f "$PROJECT_ENV_LOADER" ]]; then
|
||||
export PROJECT_ROOT
|
||||
# shellcheck disable=SC1090
|
||||
source "$PROJECT_ENV_LOADER"
|
||||
ENV_SOURCE="$PROJECT_ENV_LOADER"
|
||||
elif [[ -f "$ENV_FILE" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
ENV_SOURCE="$ENV_FILE"
|
||||
fi
|
||||
|
||||
RPC_URL_138="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
@@ -97,22 +106,25 @@ run_forge_dry_run() {
|
||||
)
|
||||
local exit_code=0
|
||||
|
||||
echo "Running targeted build warm-up"
|
||||
forge build contracts/bridge/trustless/EnhancedSwapRouter.sol script/bridge/trustless/DeployEnhancedSwapRouter.s.sol
|
||||
echo ""
|
||||
echo "Running sourced non-broadcast forge script"
|
||||
echo "Timeout: ${RUN_TIMEOUT_SECONDS}s"
|
||||
echo "Verbosity: $VERBOSITY"
|
||||
echo ""
|
||||
|
||||
set +e
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout "${RUN_TIMEOUT_SECONDS}s" "${forge_cmd[@]}"
|
||||
exit_code=$?
|
||||
else
|
||||
"${forge_cmd[@]}"
|
||||
exit_code=$?
|
||||
fi
|
||||
(
|
||||
cd "$SMOM_ROOT" || exit 1
|
||||
echo "Running targeted build warm-up"
|
||||
forge build contracts/bridge/trustless/EnhancedSwapRouter.sol script/bridge/trustless/DeployEnhancedSwapRouter.s.sol
|
||||
echo ""
|
||||
echo "Running sourced non-broadcast forge script"
|
||||
echo "Working directory: $SMOM_ROOT"
|
||||
echo "Timeout: ${RUN_TIMEOUT_SECONDS}s"
|
||||
echo "Verbosity: $VERBOSITY"
|
||||
echo ""
|
||||
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout "${RUN_TIMEOUT_SECONDS}s" "${forge_cmd[@]}"
|
||||
else
|
||||
"${forge_cmd[@]}"
|
||||
fi
|
||||
)
|
||||
exit_code=$?
|
||||
set -e
|
||||
|
||||
if (( exit_code == 124 )); then
|
||||
@@ -136,7 +148,10 @@ run_forge_dry_run() {
|
||||
|
||||
echo "=== EnhancedSwapRouter Chain 138 Dry Run ==="
|
||||
echo "Project root: $SMOM_ROOT"
|
||||
echo "Repository root: $PROJECT_ROOT"
|
||||
echo "Project env loader: $PROJECT_ENV_LOADER"
|
||||
echo "Env file: $ENV_FILE"
|
||||
echo "Env source: $ENV_SOURCE"
|
||||
echo ""
|
||||
|
||||
if (( ${#missing[@]} > 0 )); then
|
||||
@@ -145,7 +160,7 @@ if (( ${#missing[@]} > 0 )); then
|
||||
echo " - $item"
|
||||
done
|
||||
echo ""
|
||||
echo "Set them in $ENV_FILE or export them in your shell, then rerun."
|
||||
echo "Set them in $ENV_FILE, the repo root env loaded by $PROJECT_ENV_LOADER, or export them in your shell, then rerun."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -215,7 +230,11 @@ echo " - cEURT <-> cXAUC"
|
||||
echo ""
|
||||
|
||||
echo "Exact dry-run command"
|
||||
echo "cd \"$SMOM_ROOT\" && source .env && forge script script/bridge/trustless/DeployEnhancedSwapRouter.s.sol:DeployEnhancedSwapRouter --rpc-url \"$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\""
|
||||
if [[ -f "$PROJECT_ENV_LOADER" ]]; then
|
||||
echo "cd \"$PROJECT_ROOT\" && source scripts/lib/load-project-env.sh && cd smom-dbis-138 && forge script script/bridge/trustless/DeployEnhancedSwapRouter.s.sol:DeployEnhancedSwapRouter --rpc-url \"$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\""
|
||||
else
|
||||
echo "cd \"$SMOM_ROOT\" && source .env && forge script script/bridge/trustless/DeployEnhancedSwapRouter.s.sol:DeployEnhancedSwapRouter --rpc-url \"$RPC_URL_138\" --private-key \"\$PRIVATE_KEY\""
|
||||
fi
|
||||
echo ""
|
||||
echo "Example minimal exports before dry-run"
|
||||
cat <<EOF
|
||||
|
||||
196
scripts/deployment/dry-run-enhanced-swap-router-v2-chain138.sh
Normal file
196
scripts/deployment/dry-run-enhanced-swap-router-v2-chain138.sh
Normal file
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
|
||||
PROJECT_ENV_LOADER="$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
||||
ENV_FILE="$SMOM_ROOT/.env"
|
||||
ENV_SOURCE="<none>"
|
||||
RUN_FORGE_DRY_RUN=0
|
||||
RUN_TIMEOUT_SECONDS="${RUN_TIMEOUT_SECONDS:-120}"
|
||||
VERBOSITY="${VERBOSITY:--vv}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--run)
|
||||
RUN_FORGE_DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--timeout-seconds)
|
||||
RUN_TIMEOUT_SECONDS="${2:?missing value for --timeout-seconds}"
|
||||
shift 2
|
||||
;;
|
||||
--verbosity)
|
||||
VERBOSITY="${2:?missing value for --verbosity}"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
echo "Usage: $0 [--run] [--timeout-seconds <seconds>] [--verbosity <-v|-vv|-vvv|...>]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -f "$PROJECT_ENV_LOADER" ]]; then
|
||||
export PROJECT_ROOT
|
||||
# shellcheck disable=SC1090
|
||||
source "$PROJECT_ENV_LOADER"
|
||||
ENV_SOURCE="$PROJECT_ENV_LOADER"
|
||||
elif [[ -f "$ENV_FILE" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
ENV_SOURCE="$ENV_FILE"
|
||||
fi
|
||||
|
||||
RPC_URL_138="${RPC_URL_138:-http://192.168.11.211:8545}"
|
||||
WETH="${WETH:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}"
|
||||
OFFICIAL_USDT_ADDRESS="${OFFICIAL_USDT_ADDRESS:-0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1}"
|
||||
OFFICIAL_USDC_ADDRESS="${OFFICIAL_USDC_ADDRESS:-0x71D6687F38b93CCad569Fa6352c876eea967201b}"
|
||||
DODO_PMM_PROVIDER_ADDRESS="${DODO_PMM_PROVIDER_ADDRESS:-${DODO_PMM_PROVIDER:-}}"
|
||||
|
||||
show_var() {
|
||||
local name="$1" value="$2" note="${3:-}"
|
||||
printf ' %-28s %s' "$name" "$value"
|
||||
[[ -n "$note" ]] && printf ' (%s)' "$note"
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
show_secret_var() {
|
||||
local name="$1" value="${2:-}" note="${3:-}"
|
||||
local display="<unset>"
|
||||
if [[ -n "$value" ]]; then
|
||||
display="<set>"
|
||||
fi
|
||||
printf ' %-28s %s' "$name" "$display"
|
||||
[[ -n "$note" ]] && printf ' (%s)' "$note"
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
probe_support() {
|
||||
local token_in="$1" token_out="$2" provider="$3" label="$4"
|
||||
local result
|
||||
result="$(cast call "$provider" "supportsTokenPair(address,address)(bool)" "$token_in" "$token_out" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
if [[ "$result" == "true" ]]; then
|
||||
echo " OK: $label"
|
||||
return 0
|
||||
fi
|
||||
echo " MISSING: $label"
|
||||
return 1
|
||||
}
|
||||
|
||||
probe_quote() {
|
||||
local token_in="$1" token_out="$2" provider="$3" amount="$4" label="$5"
|
||||
local result amount_out
|
||||
result="$(cast call "$provider" "getQuote(address,address,uint256)(uint256,uint256)" "$token_in" "$token_out" "$amount" --rpc-url "$RPC_URL_138" 2>/dev/null || true)"
|
||||
amount_out="$(awk 'NR==1 {print $1}' <<<"$result")"
|
||||
if [[ -n "$amount_out" && "$amount_out" != "0" ]]; then
|
||||
echo " OK: $label => $amount_out"
|
||||
return 0
|
||||
fi
|
||||
echo " ZERO-QUOTE: $label"
|
||||
return 1
|
||||
}
|
||||
|
||||
run_forge_dry_run() {
|
||||
local forge_cmd=(
|
||||
forge script script/bridge/trustless/DeployEnhancedSwapRouterV2.s.sol:DeployEnhancedSwapRouterV2
|
||||
--skip test
|
||||
--non-interactive
|
||||
--rpc-url "$RPC_URL_138"
|
||||
--private-key "$PRIVATE_KEY"
|
||||
"$VERBOSITY"
|
||||
)
|
||||
(
|
||||
cd "$SMOM_ROOT"
|
||||
forge build contracts/bridge/trustless/EnhancedSwapRouterV2.sol script/bridge/trustless/DeployEnhancedSwapRouterV2.s.sol
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout "${RUN_TIMEOUT_SECONDS}s" "${forge_cmd[@]}"
|
||||
else
|
||||
"${forge_cmd[@]}"
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
echo "=== EnhancedSwapRouterV2 Chain 138 Dry Run ==="
|
||||
echo "Project root: $SMOM_ROOT"
|
||||
echo "Repository root: $PROJECT_ROOT"
|
||||
echo "Env source: $ENV_SOURCE"
|
||||
echo ""
|
||||
|
||||
show_var "RPC_URL_138" "$RPC_URL_138" "Core RPC only"
|
||||
show_secret_var "PRIVATE_KEY" "${PRIVATE_KEY:-}" "not printed when present"
|
||||
show_var "DODO_PMM_PROVIDER_ADDRESS" "${DODO_PMM_PROVIDER_ADDRESS:-<unset>}" "required"
|
||||
show_var "CHAIN138_POOL_WETH_USDT" "${CHAIN138_POOL_WETH_USDT:-<unset>}" "required for swapToStablecoin readiness"
|
||||
show_var "CHAIN138_POOL_WETH_USDC" "${CHAIN138_POOL_WETH_USDC:-<unset>}" "required for swapToStablecoin readiness"
|
||||
show_var "CHAIN138_D3_PROXY_ADDRESS" "${CHAIN138_D3_PROXY_ADDRESS:-0xc9a11abB7C63d88546Be24D58a6d95e3762cB843}" "optional DODO v3 execution"
|
||||
show_var "CHAIN138_D3_MM_ADDRESS" "${CHAIN138_D3_MM_ADDRESS:-0x6550A3a59070061a262a893A1D6F3F490afFDBDA}" "optional DODO v3 execution"
|
||||
show_var "UNISWAP_V3_ROUTER" "${UNISWAP_V3_ROUTER:-<unset>}" "optional"
|
||||
show_var "UNISWAP_QUOTER_ADDRESS" "${UNISWAP_QUOTER_ADDRESS:-<unset>}" "optional"
|
||||
show_var "BALANCER_VAULT" "${BALANCER_VAULT:-<unset>}" "optional"
|
||||
show_var "CURVE_3POOL" "${CURVE_3POOL:-<unset>}" "optional"
|
||||
echo ""
|
||||
|
||||
if [[ -z "${PRIVATE_KEY:-}" ]]; then
|
||||
echo "PRIVATE_KEY is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$DODO_PMM_PROVIDER_ADDRESS" ]]; then
|
||||
echo "DODO_PMM_PROVIDER_ADDRESS is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
readiness_fail=0
|
||||
echo "Provider route probe"
|
||||
if [[ -n "${CHAIN138_POOL_WETH_USDT:-}" ]]; then
|
||||
if ! probe_support "$WETH" "$OFFICIAL_USDT_ADDRESS" "$DODO_PMM_PROVIDER_ADDRESS" "WETH -> USDT"; then
|
||||
readiness_fail=1
|
||||
fi
|
||||
else
|
||||
echo " SKIP: WETH -> USDT (CHAIN138_POOL_WETH_USDT unset)"
|
||||
fi
|
||||
if [[ -n "${CHAIN138_POOL_WETH_USDC:-}" ]]; then
|
||||
if ! probe_support "$WETH" "$OFFICIAL_USDC_ADDRESS" "$DODO_PMM_PROVIDER_ADDRESS" "WETH -> USDC"; then
|
||||
readiness_fail=1
|
||||
fi
|
||||
else
|
||||
echo " SKIP: WETH -> USDC (CHAIN138_POOL_WETH_USDC unset)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "Quote readiness probe"
|
||||
for amount in "1000000000000000000:1" "5000000000000000000:5" "25000000000000000000:25"; do
|
||||
raw_amount="${amount%%:*}"
|
||||
human_amount="${amount##*:}"
|
||||
if [[ -n "${CHAIN138_POOL_WETH_USDT:-}" ]]; then
|
||||
if ! probe_quote "$WETH" "$OFFICIAL_USDT_ADDRESS" "$DODO_PMM_PROVIDER_ADDRESS" "$raw_amount" "WETH -> USDT @ ${human_amount} WETH"; then
|
||||
readiness_fail=1
|
||||
fi
|
||||
fi
|
||||
if [[ -n "${CHAIN138_POOL_WETH_USDC:-}" ]]; then
|
||||
if ! probe_quote "$WETH" "$OFFICIAL_USDC_ADDRESS" "$DODO_PMM_PROVIDER_ADDRESS" "$raw_amount" "WETH -> USDC @ ${human_amount} WETH"; then
|
||||
readiness_fail=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
if [[ -z "${CHAIN138_POOL_WETH_USDT:-}" || -z "${CHAIN138_POOL_WETH_USDC:-}" ]]; then
|
||||
echo "Readiness note: canonical WETH -> stable DODO PMM routes are still incomplete, so swapToStablecoin readiness remains partial."
|
||||
echo "The router-v2 deployment can still proceed for explicit executeRoute calldata, including DODO v3 pilot execution."
|
||||
elif (( readiness_fail == 1 )); then
|
||||
echo "Readiness failed: Chain 138 still lacks at least one required WETH -> stable DODO route or non-zero quote."
|
||||
echo "Set CHAIN138_POOL_WETH_USDT / CHAIN138_POOL_WETH_USDC after creating and registering the canonical pools, then rerun."
|
||||
exit 1
|
||||
else
|
||||
echo "Readiness passed: required WETH -> stable DODO routes are present with non-zero quotes for 1/5/25 WETH."
|
||||
fi
|
||||
|
||||
if (( RUN_FORGE_DRY_RUN == 1 )); then
|
||||
echo ""
|
||||
run_forge_dry_run
|
||||
fi
|
||||
@@ -73,10 +73,10 @@ cat > "$CONFIG_DIR/SUPPORTED_CHAINS.md" << 'EOF'
|
||||
|
||||
| Framework | Type | Status | Adapter | Nodes |
|
||||
|-----------|------|--------|---------|-------|
|
||||
| Firefly | Orchestration | ✅ Deployed | FireflyAdapter | VMID 6202, 6203 |
|
||||
| Cacti | Interoperability | ✅ Deployed | CactiAdapter | VMID 5201 |
|
||||
| Fabric | Permissioned | 🔨 Plan | FabricAdapter | TBD |
|
||||
| Indy | Identity | 🔨 Plan | IndyVerifier | TBD |
|
||||
| FireFly | Orchestration | ⚠️ Adapter/service client only | FireflyAdapter | `6200` live primary; `6201` retired / standby; `6202` / `6203` not deployed |
|
||||
| Cacti | Interoperability | ⚠️ Active surfaces, adapter still pending | CactiAdapter | `5200` live primary; `5201` / `5202` live public Cacti surfaces with local `:4000` APIs |
|
||||
| Fabric | Permissioned | ⚠️ Primary sample network only | FabricAdapter | `6000` live sample network; `6001` / `6002` placeholders |
|
||||
| Indy | Identity | ⚠️ Primary validator pool only | IndyVerifier | `6400` live validator pool; `6401` / `6402` placeholders |
|
||||
|
||||
## Legend
|
||||
- ✅ Live: Fully deployed and operational
|
||||
|
||||
572
scripts/deployment/print-chain138-public-chain-unload-routes.sh
Executable file
572
scripts/deployment/print-chain138-public-chain-unload-routes.sh
Executable file
@@ -0,0 +1,572 @@
|
||||
#!/usr/bin/env bash
|
||||
# Print the live unload routes from Chain 138 to reachable public chains.
|
||||
#
|
||||
# This helper does not send transactions. It reviews the current WETH9 bridge
|
||||
# topology, classifies each public-chain lane, and prints exact cast command
|
||||
# packs for the current amount/recipient.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deployment/print-chain138-public-chain-unload-routes.sh
|
||||
#
|
||||
# Optional env:
|
||||
# TARGET_CHAIN=gnosis
|
||||
# UNLOAD_AMOUNT_WEI=10000000000000000
|
||||
# RECIPIENT=0x...
|
||||
# OPTIONAL_UNWRAP_WEI=2000000000000000
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
trim_word() {
|
||||
echo "$1" | awk '{print $1}'
|
||||
}
|
||||
|
||||
fmt_ether() {
|
||||
local raw="${1:-0}"
|
||||
raw="$(trim_word "$raw")"
|
||||
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"
|
||||
}
|
||||
|
||||
env_value_from_file() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
[[ -f "$file" ]] || {
|
||||
echo ""
|
||||
return 0
|
||||
}
|
||||
grep -E "^${key}=" "$file" | head -n1 | cut -d'=' -f2- | tr -d ' "\r\n' || true
|
||||
}
|
||||
|
||||
relay_inventory_note() {
|
||||
local inventory_raw="$1"
|
||||
if [[ "$(echo "$inventory_raw >= $UNLOAD_AMOUNT_WEI" | bc)" == "1" ]]; then
|
||||
echo "enough-for-request"
|
||||
elif [[ "$(echo "$inventory_raw > 0" | bc)" == "1" ]]; then
|
||||
echo "small-only"
|
||||
else
|
||||
echo "empty"
|
||||
fi
|
||||
}
|
||||
|
||||
practical_route_label() {
|
||||
local name="$1"
|
||||
local mainnet_quote_state="$2"
|
||||
case "$name" in
|
||||
mainnet|bsc|avalanche) echo "relay-backed-direct" ;;
|
||||
wemix) echo "blocked" ;;
|
||||
*)
|
||||
if [[ "$mainnet_quote_state" == "blocked" ]]; then
|
||||
echo "mainnet-hub-blocked"
|
||||
else
|
||||
echo "via-mainnet-hub"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
prereq_text() {
|
||||
local name="$1"
|
||||
local practical_route="$2"
|
||||
local inventory_state="$3"
|
||||
local mainnet_dest_enabled="$4"
|
||||
|
||||
case "$practical_route" in
|
||||
relay-backed-direct)
|
||||
case "$inventory_state" in
|
||||
enough-for-request) echo "inventory ready" ;;
|
||||
small-only) echo "inventory only covers tiny unloads" ;;
|
||||
empty)
|
||||
if [[ "$name" == "mainnet" ]]; then
|
||||
echo "seed mainnet relay bridge WETH"
|
||||
else
|
||||
echo "seed relay bridge WETH"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
via-mainnet-hub)
|
||||
if [[ "$mainnet_dest_enabled" == "enabled" ]]; then
|
||||
echo "bootstrap mainnet first"
|
||||
else
|
||||
echo "fix mainnet destination mapping first"
|
||||
fi
|
||||
;;
|
||||
mainnet-hub-blocked)
|
||||
echo "repair Mainnet WETH9 source bridge quote/send path first"
|
||||
;;
|
||||
*)
|
||||
echo "deploy bridge and seed gas"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
best_use_text() {
|
||||
local name="$1"
|
||||
local practical_route="$2"
|
||||
local inventory_state="$3"
|
||||
local mainnet_dest_enabled="$4"
|
||||
|
||||
case "$practical_route" in
|
||||
relay-backed-direct)
|
||||
case "$name" in
|
||||
mainnet)
|
||||
if [[ "$inventory_state" == "enough-for-request" ]]; then
|
||||
echo "direct 138 -> mainnet unload"
|
||||
else
|
||||
echo "bootstrap mainnet through the proven BSC recovery path, then keep relay inventory topped up"
|
||||
fi
|
||||
;;
|
||||
bsc)
|
||||
if [[ "$inventory_state" == "enough-for-request" ]]; then
|
||||
echo "direct 138 -> BSC unload"
|
||||
elif [[ "$inventory_state" == "small-only" ]]; then
|
||||
echo "tiny direct 138 -> BSC unloads; use mainnet hub for larger funding"
|
||||
else
|
||||
echo "seed BSC relay inventory or fund BSC from mainnet hub"
|
||||
fi
|
||||
;;
|
||||
avalanche)
|
||||
if [[ "$inventory_state" == "enough-for-request" ]]; then
|
||||
echo "direct 138 -> Avalanche unload"
|
||||
else
|
||||
echo "seed Avalanche relay inventory or fund Avalanche from mainnet hub"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
via-mainnet-hub)
|
||||
if [[ "$mainnet_dest_enabled" == "enabled" ]]; then
|
||||
echo "bootstrap mainnet, then send mainnet -> $name"
|
||||
else
|
||||
echo "repair mainnet -> $name mapping before using the hub"
|
||||
fi
|
||||
;;
|
||||
mainnet-hub-blocked)
|
||||
echo "blocked until the Mainnet WETH9 source bridge/router quote path is repaired"
|
||||
;;
|
||||
*)
|
||||
echo "deploy and wire WEMIX first"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
print_source_send_commands() {
|
||||
local name="$1"
|
||||
local selector="$2"
|
||||
local upper
|
||||
upper="$(echo "$name" | tr '[:lower:]-' '[:upper:]_')"
|
||||
|
||||
cat <<EOF
|
||||
export FEE_${upper}_WEI=\$(cast call "$CCIPWETH9_BRIDGE_CHAIN138" "calculateFee(uint64,uint256)(uint256)" \\
|
||||
"$selector" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$RPC_URL_138" | awk '{print \$1}')
|
||||
echo "FEE_${upper}_WEI=\$FEE_${upper}_WEI"
|
||||
|
||||
cast send "$WETH9_138" "approve(address,uint256)" \\
|
||||
"$CCIPWETH9_BRIDGE_CHAIN138" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$RPC_URL_138" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy --gas-price 2000000000
|
||||
|
||||
cast send "$LINK_138" "approve(address,uint256)" \\
|
||||
"$CCIPWETH9_BRIDGE_CHAIN138" "\$FEE_${upper}_WEI" \\
|
||||
--rpc-url "\$RPC_URL_138" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy --gas-price 2000000000
|
||||
|
||||
cast send "$CCIPWETH9_BRIDGE_CHAIN138" "sendCrossChain(uint64,address,uint256)" \\
|
||||
"$selector" "\$RECIPIENT" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$RPC_URL_138" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy --gas-limit 900000 --gas-price 2000000000
|
||||
EOF
|
||||
}
|
||||
|
||||
print_unwrap_commands() {
|
||||
local wrapped_token="$1"
|
||||
local rpc="$2"
|
||||
[[ -n "$wrapped_token" && -n "$rpc" ]] || return 0
|
||||
|
||||
cat <<EOF
|
||||
# Optional: unwrap a little of the received wrapped-native token into gas.
|
||||
cast send "$wrapped_token" "withdraw(uint256)" \\
|
||||
"$OPTIONAL_UNWRAP_WEI" \\
|
||||
--rpc-url "$rpc" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy
|
||||
EOF
|
||||
}
|
||||
|
||||
print_mainnet_send_commands() {
|
||||
local name="$1"
|
||||
local selector="$2"
|
||||
local wrapped_token="$3"
|
||||
local rpc="$4"
|
||||
local upper
|
||||
upper="$(echo "$name" | tr '[:lower:]-' '[:upper:]_')"
|
||||
|
||||
cat <<EOF
|
||||
# After mainnet is funded, fan out from the mainnet WETH9 bridge:
|
||||
export FEE_MAINNET_TO_${upper}_WEI=\$(cast call "$MAINNET_CCIP_WETH9_BRIDGE" "calculateFee(uint64,uint256)(uint256)" \\
|
||||
"$selector" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$ETHEREUM_MAINNET_RPC" | awk '{print \$1}')
|
||||
echo "FEE_MAINNET_TO_${upper}_WEI=\$FEE_MAINNET_TO_${upper}_WEI"
|
||||
|
||||
cast send "$MAINNET_WETH9" "approve(address,uint256)" \\
|
||||
"$MAINNET_CCIP_WETH9_BRIDGE" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$ETHEREUM_MAINNET_RPC" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy
|
||||
EOF
|
||||
|
||||
if [[ "${MAINNET_FEE_TOKEN,,}" != "${ZERO_ADDRESS,,}" ]]; then
|
||||
cat <<EOF
|
||||
|
||||
cast send "$MAINNET_FEE_TOKEN" "approve(address,uint256)" \\
|
||||
"$MAINNET_CCIP_WETH9_BRIDGE" "\$FEE_MAINNET_TO_${upper}_WEI" \\
|
||||
--rpc-url "\$ETHEREUM_MAINNET_RPC" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy
|
||||
|
||||
cast send "$MAINNET_CCIP_WETH9_BRIDGE" "sendCrossChain(uint64,address,uint256)" \\
|
||||
"$selector" "\$RECIPIENT" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$ETHEREUM_MAINNET_RPC" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy --gas-limit 900000
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
|
||||
cast send "$MAINNET_CCIP_WETH9_BRIDGE" "sendCrossChain(uint64,address,uint256)" \\
|
||||
"$selector" "\$RECIPIENT" "$UNLOAD_AMOUNT_WEI" \\
|
||||
--rpc-url "\$ETHEREUM_MAINNET_RPC" \\
|
||||
--private-key "\$PRIVATE_KEY" \\
|
||||
--legacy --gas-limit 900000 --value "\$FEE_MAINNET_TO_${upper}_WEI"
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ -n "$wrapped_token" && -n "$rpc" ]]; then
|
||||
echo ""
|
||||
print_unwrap_commands "$wrapped_token" "$rpc"
|
||||
fi
|
||||
}
|
||||
|
||||
need_var PRIVATE_KEY
|
||||
need_var RPC_URL_138
|
||||
need_var CCIPWETH9_BRIDGE_CHAIN138
|
||||
need_var ETH_MAINNET_SELECTOR
|
||||
need_var MAINNET_CCIP_WETH9_BRIDGE
|
||||
need_var ETHEREUM_MAINNET_RPC
|
||||
|
||||
DEPLOYER="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || true)"
|
||||
[[ -n "$DEPLOYER" ]] || {
|
||||
echo "Error: could not derive deployer from PRIVATE_KEY" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
WETH9_138="$(cast call "$CCIPWETH9_BRIDGE_CHAIN138" 'weth9()(address)' --rpc-url "$RPC_URL_138" | tail -n1)"
|
||||
LINK_138="$(cast call "$CCIPWETH9_BRIDGE_CHAIN138" 'feeToken()(address)' --rpc-url "$RPC_URL_138" | tail -n1)"
|
||||
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)"
|
||||
|
||||
ZERO_ADDRESS="0x0000000000000000000000000000000000000000"
|
||||
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,,}"
|
||||
TARGET_CHAIN="${TARGET_CHAIN:-all}"
|
||||
UNLOAD_AMOUNT_WEI="${UNLOAD_AMOUNT_WEI:-10000000000000000}"
|
||||
OPTIONAL_UNWRAP_WEI="${OPTIONAL_UNWRAP_WEI:-2000000000000000}"
|
||||
RECIPIENT="${RECIPIENT:-$DEPLOYER}"
|
||||
|
||||
RELAY_DIR="$PROJECT_ROOT/services/relay"
|
||||
BSC_RELAY_PROFILE="$RELAY_DIR/.env.bsc"
|
||||
AVAX_RELAY_PROFILE="$RELAY_DIR/.env.avax"
|
||||
|
||||
CHAINS=(
|
||||
"mainnet|${ETH_MAINNET_SELECTOR:-}|${ETHEREUM_MAINNET_RPC:-}|${MAINNET_CCIP_WETH9_BRIDGE:-}|$MAINNET_WETH9|${CCIP_RELAY_BRIDGE_MAINNET:-}|$RELAY_DIR/.env"
|
||||
"bsc|${BSC_SELECTOR:-}|${BSC_MAINNET_RPC:-}|${CCIPWETH9_BRIDGE_BSC:-}|${WETH9_BSC:-}|0x886C6A4ABC064dbf74E7caEc460b7eeC31F1b78C|$BSC_RELAY_PROFILE"
|
||||
"avalanche|${AVALANCHE_SELECTOR:-}|${AVALANCHE_MAINNET_RPC:-}|${CCIPWETH9_BRIDGE_AVALANCHE:-}|${WETH9_AVALANCHE:-}|0x3f8C409C6072a2B6a4Ff17071927bA70F80c725F|$AVAX_RELAY_PROFILE"
|
||||
"gnosis|${GNOSIS_SELECTOR:-}|${GNOSIS_RPC:-}|${CCIPWETH9_BRIDGE_GNOSIS:-}|${WETH9_GNOSIS:-}||"
|
||||
"cronos|${CRONOS_SELECTOR:-}|${CRONOS_RPC:-}|${CCIPWETH9_BRIDGE_CRONOS:-}|${WETH9_CRONOS:-}||"
|
||||
"celo|${CELO_SELECTOR:-1346049177634351622}|${CELO_RPC:-}|${CCIPWETH9_BRIDGE_CELO:-}|${WETH9_CELO:-}||"
|
||||
"polygon|${POLYGON_SELECTOR:-}|${POLYGON_MAINNET_RPC:-}|${CCIPWETH9_BRIDGE_POLYGON:-}|${WETH9_POLYGON:-}||"
|
||||
"arbitrum|${ARBITRUM_SELECTOR:-}|${ARBITRUM_MAINNET_RPC:-}|${CCIPWETH9_BRIDGE_ARBITRUM:-}|${WETH9_ARBITRUM:-}||"
|
||||
"optimism|${OPTIMISM_SELECTOR:-}|${OPTIMISM_MAINNET_RPC:-}|${CCIPWETH9_BRIDGE_OPTIMISM:-}|${WETH9_OPTIMISM:-}||"
|
||||
"base|${BASE_SELECTOR:-}|${BASE_MAINNET_RPC:-}|${CCIPWETH9_BRIDGE_BASE:-}|${WETH9_BASE:-}||"
|
||||
"wemix|${WEMIX_SELECTOR:-5142893604156789321}|${WEMIX_RPC:-}|${CCIPWETH9_BRIDGE_WEMIX:-}|${WETH9_WEMIX:-}||"
|
||||
)
|
||||
|
||||
echo "# Chain 138 Public-Chain Unload Routes"
|
||||
echo "# Generated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "# Deployer: $DEPLOYER"
|
||||
echo "# Recipient: $RECIPIENT"
|
||||
echo "# Unload amount: $UNLOAD_AMOUNT_WEI ($(fmt_ether "$UNLOAD_AMOUNT_WEI"))"
|
||||
echo "# Optional unwrap amount: $OPTIONAL_UNWRAP_WEI ($(fmt_ether "$OPTIONAL_UNWRAP_WEI"))"
|
||||
echo "# WETH9 rail only. WETH10 remains drifted and is intentionally excluded."
|
||||
echo ""
|
||||
|
||||
printf '%-10s | %-22s | %-28s | %-34s\n' "Chain" "Practical Route" "Live Prerequisite" "Best Use"
|
||||
printf '%s\n' "------------------------------------------------------------------------------------------------------------------------"
|
||||
|
||||
for entry in "${CHAINS[@]}"; do
|
||||
IFS='|' read -r name selector rpc native_bridge wrapped_token relay_bridge relay_profile <<< "$entry"
|
||||
if [[ "$TARGET_CHAIN" != "all" && "$TARGET_CHAIN" != "$name" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
source_dest="$(query_dest "$RPC_URL_138" "$CCIPWETH9_BRIDGE_CHAIN138" "$selector")"
|
||||
source_addr="$(extract_enabled_addr "$source_dest")"
|
||||
route_type="disabled"
|
||||
if [[ -n "$source_addr" && -n "$native_bridge" && "$source_addr" == "${native_bridge,,}" ]]; then
|
||||
route_type="direct-native"
|
||||
elif [[ -n "$source_addr" && -n "$relay_bridge" && "$source_addr" == "${relay_bridge,,}" ]]; then
|
||||
route_type="relay-backed"
|
||||
elif [[ -n "$source_addr" ]]; then
|
||||
route_type="other:$source_addr"
|
||||
elif [[ -z "$native_bridge" ]]; then
|
||||
route_type="deploy-first"
|
||||
fi
|
||||
|
||||
inventory_raw="0"
|
||||
if [[ "$route_type" == "relay-backed" && -n "$wrapped_token" && -n "$relay_bridge" && -n "$rpc" ]]; then
|
||||
inventory_raw="$(balance_of "$rpc" "$wrapped_token" "$relay_bridge")"
|
||||
fi
|
||||
inventory_state="$(relay_inventory_note "$inventory_raw")"
|
||||
mainnet_route_to_target="$(query_dest "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
||||
mainnet_dest_enabled="disabled"
|
||||
mainnet_quote_state="n/a"
|
||||
if [[ "$name" != "mainnet" ]]; then
|
||||
mainnet_quote_state="$(quote_fee_state "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
||||
fi
|
||||
if contains_true "$mainnet_route_to_target"; then
|
||||
mainnet_dest_enabled="enabled"
|
||||
fi
|
||||
practical_route="$(practical_route_label "$name" "$mainnet_quote_state")"
|
||||
prereq="$(prereq_text "$name" "$practical_route" "$inventory_state" "$mainnet_dest_enabled")"
|
||||
best_use="$(best_use_text "$name" "$practical_route" "$inventory_state" "$mainnet_dest_enabled")"
|
||||
printf '%-10s | %-22s | %-28s | %-34s\n' "$name" "$practical_route" "$prereq" "$best_use"
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
for entry in "${CHAINS[@]}"; do
|
||||
IFS='|' read -r name selector rpc native_bridge wrapped_token relay_bridge relay_profile <<< "$entry"
|
||||
if [[ "$TARGET_CHAIN" != "all" && "$TARGET_CHAIN" != "$name" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
source_dest="$(query_dest "$RPC_URL_138" "$CCIPWETH9_BRIDGE_CHAIN138" "$selector")"
|
||||
source_addr="$(extract_enabled_addr "$source_dest")"
|
||||
route_type="disabled"
|
||||
if [[ -n "$source_addr" && -n "$native_bridge" && "$source_addr" == "${native_bridge,,}" ]]; then
|
||||
route_type="direct-native"
|
||||
elif [[ -n "$source_addr" && -n "$relay_bridge" && "$source_addr" == "${relay_bridge,,}" ]]; then
|
||||
route_type="relay-backed"
|
||||
elif [[ -n "$source_addr" ]]; then
|
||||
route_type="other:$source_addr"
|
||||
elif [[ -z "$native_bridge" ]]; then
|
||||
route_type="deploy-first"
|
||||
fi
|
||||
|
||||
inventory_raw="0"
|
||||
inventory_state="empty"
|
||||
if [[ "$route_type" == "relay-backed" && -n "$wrapped_token" && -n "$relay_bridge" && -n "$rpc" ]]; then
|
||||
inventory_raw="$(balance_of "$rpc" "$wrapped_token" "$relay_bridge")"
|
||||
inventory_state="$(relay_inventory_note "$inventory_raw")"
|
||||
fi
|
||||
|
||||
mainnet_route_to_target="$(query_dest "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
||||
mainnet_dest_enabled="disabled"
|
||||
mainnet_dest_addr="$(extract_enabled_addr "$mainnet_route_to_target")"
|
||||
mainnet_quote_state="n/a"
|
||||
if [[ "$name" != "mainnet" ]]; then
|
||||
mainnet_quote_state="$(quote_fee_state "$ETHEREUM_MAINNET_RPC" "$MAINNET_CCIP_WETH9_BRIDGE" "$selector")"
|
||||
fi
|
||||
if [[ "$name" == "mainnet" ]]; then
|
||||
mainnet_dest_enabled="n/a"
|
||||
mainnet_dest_addr="n/a"
|
||||
elif contains_true "$mainnet_route_to_target"; then
|
||||
mainnet_dest_enabled="enabled"
|
||||
fi
|
||||
practical_route="$(practical_route_label "$name" "$mainnet_quote_state")"
|
||||
|
||||
remote_to_mainnet="$(query_dest "$rpc" "$native_bridge" "$ETH_MAINNET_SELECTOR")"
|
||||
remote_to_138="$(query_dest "$rpc" "$native_bridge" "$CHAIN138_SELECTOR_VALUE")"
|
||||
mainnet_enabled="disabled"
|
||||
back_addr="$(extract_enabled_addr "$remote_to_138")"
|
||||
return_state="missing"
|
||||
if contains_true "$remote_to_mainnet"; then
|
||||
mainnet_enabled="enabled"
|
||||
fi
|
||||
if [[ -n "$back_addr" ]]; then
|
||||
if [[ "$back_addr" == "$CURRENT_138_WETH9" ]]; then
|
||||
return_state="current"
|
||||
elif [[ "$back_addr" == "$LEGACY_138_WETH9" ]]; then
|
||||
return_state="legacy"
|
||||
else
|
||||
return_state="$back_addr"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "## $name"
|
||||
echo "- Chain 138 mapping: $route_type"
|
||||
echo "- Chain 138 destination: ${source_addr:-disabled}"
|
||||
echo "- Practical route today: $practical_route"
|
||||
if [[ -n "$wrapped_token" ]]; then
|
||||
echo "- Destination wrapped asset: $wrapped_token"
|
||||
fi
|
||||
if [[ -n "$rpc" ]]; then
|
||||
echo "- Deployer native gas on destination: $(fmt_ether "$(native_balance "$rpc")")"
|
||||
fi
|
||||
if [[ "$route_type" == "relay-backed" ]]; then
|
||||
echo "- Relay bridge inventory: $(fmt_ether "$inventory_raw")"
|
||||
echo "- Request coverage at current amount: $(relay_inventory_note "$inventory_raw")"
|
||||
fi
|
||||
if [[ "$mainnet_enabled" == "enabled" ]]; then
|
||||
echo "- Mainnet fan-out from this chain: enabled"
|
||||
elif [[ "$name" != "mainnet" && -n "$native_bridge" ]]; then
|
||||
echo "- Mainnet fan-out from this chain: disabled"
|
||||
fi
|
||||
if [[ "$name" != "mainnet" && -n "$native_bridge" ]]; then
|
||||
echo "- Return path to Chain 138: $return_state"
|
||||
fi
|
||||
echo "- Mainnet hub destination: ${mainnet_dest_addr:-disabled} ($mainnet_dest_enabled)"
|
||||
if [[ "$name" != "mainnet" ]]; then
|
||||
echo "- Mainnet hub quote preflight: $mainnet_quote_state"
|
||||
fi
|
||||
echo "- Best use today: $(best_use_text "$name" "$practical_route" "$inventory_state" "$mainnet_dest_enabled")"
|
||||
echo ""
|
||||
|
||||
if [[ "$practical_route" == "relay-backed-direct" ]]; then
|
||||
echo "Exact Chain 138 send commands:"
|
||||
print_source_send_commands "$name" "$selector"
|
||||
echo ""
|
||||
if [[ -n "$relay_profile" && -f "$relay_profile" ]]; then
|
||||
relay_name="$(basename "$relay_profile")"
|
||||
if [[ "$name" == "mainnet" ]]; then
|
||||
cat <<EOF
|
||||
# Relay worker for this lane:
|
||||
cd "$RELAY_DIR"
|
||||
./start-relay.sh
|
||||
EOF
|
||||
elif [[ "$name" == "bsc" || "$name" == "avalanche" ]]; then
|
||||
profile_name="${name/avalanche/avax}"
|
||||
cat <<EOF
|
||||
# Relay worker for this lane:
|
||||
cd "$RELAY_DIR"
|
||||
./start-relay.sh "$profile_name"
|
||||
EOF
|
||||
fi
|
||||
echo "# Relay profile file: $relay_name"
|
||||
fi
|
||||
else
|
||||
if [[ "$name" == "wemix" ]]; then
|
||||
echo "No unload commands printed because the WEMIX bridge is not deployed in env."
|
||||
else
|
||||
echo "The native Chain 138 mapping is not the practical route of truth here."
|
||||
echo "Why: Chain 138 emits through a custom router that only creates events; it does not natively deliver into these public CCIP bridges."
|
||||
echo "Proof point: the earlier 138 -> Gnosis native-bridge attempt failed for exactly this reason."
|
||||
echo ""
|
||||
if [[ "$mainnet_dest_enabled" == "enabled" && "$mainnet_quote_state" == "quoted" ]]; then
|
||||
echo "Exact mainnet hub commands:"
|
||||
echo "# First bootstrap mainnet. If the mainnet relay bridge is empty, reuse the proven BSC relay plus external bridge recovery flow."
|
||||
print_mainnet_send_commands "$name" "$selector" "$wrapped_token" "$rpc"
|
||||
elif [[ "$mainnet_dest_enabled" == "enabled" ]]; then
|
||||
echo "Mainnet exposes an enabled destination for $name, but the current Mainnet WETH9 source bridge quote path is blocked."
|
||||
echo "Do not broadcast mainnet fan-out sends until the source bridge/router path is repaired or replaced."
|
||||
else
|
||||
echo "Mainnet does not currently expose an enabled destination for $name, so the hub route must be repaired first."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
done
|
||||
38
scripts/deployment/print-gnosis-bootstrap-cast-sequence.sh
Executable file
38
scripts/deployment/print-gnosis-bootstrap-cast-sequence.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deprecated helper kept as a guardrail for historical references.
|
||||
#
|
||||
# Why deprecated:
|
||||
# - Chain 138 emits through a custom router that only creates MessageSent events.
|
||||
# - Native destination mappings like 138 -> Gnosis do not by themselves create
|
||||
# a live delivery path into public-chain CCIP bridges.
|
||||
# - A real 138 -> Gnosis test already failed for this reason.
|
||||
#
|
||||
# Use instead:
|
||||
# ./scripts/deployment/print-chain138-public-chain-unload-routes.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cat <<'EOF'
|
||||
print-gnosis-bootstrap-cast-sequence.sh is deprecated.
|
||||
|
||||
Reason:
|
||||
- `138 -> Gnosis` is not a practical first-hop route with the current live architecture.
|
||||
- Chain 138 uses a custom event-emitting router, so a native Gnosis bridge mapping is not enough to deliver the message.
|
||||
- The earlier live `138 -> Gnosis` attempt failed for exactly that reason.
|
||||
|
||||
Use this instead:
|
||||
|
||||
cd /home/intlc/projects/proxmox/smom-dbis-138
|
||||
./scripts/deployment/print-chain138-public-chain-unload-routes.sh
|
||||
|
||||
Useful examples:
|
||||
|
||||
TARGET_CHAIN=mainnet ./scripts/deployment/print-chain138-public-chain-unload-routes.sh
|
||||
TARGET_CHAIN=bsc ./scripts/deployment/print-chain138-public-chain-unload-routes.sh
|
||||
TARGET_CHAIN=gnosis ./scripts/deployment/print-chain138-public-chain-unload-routes.sh
|
||||
|
||||
Current practical model:
|
||||
- first-hop relay-backed lanes: Mainnet, BSC, Avalanche
|
||||
- hub-distribution lanes after mainnet bootstrap: Gnosis, Cronos, Celo, Polygon, Arbitrum, Optimism, Base
|
||||
- WEMIX: deploy and wire first
|
||||
EOF
|
||||
@@ -19,11 +19,14 @@ RUN_PHASE2="${RUN_PHASE2:-1}"
|
||||
DRY_RUN="${DRY_RUN:-}"
|
||||
RPC_138="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
|
||||
GAS_PRICE="${GAS_PRICE_138:-${GAS_PRICE:-1000000000}}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d}}"
|
||||
POOL_CUSDTCUSDC="${POOL_CUSDTCUSDC:-0x9fcB06Aa1FD5215DC0E91Fd098aeff4B62fEa5C8}"
|
||||
# Keep the default aligned with the live canonical integration/provider mapping.
|
||||
POOL_CUSDTUSDT="${POOL_CUSDTUSDT:-0x6fc60DEDc92a2047062294488539992710b99D71}"
|
||||
POOL_CUSDCUSDC="${POOL_CUSDCUSDC:-0x90bd9Bf18Daa26Af3e814ea224032d015db58Ea5}"
|
||||
INTEGRATION="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-0x86ADA6Ef91A3B450F89f2b751e93B1b7A3218895}}"
|
||||
POOL_CUSDTCUSDC="${POOL_CUSDTCUSDC:-0x9e89bAe009adf128782E19e8341996c596ac40dC}"
|
||||
POOL_CUSDTUSDT="${POOL_CUSDTUSDT:-0x866Cb44b59303d8dc5f4F9E3E7A8e8b0bf238d66}"
|
||||
POOL_CUSDCUSDC="${POOL_CUSDCUSDC:-0xc39B7D0F40838cbFb54649d327f49a6DAC964062}"
|
||||
export DODO_LP_FEE_BPS="${DODO_LP_FEE_BPS:-10}"
|
||||
export DODO_INITIAL_PRICE_1E18="${DODO_INITIAL_PRICE_1E18:-1000000000000000000}"
|
||||
export DODO_K_FACTOR_1E18="${DODO_K_FACTOR_1E18:-0}"
|
||||
export DODO_ENABLE_TWAP="${DODO_ENABLE_TWAP:-false}"
|
||||
|
||||
export RPC_URL_138="$RPC_138"
|
||||
export DODO_PMM_INTEGRATION_ADDRESS="$INTEGRATION"
|
||||
@@ -46,21 +49,15 @@ phase1() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 1a) Create all 3 pools in parallel (if not already created — scripts will fail if pool exists)
|
||||
log "Creating PMM pools (parallel)..."
|
||||
# 1a) Create all 3 pools sequentially (single deployer nonce lane; canonical stable params are i=1e18, k=0)
|
||||
log "Creating PMM pools (sequential)..."
|
||||
if [[ -z "$DRY_RUN" ]]; then
|
||||
( forge script script/dex/CreateCUSDTCUSDCPool.s.sol:CreateCUSDTCUSDCPool \
|
||||
--rpc-url "$RPC_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE" -vv 2>&1 | tee /tmp/pmm-create-cusdt-cusdc.log ) &
|
||||
PID1=$!
|
||||
( forge script script/dex/CreateCUSDTUSDTPool.s.sol:CreateCUSDTUSDTPool \
|
||||
--rpc-url "$RPC_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE" -vv 2>&1 | tee /tmp/pmm-create-cusdt-usdt.log ) &
|
||||
PID2=$!
|
||||
( forge script script/dex/CreateCUSDCUSDCPool.s.sol:CreateCUSDCUSDCPool \
|
||||
--rpc-url "$RPC_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE" -vv 2>&1 | tee /tmp/pmm-create-cusdc-usdc.log ) &
|
||||
PID3=$!
|
||||
wait $PID1 2>/dev/null || true
|
||||
wait $PID2 2>/dev/null || true
|
||||
wait $PID3 2>/dev/null || true
|
||||
forge script script/dex/CreateCUSDTCUSDCPool.s.sol:CreateCUSDTCUSDCPool \
|
||||
--rpc-url "$RPC_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE" -vv 2>&1 | tee /tmp/pmm-create-cusdt-cusdc.log || true
|
||||
forge script script/dex/CreateCUSDTUSDTPool.s.sol:CreateCUSDTUSDTPool \
|
||||
--rpc-url "$RPC_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE" -vv 2>&1 | tee /tmp/pmm-create-cusdt-usdt.log || true
|
||||
forge script script/dex/CreateCUSDCUSDCPool.s.sol:CreateCUSDCUSDCPool \
|
||||
--rpc-url "$RPC_138" --broadcast --private-key "$PRIVATE_KEY" --with-gas-price "$GAS_PRICE" -vv 2>&1 | tee /tmp/pmm-create-cusdc-usdc.log || true
|
||||
else
|
||||
log "[DRY RUN] forge script CreateCUSDTCUSDCPool ..."
|
||||
log "[DRY RUN] forge script CreateCUSDTUSDTPool ..."
|
||||
|
||||
@@ -18,11 +18,21 @@ ORIG_TX_TIMEOUT_SECONDS="${TX_TIMEOUT_SECONDS-}"
|
||||
ORIG_POST_CREATE_POLL_SECONDS="${POST_CREATE_POLL_SECONDS-}"
|
||||
ORIG_POST_CREATE_POLL_INTERVAL="${POST_CREATE_POLL_INTERVAL-}"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
if [[ -f "$SMOM_ROOT/scripts/lib/deployment/dotenv.sh" ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "$SMOM_ROOT/scripts/lib/deployment/dotenv.sh"
|
||||
load_deployment_env --repo-root "$SMOM_ROOT"
|
||||
elif [[ -f "$ENV_FILE" ]]; then
|
||||
had_nounset=0
|
||||
if [[ $- == *u* ]]; then
|
||||
had_nounset=1
|
||||
set +u
|
||||
fi
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
(( had_nounset )) && set -u
|
||||
fi
|
||||
|
||||
[[ -n "$ORIG_RPC_URL_138" ]] && RPC_URL_138="$ORIG_RPC_URL_138"
|
||||
|
||||
Reference in New Issue
Block a user