- 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
938 lines
32 KiB
Bash
Executable File
938 lines
32 KiB
Bash
Executable File
#!/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}"
|