Files
smom-dbis-138/scripts/deployment/deploy-cw-stablecoin-vault-and-wire.sh
defiQUG 76aa419320 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
2026-04-07 23:40:52 -07:00

275 lines
8.0 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)"
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