- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains - Omit embedded publish git dirs and empty placeholders from index Made-with: Cursor
199 lines
6.8 KiB
Bash
Executable File
199 lines
6.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Grant MINTER_ROLE and BURNER_ROLE on CompliantWrappedToken (cW*) to the chain CW bridge
|
|
# when the deployer still has DEFAULT_ADMIN_ROLE and roles are not frozen.
|
|
#
|
|
# Use after DeployCWTokens or when verify shows MINTER=false BURNER=false for some symbols.
|
|
# See: docs/07-ccip/CW_DEPLOY_AND_WIRE_RUNBOOK.md
|
|
#
|
|
# Usage (repo root):
|
|
# ./scripts/deployment/grant-cw-bridge-roles-on-chain.sh AVALANCHE [--dry-run]
|
|
# ./scripts/deployment/grant-cw-bridge-roles-on-chain.sh MAINNET [--dry-run]
|
|
#
|
|
# Env: PRIVATE_KEY, CW_BRIDGE_<CHAIN>, <CHAIN>_RPC from smom-dbis-138/.env (via dotenv).
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
SMOM="${PROJECT_ROOT}/smom-dbis-138"
|
|
|
|
CHAIN="${1:-}"
|
|
DRY_RUN=false
|
|
[[ "${2:-}" == "--dry-run" ]] && DRY_RUN=true
|
|
|
|
if [[ -z "$CHAIN" ]]; then
|
|
echo "Usage: $0 <CHAIN_NAME> [--dry-run]" >&2
|
|
echo " CHAIN_NAME: MAINNET CRONOS BSC POLYGON GNOSIS AVALANCHE BASE ARBITRUM OPTIMISM CELO WEMIX" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! -f "$SMOM/.env" ]]; then
|
|
echo "Missing $SMOM/.env" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -f "$SMOM/scripts/lib/deployment/dotenv.sh" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$SMOM/scripts/lib/deployment/dotenv.sh"
|
|
load_deployment_env --repo-root "$SMOM"
|
|
else
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
source "$SMOM/.env"
|
|
set +a
|
|
fi
|
|
|
|
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required in smom-dbis-138/.env" >&2; exit 1; }
|
|
command -v cast >/dev/null 2>&1 || { echo "cast (foundry) required" >&2; exit 1; }
|
|
command -v python3 >/dev/null 2>&1 || { echo "python3 required (gas bump)" >&2; exit 1; }
|
|
|
|
declare -A CHAIN_NAME_TO_ID=(
|
|
[MAINNET]=1 [CRONOS]=25 [BSC]=56 [POLYGON]=137 [GNOSIS]=100
|
|
[AVALANCHE]=43114 [BASE]=8453 [ARBITRUM]=42161 [OPTIMISM]=10 [CELO]=42220 [WEMIX]=1111
|
|
)
|
|
|
|
chain_id="${CHAIN_NAME_TO_ID[$CHAIN]:-}"
|
|
if [[ -z "$chain_id" ]]; then
|
|
echo "Unknown chain: $CHAIN" >&2
|
|
exit 2
|
|
fi
|
|
|
|
bridge_var="CW_BRIDGE_${CHAIN}"
|
|
bridge="${!bridge_var:-}"
|
|
if [[ -z "$bridge" ]]; then
|
|
echo "SKIP: CW_BRIDGE_${CHAIN} is not set (nothing to grant on this chain)."
|
|
exit 0
|
|
fi
|
|
|
|
rpc=""
|
|
case "$CHAIN" in
|
|
MAINNET) rpc="${ETH_MAINNET_RPC_URL:-${ETHEREUM_MAINNET_RPC:-}}" ;;
|
|
CRONOS) rpc="${CRONOS_RPC_URL:-${CRONOS_RPC:-}}" ;;
|
|
BSC) rpc="${BSC_RPC_URL:-}" ;;
|
|
POLYGON) rpc="${POLYGON_MAINNET_RPC:-${POLYGON_RPC_URL:-}}" ;;
|
|
GNOSIS) rpc="${GNOSIS_RPC:-}" ;;
|
|
AVALANCHE) rpc="${AVALANCHE_RPC_URL:-}" ;;
|
|
BASE) rpc="${BASE_MAINNET_RPC:-}" ;;
|
|
ARBITRUM) rpc="${ARBITRUM_MAINNET_RPC:-${ARBITRUM_MAINNET_RPC_URL:-${ARBITRUM_RPC_URL:-}}}" ;;
|
|
OPTIMISM) rpc="${OPTIMISM_MAINNET_RPC:-}" ;;
|
|
CELO) rpc="${CELO_RPC:-${CELO_MAINNET_RPC:-}}" ;;
|
|
WEMIX) rpc="${WEMIX_RPC:-${WEMIX_MAINNET_RPC:-}}" ;;
|
|
esac
|
|
|
|
if [[ -z "$rpc" ]]; then
|
|
echo "No RPC URL for $CHAIN (set e.g. AVALANCHE_RPC_URL)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Busy networks: base fee can exceed cast defaults — bump legacy gas price (percent, default 150%).
|
|
CW_GRANT_GAS_BUMP_PCT="${CW_GRANT_GAS_BUMP_PCT:-150}"
|
|
_gas_price_wei() {
|
|
local base
|
|
base="$(cast gas-price --rpc-url "$rpc" 2>/dev/null | head -1 | tr -d '[:space:]')"
|
|
[[ -z "$base" || ! "$base" =~ ^[0-9]+$ ]] && base="50000000000"
|
|
python3 -c "import sys; b=int(sys.argv[1]); p=int(sys.argv[2]); print(max(b * p // 100, b + 10**9))" "$base" "$CW_GRANT_GAS_BUMP_PCT"
|
|
}
|
|
GAS_PRICE_WEI="${CW_GRANT_GAS_PRICE_WEI:-$(_gas_price_wei)}"
|
|
|
|
DEPLOYER="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null)" || { echo "Invalid PRIVATE_KEY" >&2; exit 1; }
|
|
MINTER_ROLE="$(cast keccak "MINTER_ROLE")"
|
|
BURNER_ROLE="$(cast keccak "BURNER_ROLE")"
|
|
# OpenZeppelin AccessControl DEFAULT_ADMIN_ROLE
|
|
DEFAULT_ADMIN_ROLE="0x0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
CW_TOKEN_PREFIXES=(CWUSDT CWUSDC CWAUSDT CWEURC CWEURT CWGBPC CWGBPT CWAUDC CWJPYC CWCHFC CWCADC CWXAUC CWXAUT)
|
|
|
|
resolve_token() {
|
|
local prefix="$1"
|
|
local chain_var="${prefix}_${CHAIN}"
|
|
local address_var="${prefix}_ADDRESS_${chain_id}"
|
|
if [[ -n "${!chain_var:-}" ]]; then
|
|
printf '%s' "${!chain_var}"
|
|
return
|
|
fi
|
|
if [[ -n "${!address_var:-}" ]]; then
|
|
printf '%s' "${!address_var}"
|
|
return
|
|
fi
|
|
printf ''
|
|
}
|
|
|
|
echo "=== Grant cW* bridge roles: $CHAIN (chainId $chain_id) ==="
|
|
echo "RPC: $rpc"
|
|
echo "Bridge: $bridge"
|
|
echo "From: $DEPLOYER"
|
|
echo "Gas: legacy wei=$GAS_PRICE_WEI (override CW_GRANT_GAS_PRICE_WEI or bump CW_GRANT_GAS_BUMP_PCT)"
|
|
if $DRY_RUN; then echo "MODE: dry-run (no transactions)"; fi
|
|
echo ""
|
|
|
|
granted=0
|
|
skipped=0
|
|
failed=0
|
|
|
|
for prefix in "${CW_TOKEN_PREFIXES[@]}"; do
|
|
token="$(resolve_token "$prefix")"
|
|
[[ -z "$token" ]] && continue
|
|
|
|
# Some proxies revert on operationalRolesFrozen(); skip that probe and rely on grantRole + admin check.
|
|
is_admin="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$DEFAULT_ADMIN_ROLE" "$DEPLOYER" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
|
if [[ "$is_admin" != "true" ]]; then
|
|
echo " SKIP $prefix $token — deployer lacks DEFAULT_ADMIN_ROLE"
|
|
skipped=$((skipped + 1))
|
|
continue
|
|
fi
|
|
|
|
hm="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
|
hb="$(cast call "$token" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null || echo "false")"
|
|
|
|
if [[ "$hm" == "true" && "$hb" == "true" ]]; then
|
|
skipped=$((skipped + 1))
|
|
continue
|
|
fi
|
|
|
|
echo " FIX $prefix $token MINTER=$hm BURNER=$hb"
|
|
|
|
if $DRY_RUN; then
|
|
[[ "$hm" != "true" ]] && echo " would: grantRole(MINTER_ROLE, bridge)"
|
|
[[ "$hb" != "true" ]] && echo " would: grantRole(BURNER_ROLE, bridge)"
|
|
granted=$((granted + 1))
|
|
continue
|
|
fi
|
|
|
|
_after_minter() {
|
|
cast call "$token" "hasRole(bytes32,address)(bool)" "$MINTER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null | grep -q true
|
|
}
|
|
_after_burner() {
|
|
cast call "$token" "hasRole(bytes32,address)(bool)" "$BURNER_ROLE" "$bridge" --rpc-url "$rpc" 2>/dev/null | grep -q true
|
|
}
|
|
|
|
if [[ "$hm" != "true" ]]; then
|
|
cast send "$token" "grantRole(bytes32,address)" "$MINTER_ROLE" "$bridge" \
|
|
--rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy --gas-price "$GAS_PRICE_WEI" >/dev/null 2>&1 || true
|
|
sleep 2
|
|
if _after_minter; then
|
|
echo " OK MINTER_ROLE"
|
|
else
|
|
echo " FAIL MINTER_ROLE" >&2
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
if [[ "$hb" != "true" ]]; then
|
|
cast send "$token" "grantRole(bytes32,address)" "$BURNER_ROLE" "$bridge" \
|
|
--rpc-url "$rpc" --private-key "$PRIVATE_KEY" --legacy --gas-price "$GAS_PRICE_WEI" >/dev/null 2>&1 || true
|
|
sleep 2
|
|
if _after_burner; then
|
|
echo " OK BURNER_ROLE"
|
|
else
|
|
echo " FAIL BURNER_ROLE" >&2
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
granted=$((granted + 1))
|
|
done
|
|
|
|
echo ""
|
|
echo "Summary: chains=$CHAIN fixed_tokens=$granted skipped=$skipped tx_failures=$failed"
|
|
if [[ "$failed" -gt 0 ]]; then
|
|
exit 1
|
|
fi
|