- Resolve stash: merge load_deployment_env path with secure-secrets and CR/LF RPC strip - create-pmm-full-mesh-chain138.sh delegates to sync-chain138-pmm-pools-from-json.sh - env.additions.example: canonical PMM pool defaults (cUSDT/USDT per crosscheck) - Include Chain138 scripts, official mirror deploy scaffolding, and prior staged changes Made-with: Cursor
282 lines
14 KiB
Bash
Executable File
282 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Check deployer balances on all configured networks, estimate gas costs via gas API,
|
|
# ensure sufficient network token, then optionally deploy (Chain 138 phased core).
|
|
# Uses PRIVATE_KEY from .env (smom-dbis-138).
|
|
#
|
|
# Balance check: On EVM chains only the NATIVE token can be used for gas. We check that
|
|
# token for each network (cast balance = native currency). ERC-20 tokens (USDT, LINK, etc.)
|
|
# cannot be used for gas unless a meta-tx relayer is in use.
|
|
#
|
|
# Gas token per network (only these can be used for gas):
|
|
# Chain 138, Ethereum, Base, Optimism, Sepolia, Base Sepolia, Optimism Sepolia → ETH
|
|
# Polygon, Polygon Amoy → MATIC
|
|
# BSC → BNB
|
|
# Avalanche → AVAX
|
|
# Cronos → CRO
|
|
# Gnosis → xDAI
|
|
# Arbitrum → ETH
|
|
#
|
|
# Usage:
|
|
# ./scripts/deployment/check-balances-gas-and-deploy.sh # report only
|
|
# ./scripts/deployment/check-balances-gas-and-deploy.sh --deploy # deploy on Chain 138 (requires PRIVATE_KEY)
|
|
#
|
|
# Uses .env for RPCs and PRIVATE_KEY; --deploy is a tag (not .env).
|
|
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
cd "$PROJECT_ROOT"
|
|
|
|
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
|
|
source "$SCRIPT_DIR/../lib/deployment/prompts.sh"
|
|
load_deployment_env
|
|
parse_deploy_tag "$@"
|
|
DO_DEPLOY="${DO_DEPLOY:-0}"
|
|
|
|
# Infura: use Basic Auth URL when INFURA_PROJECT_SECRET is set (fixes "error sending request" from Infura)
|
|
SCRIPT_LIB="$SCRIPT_DIR/../lib"
|
|
[ -f "${SCRIPT_LIB}/infura.sh" ] && source "${SCRIPT_LIB}/infura.sh"
|
|
|
|
# Deployer address: use DEPLOYER_ADDRESS if set (matches MetaMask Portfolio), else derive from PRIVATE_KEY
|
|
if [ -n "${DEPLOYER_ADDRESS:-}" ]; then
|
|
DEPLOYER="${DEPLOYER_ADDRESS}"
|
|
[[ "$DEPLOYER" != 0x* ]] && DEPLOYER="0x$DEPLOYER"
|
|
else
|
|
if [ -z "${PRIVATE_KEY:-}" ]; then
|
|
echo "ERROR: Set PRIVATE_KEY in .env or set DEPLOYER_ADDRESS=0x4A666F96fC8764181194447A7dFdb7d471b301C8 for read-only balance check"
|
|
exit 1
|
|
fi
|
|
DEPLOYER=$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || true)
|
|
if [ -z "$DEPLOYER" ]; then
|
|
echo "ERROR: Could not derive deployer address from PRIVATE_KEY (is 'cast' available?)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Gas estimate for "full" deploy per chain (conservative)
|
|
GAS_FULL_DEPLOY_138="${GAS_FULL_DEPLOY_138:-5000000}"
|
|
GAS_FULL_DEPLOY_MAINNET="${GAS_FULL_DEPLOY_MAINNET:-5000000}"
|
|
BUFFER_BPS=120 # 20% buffer
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# Short form for matching MetaMask Portfolio (0x4A66...b301C8)
|
|
DEPLOYER_SHORT="${DEPLOYER:0:6}...${DEPLOYER: -6}"
|
|
|
|
echo -e "${BLUE}============================================${NC}"
|
|
echo -e "${BLUE}Deployer balance & gas check (all networks)${NC}"
|
|
echo -e "${BLUE}============================================${NC}"
|
|
echo "Deployer: $DEPLOYER_SHORT (full: $DEPLOYER)"
|
|
echo " (Matches MetaMask Portfolio when RPCs are reachable; use DEPLOYER_ADDRESS=0x... to check a specific wallet)"
|
|
echo ""
|
|
|
|
# Format wei to ether with 8 decimal places (avoids excessive decimals from cast)
|
|
format_wei_to_ether() {
|
|
local wei="${1:-0}"
|
|
local eth
|
|
eth=$(echo "scale=10; $wei / 1000000000000000000" | bc 2>/dev/null) || eth="0"
|
|
printf "%.8f" "${eth:-0}"
|
|
}
|
|
|
|
# Infura Gas API: supported chain IDs (EIP-1559 style; API returns suggestedMaxFeePerGas in gwei)
|
|
INFURA_GAS_API_CHAINS="1 137 10 8453 42161 11155111 84532 11155420 80002 43114 56 100 324 534352"
|
|
|
|
# Get gas price in wei from Infura Gas API for a given chain_id (optional).
|
|
# Returns wei or empty; caller should fallback to cast gas-price.
|
|
get_gas_price_wei_infura_api() {
|
|
local chain_id="${1:-1}"
|
|
local key=""
|
|
if [ -n "${INFURA_GAS_API:-}" ]; then
|
|
local url="$INFURA_GAS_API"
|
|
if [[ "$url" == *"/v3/"* ]]; then
|
|
key="${url#*v3/}"
|
|
key="${key%%/*}"
|
|
else
|
|
key="$url"
|
|
fi
|
|
elif [ -n "${INFURA_PROJECT_ID:-}" ]; then
|
|
key="${INFURA_PROJECT_ID}"
|
|
fi
|
|
if [ -z "$key" ] || ! command -v curl >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
local api_url="https://gas.api.infura.io/v3/${key}/networks/${chain_id}/suggestedGasFees"
|
|
local res
|
|
res=$(curl -sL --connect-timeout 5 "$api_url" 2>/dev/null || true)
|
|
if [ -z "$res" ] || echo "$res" | grep -qi "error\|required\|private key\|not found"; then
|
|
return 1
|
|
fi
|
|
local gwei
|
|
gwei=$(echo "$res" | jq -r '.medium.suggestedMaxFeePerGas // .standard.suggestedMaxFeePerGas // .standard.maxFeePerGas // .low.suggestedMaxFeePerGas // empty' 2>/dev/null)
|
|
if [ -z "$gwei" ] || [ "$gwei" = "null" ]; then
|
|
return 1
|
|
fi
|
|
local wei
|
|
wei=$(echo "scale=0; ($gwei) * 1000000000 / 1" | bc 2>/dev/null)
|
|
[ -n "$wei" ] && [ "$wei" != "0" ] && echo "$wei"
|
|
}
|
|
|
|
# Get mainnet gas price from Infura Gas API (preferred) or RPC fallback.
|
|
get_mainnet_gas_price_wei() {
|
|
local wei
|
|
wei=$(get_gas_price_wei_infura_api 1)
|
|
if [ -z "$wei" ] || [ "$wei" = "0" ]; then
|
|
[ -n "${ETHEREUM_MAINNET_RPC:-}" ] && command -v cast >/dev/null 2>&1 && wei=$(cast gas-price --rpc-url "$ETHEREUM_MAINNET_RPC" 2>/dev/null || true)
|
|
fi
|
|
[ -n "$wei" ] && [ "$wei" != "0" ] && echo "$wei" || echo "30000000000"
|
|
}
|
|
|
|
# Resolve gas price for a chain: try Infura Gas API first (if chain supported), else RPC, else default_wei.
|
|
# Usage: get_gas_price_for_chain chain_id rpc_url default_wei
|
|
# Output: wei (and sets GAS_SOURCE="Infura Gas API" or "RPC" or "default")
|
|
get_gas_price_for_chain() {
|
|
local chain_id="$1"
|
|
local rpc_url="$2"
|
|
local default_wei="${3:-30000000000}"
|
|
local wei=""
|
|
[ -n "$rpc_url" ] && type ensure_infura_rpc_url &>/dev/null && rpc_url=$(ensure_infura_rpc_url "$rpc_url")
|
|
if [[ " $INFURA_GAS_API_CHAINS " == *" $chain_id "* ]]; then
|
|
wei=$(get_gas_price_wei_infura_api "$chain_id")
|
|
[ -n "$wei" ] && GAS_SOURCE="Infura Gas API" && echo "$wei" && return
|
|
fi
|
|
if [ -n "$rpc_url" ] && command -v cast >/dev/null 2>&1; then
|
|
wei=$(cast gas-price --rpc-url "$rpc_url" 2>/dev/null || true)
|
|
[ -n "$wei" ] && [ "$wei" != "0" ] && GAS_SOURCE="RPC" && echo "$wei" && return
|
|
fi
|
|
GAS_SOURCE="default"
|
|
echo "$default_wei"
|
|
}
|
|
|
|
mainnet_gas_wei=$(get_mainnet_gas_price_wei)
|
|
mainnet_gas_gwei=$(printf "%.2f" $(echo "scale=4; $mainnet_gas_wei / 1000000000" | bc 2>/dev/null || echo "30"))
|
|
echo "Gas prices (verified via Infura Gas API where supported, else RPC or default):"
|
|
echo " Ethereum Mainnet (1): ${mainnet_gas_gwei} gwei (Infura Gas API)"
|
|
echo ""
|
|
|
|
# Check native (gas) token balance for one network. Only this token can be used for gas on EVM.
|
|
check_network() {
|
|
local name="$1"
|
|
local rpc="$2"
|
|
local chain_id="${3:-}"
|
|
local gas_token="${4:-ETH}"
|
|
local gas_limit="${5:-$GAS_FULL_DEPLOY_MAINNET}"
|
|
local gas_price_wei="${6:-$mainnet_gas_wei}"
|
|
|
|
if [ -z "$rpc" ]; then
|
|
echo -e " ${YELLOW}$name (chain $chain_id): no RPC — gas token = $gas_token (not checked)${NC}"
|
|
return 0
|
|
fi
|
|
type ensure_infura_rpc_url &>/dev/null && rpc=$(ensure_infura_rpc_url "$rpc")
|
|
local balance_wei
|
|
balance_wei=$(cast balance "$DEPLOYER" --rpc-url "$rpc" 2>/dev/null) || balance_wei=""
|
|
local rpc_ok=1
|
|
if [ -z "$balance_wei" ] || ! [[ "$balance_wei" =~ ^[0-9]+$ ]]; then
|
|
rpc_ok=0
|
|
balance_wei="0"
|
|
fi
|
|
local balance_display
|
|
if [ "$rpc_ok" = "0" ]; then
|
|
balance_display="(unable to fetch — RPC unreachable?)"
|
|
else
|
|
balance_display=$(format_wei_to_ether "$balance_wei")
|
|
fi
|
|
local cost_wei
|
|
cost_wei=$(echo "$gas_limit * $gas_price_wei" | bc 2>/dev/null || echo "0")
|
|
local cost_with_buffer
|
|
cost_with_buffer=$(echo "$cost_wei * $BUFFER_BPS / 100" | bc 2>/dev/null || echo "$cost_wei")
|
|
local cost_display
|
|
cost_display=$(format_wei_to_ether "$cost_with_buffer")
|
|
local ok=0
|
|
[ "$rpc_ok" = "1" ] && [ "$(echo "$balance_wei >= $cost_with_buffer" | bc 2>/dev/null)" = "1" ] && ok=1
|
|
if [ "$ok" = "1" ]; then
|
|
echo -e " ${GREEN}$name (chain $chain_id): gas token $gas_token — balance ${balance_display}, required ~${cost_display} — OK for deploy${NC}"
|
|
elif [ "$rpc_ok" = "0" ]; then
|
|
echo -e " ${YELLOW}$name (chain $chain_id): gas token $gas_token — balance ${balance_display}, required ~${cost_display} — check RPC / run from network with access${NC}"
|
|
else
|
|
echo -e " ${RED}$name (chain $chain_id): gas token $gas_token — balance ${balance_display}, required ~${cost_display} — INSUFFICIENT for deploy${NC}"
|
|
fi
|
|
return $((1 - ok))
|
|
}
|
|
|
|
# All networks: order matches MetaMask Portfolio (Ethereum, BSC, Arbitrum, Optimism, Polygon, Cronos, …)
|
|
# Gas needed from Infura Gas API or RPC. Native gas token only (ETH, BNB, POL, CRO, etc.).
|
|
echo "Checking gas token balance (same order as MetaMask Portfolio where applicable):"
|
|
echo ""
|
|
any_insufficient=0
|
|
# --- Portfolio main networks (Ethereum, BSC, Arbitrum, Optimism, Polygon, Cronos) ---
|
|
check_network "Ethereum Mainnet" "${ETHEREUM_MAINNET_RPC:-}" "1" "ETH" "$GAS_FULL_DEPLOY_MAINNET" "$mainnet_gas_wei" || any_insufficient=1
|
|
check_network "BSC" "${BSC_RPC_URL:-${BSC_MAINNET_RPC:-https://bsc-dataseed.binance.org}}" "56" "BNB" "5000000" "$(get_gas_price_for_chain 56 "${BSC_RPC_URL:-${BSC_MAINNET_RPC:-}}" "5000000000")" || true
|
|
check_network "Arbitrum" "${ARBITRUM_MAINNET_RPC:-https://arbitrum-one.publicnode.com}" "42161" "ETH" "5000000" "$(get_gas_price_for_chain 42161 "${ARBITRUM_MAINNET_RPC:-}" "100000000")" || true
|
|
check_network "Optimism" "${OPTIMISM_MAINNET_RPC:-}" "10" "ETH" "5000000" "$(get_gas_price_for_chain 10 "${OPTIMISM_MAINNET_RPC:-}" "1000000")" || true
|
|
check_network "Polygon" "${POLYGON_MAINNET_RPC:-}" "137" "POL" "5000000" "$(get_gas_price_for_chain 137 "${POLYGON_MAINNET_RPC:-}" "50000000000")" || any_insufficient=1
|
|
check_network "Cronos" "${CRONOS_RPC_URL:-https://evm.cronos.org}" "25" "CRO" "5000000" "$(get_gas_price_for_chain 25 "${CRONOS_RPC_URL:-}" "1000000000")" || true
|
|
# --- Chain 138 (project chain) ---
|
|
check_network "Chain 138" "${RPC_URL_138:-}" "138" "ETH" "$GAS_FULL_DEPLOY_138" "$(cast gas-price --rpc-url "${RPC_URL_138:-http://192.168.11.211:8545}" 2>/dev/null || echo "1000000000")" || any_insufficient=1
|
|
# --- Other mainnets ---
|
|
check_network "Base" "${BASE_MAINNET_RPC:-}" "8453" "ETH" "5000000" "$(get_gas_price_for_chain 8453 "${BASE_MAINNET_RPC:-}" "100000000")" || true
|
|
check_network "Avalanche" "${AVALANCHE_RPC_URL:-${AVALANCHE_MAINNET_RPC:-https://avalanche-c-chain.publicnode.com}}" "43114" "AVAX" "5000000" "$(get_gas_price_for_chain 43114 "${AVALANCHE_RPC_URL:-${AVALANCHE_MAINNET_RPC:-}}" "25000000000")" || true
|
|
check_network "Gnosis" "${GNOSIS_MAINNET_RPC:-${GNOSIS_RPC:-https://rpc.gnosischain.com}}" "100" "xDAI" "5000000" "$(get_gas_price_for_chain 100 "${GNOSIS_MAINNET_RPC:-${GNOSIS_RPC:-}}" "1000000000")" || true
|
|
# --- Testnets ---
|
|
check_network "Ethereum Sepolia" "${ETHEREUM_SEPOLIA_RPC:-}" "11155111" "ETH" "5000000" "$(get_gas_price_for_chain 11155111 "${ETHEREUM_SEPOLIA_RPC:-}" "20000000000")" || true
|
|
check_network "Polygon Amoy" "${POLYGON_AMOY_RPC:-}" "80002" "MATIC" "5000000" "$(get_gas_price_for_chain 80002 "${POLYGON_AMOY_RPC:-}" "30000000000")" || true
|
|
check_network "Base Sepolia" "${BASE_SEPOLIA_RPC:-}" "84532" "ETH" "5000000" "$(get_gas_price_for_chain 84532 "${BASE_SEPOLIA_RPC:-}" "100000000")" || true
|
|
check_network "Optimism Sepolia" "${OPTIMISM_SEPOLIA_RPC:-}" "11155420" "ETH" "5000000" "$(get_gas_price_for_chain 11155420 "${OPTIMISM_SEPOLIA_RPC:-}" "1000000")" || true
|
|
|
|
echo ""
|
|
echo "Gas token summary: each chain uses only its native token for gas. ERC-20 (USDT, LINK, etc.) cannot pay gas."
|
|
echo "Gas needed above was computed from: Infura Gas API (chains 1, 10, 56, 100, 137, 42161, 43114, 8453, 84532, 80002, 11155111, 11155420) or RPC eth_gasPrice when API unavailable."
|
|
|
|
echo ""
|
|
if [ "${DO_DEPLOY:-0}" = "1" ]; then
|
|
if [ -n "${DEPLOYER_ADDRESS:-}" ]; then
|
|
echo -e "${RED}ERROR: --deploy requires PRIVATE_KEY in .env (unset DEPLOYER_ADDRESS to use key-derived deployer).${NC}"
|
|
exit 1
|
|
fi
|
|
if [ -z "${PRIVATE_KEY:-}" ]; then
|
|
echo -e "${RED}ERROR: PRIVATE_KEY required for --deploy.${NC}"
|
|
exit 1
|
|
fi
|
|
echo -e "${BLUE}Deploy phase (Chain 138 only; other chains need CCIP_* env)${NC}"
|
|
RPC_138="${RPC_URL_138:-http://192.168.11.211:8545}"
|
|
balance_138=$(cast balance "$DEPLOYER" --rpc-url "$RPC_138" 2>/dev/null || echo "0")
|
|
need_138=$(echo "5000000 * 1000000000 * $BUFFER_BPS / 100" | bc 2>/dev/null || echo "6000000000000000000")
|
|
if [ -n "$balance_138" ] && [ "$(echo "$balance_138 >= $need_138" | bc 2>/dev/null)" = "1" ]; then
|
|
echo "Deploying phased core (01_DeployCore, 02_DeployBridges) on Chain 138..."
|
|
export PRIVATE_KEY
|
|
out_01=$(mktemp)
|
|
trap "rm -f $out_01" EXIT
|
|
# Chain 138 often requires minimum gas price (e.g. 1 gwei)
|
|
GAS_PRICE_138="${GAS_PRICE_138:-1000000000}"
|
|
if forge script script/deploy/01_DeployCore.s.sol:DeployCore \
|
|
--rpc-url "$RPC_138" \
|
|
--private-key "$PRIVATE_KEY" \
|
|
--broadcast \
|
|
--with-gas-price "$GAS_PRICE_138" \
|
|
-vvv 2>&1 | tee "$out_01"; then
|
|
reg=$(grep "UNIVERSAL_ASSET_REGISTRY" "$out_01" | tail -1 | grep -oE "0x[a-fA-F0-9]{40}" | head -1)
|
|
if [ -n "$reg" ] && [ -n "${CCIP_ROUTER:-}" ]; then
|
|
export UNIVERSAL_ASSET_REGISTRY="$reg"
|
|
forge script script/deploy/02_DeployBridges.s.sol:DeployBridges \
|
|
--rpc-url "$RPC_138" \
|
|
--private-key "$PRIVATE_KEY" \
|
|
--broadcast \
|
|
--with-gas-price "$GAS_PRICE_138" \
|
|
-vvv 2>&1 || echo "02_DeployBridges failed or skipped."
|
|
fi
|
|
else
|
|
echo "01_DeployCore failed or skipped (may already be deployed)."
|
|
fi
|
|
echo -e "${GREEN}Chain 138 deploy phase finished.${NC}"
|
|
else
|
|
echo -e "${RED}Chain 138 balance insufficient for deploy (need more native ETH on 138). Fund deployer and re-run.${NC}"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Run with --deploy to run deployment on Chain 138 (phased core)."
|
|
echo "Other chains: fund deployer with that chain's native token (MATIC, BNB, etc.) and set CCIP_* in .env for DeployAll."
|
|
fi
|