#!/usr/bin/env bash # Burn deployer's full cXAUC + cXAUT balances, then remint using troy-ounce semantics # (1 full token = 1 troy oz Au). Optional USD target + spot to derive ounces. # # Defaults (override with env): # RETARGET_USD=50000000 Total USD notional (see RETARGET_MODE) # RETARGET_MODE=split split = half USD to cXAUC, half to cXAUT | each = full USD per token # XAU_SPOT_USD=4506.30 USD per troy oz (set from your pricing feed) # # Or set explicit troy ounces (skips USD math): # TROY_OZ_CXAUC=1234.5 TROY_OZ_CXAUT=1234.5 # # DRY_RUN=1 print actions only # SKIP_BURN=1 only mint (dangerous if balance nonzero) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" [ -f .env ] && set -a && source .env && set +a CXAUC="0x290E52a8819A4fbD0714E517225429aA2B70EC6b" CXAUT="0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E" RPC="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}" GAS_LIMIT="${GAS_LIMIT:-200000}" RETARGET_USD="${RETARGET_USD:-50000000}" RETARGET_MODE="${RETARGET_MODE:-split}" XAU_SPOT_USD="${XAU_SPOT_USD:-4506.30}" [ -n "${PRIVATE_KEY:-}" ] || { echo "PRIVATE_KEY not set"; exit 1; } DEPLOYER="$(cast wallet address "$PRIVATE_KEY")" || exit 1 balance_of() { local token="$1" local data raw data="$(cast calldata "balanceOf(address)" "$DEPLOYER")" raw="$(cast rpc eth_call "{\"to\":\"$token\",\"data\":\"$data\"}" latest --rpc-url "$RPC" | tr -d '\n\"')" python3 -c "print(int('$raw', 16))" } to_base_units() { python3 -c " from decimal import Decimal, ROUND_DOWN import sys oz = Decimal(sys.argv[1].strip()) if oz <= 0: sys.exit('troy oz must be positive') b = (oz * Decimal(10**6)).to_integral_value(rounding=ROUND_DOWN) print(int(b)) " "$1" } run_burn() { local token="$1" label="$2" bal="$3" [ "$bal" != "0" ] || { echo " $label: balance 0, skip burn"; return 0; } if [ -n "${DRY_RUN:-}" ]; then echo "[dry-run] burn $label balance=$bal" return 0 fi cast send "$token" "burn(uint256)" "$bal" \ --rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit "$GAS_LIMIT" } run_mint() { local token="$1" label="$2" oz="$3" local base base="$(to_base_units "$oz")" if [ -n "${DRY_RUN:-}" ]; then echo "[dry-run] mint $label ${oz} oz -> $base base to $DEPLOYER" return 0 fi cast send "$token" "mint(address,uint256)" "$DEPLOYER" "$base" \ --rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit "$GAS_LIMIT" } if [ -n "${TROY_OZ_CXAUC:-}" ] && [ -n "${TROY_OZ_CXAUT:-}" ]; then OZ_CXAUC="$TROY_OZ_CXAUC" OZ_CXAUT="$TROY_OZ_CXAUT" else read -r OZ_CXAUC OZ_CXAUT < <(RETARGET_USD="$RETARGET_USD" RETARGET_MODE="$RETARGET_MODE" XAU_SPOT_USD="$XAU_SPOT_USD" python3 <<'PY' import os from decimal import Decimal, ROUND_DOWN spot = Decimal(os.environ["XAU_SPOT_USD"]) total = Decimal(os.environ["RETARGET_USD"]) mode = os.environ.get("RETARGET_MODE", "split") if spot <= 0 or total <= 0: raise SystemExit("RETARGET_USD and XAU_SPOT_USD must be positive") if mode == "split": usd_c = total / Decimal(2) usd_t = total / Decimal(2) elif mode == "each": usd_c = total usd_t = total else: raise SystemExit("RETARGET_MODE must be split or each") oz_c = (usd_c / spot).quantize(Decimal("0.000001"), rounding=ROUND_DOWN) oz_t = (usd_t / spot).quantize(Decimal("0.000001"), rounding=ROUND_DOWN) print(f"{oz_c:f} {oz_t:f}") PY ) fi echo "=== Reconcile cXAUC / cXAUT (deployer $DEPLOYER) ===" echo " RPC: $RPC" echo " Target: cXAUC=${OZ_CXAUC} oz cXAUT=${OZ_CXAUT} oz" echo " (RETARGET_USD=$RETARGET_USD RETARGET_MODE=$RETARGET_MODE XAU_SPOT_USD=$XAU_SPOT_USD)" echo "" BAL_C="$(balance_of "$CXAUC")" BAL_T="$(balance_of "$CXAUT")" echo " Current balances (base units): cXAUC=$BAL_C cXAUT=$BAL_T" if [ -z "${SKIP_BURN:-}" ]; then echo "--- Burn ---" run_burn "$CXAUC" "cXAUC" "$BAL_C" run_burn "$CXAUT" "cXAUT" "$BAL_T" else echo "--- Skip burn (SKIP_BURN=1) ---" fi echo "--- Mint ---" run_mint "$CXAUC" "cXAUC" "$OZ_CXAUC" run_mint "$CXAUT" "cXAUT" "$OZ_CXAUT" echo "Done."