feat: expand non-evm relay and route planning support

This commit is contained in:
defiQUG
2026-04-18 12:05:34 -07:00
parent da78073104
commit 843cdbf71c
113 changed files with 8542 additions and 222 deletions

View File

@@ -0,0 +1,291 @@
#!/usr/bin/env bash
# Plan + verify Chain 138 → 10 networks: move 75% of each c* balance split evenly (7.5% per network).
# Uses CWMultiTokenBridgeL1 on 138 (CW_L1_BRIDGE_CHAIN138) when routes are configured.
#
# Modes:
# --plan-only Write JSON table + human summary (default)
# --check-routes cast call supportedCanonicalToken + destinations per token×chain
# --emit-cmds Print approve + lockAndSend cast lines (dry; review before running)
# --help
#
# Env: PRIVATE_KEY, RPC_URL_138, CW_L1_BRIDGE_CHAIN138, smom-dbis-138/.env
# Optional: RECIPIENT_ADDRESS (default: deployer), OUT_JSON (default: reports/status/c138-bridge-75-split-latest.json)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
cd "$SMOM_ROOT"
MODE="plan"
while [[ $# -gt 0 ]]; do
case "$1" in
--plan-only) MODE="plan" ;;
--check-routes) MODE="check" ;;
--emit-cmds) MODE="emit" ;;
--help|-h)
grep '^#' "$0" | head -20
exit 0
;;
*) echo "Unknown: $1"; exit 1 ;;
esac
shift || true
done
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
# shellcheck disable=SC1090
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
elif [[ -f .env ]]; then
set -a && source .env && set +a
fi
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
OUT_JSON="${OUT_JSON:-$SMOM_ROOT/reports/status/c138-bridge-75-split-latest.json}"
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
DEPLOYER=""
if [[ -n "${PRIVATE_KEY:-}" ]]; then
DEPLOYER="$(cast wallet address "$PRIVATE_KEY" 2>/dev/null || true)"
fi
RECIPIENT="${RECIPIENT_ADDRESS:-$DEPLOYER}"
export RPC OUT_JSON DEPLOYER RECIPIENT BRIDGE
# CCIP chain selectors (Chainlink CCIP mainnet directory / repo BRIDGE_CONFIGURATION.md). Verify before prod.
declare -A SELECTOR=(
[Mainnet]=5009297550715157269
[Optimism]=3734403246176062136
[Cronos]=1456215246176062136
[BSC]=11344663589394136015
[Gnosis]=465200170687744372
[Polygon]=4051577828743386545
[Base]=15971525489660198786
[Arbitrum]=4949039107694359620
[Celo]=1346049177634351622
[Avalanche]=6433500567565415381
)
# name:address (Compliant / canonical c* on 138)
read -r -d '' TOKEN_ROWS << 'EOF' || true
cUSDT:0x93E66202A11B1772E55407B32B44e5Cd8eda7f22
cUSDC:0xf22258f57794CC8E06237084b353Ab30fFfa640b
cUSDT_V2:0x8d342d321DdEe97D0c5011DAF8ca0B59DA617D29
cUSDC_V2:0x1ac3F4942a71E86A9682D91837E1E71b7BACdF99
cEURC:0x8085961F9cF02b4d800A3c6d386D31da4B34266a
cEURT:0xdf4b71c61E5912712C1Bdd451416B9aC26949d72
cGBPC:0x003960f16D9d34F2e98d62723B6721Fb92074aD2
cGBPT:0x350f54e4D23795f86A9c03988c7135357CCaD97c
cAUDC:0xD51482e567c03899eecE3CAe8a058161FD56069D
cJPYC:0xEe269e1226a334182aace90056EE4ee5Cc8A6770
cCHFC:0x873990849DDa5117d7C644f0aF24370797C03885
cCADC:0x54dBd40cF05e15906A2C21f600937e96787f5679
cAUDT:0xC034b8Ff3088f644D492E95619720ba8fB582933
cJPYT:0x54fb3A6b16163D8cFa48EAff79205D1309B1a9A1
cCHFT:0xd91f31725444dD1F53FA6dE236A5e90a8281d970
cCADT:0xAb456be5Db1E1069F55F75E8c8fecAa6a71D1c8F
cXAUC:0x290E52a8819A4fbD0714E517225429aA2B70EC6b
cXAUT:0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E
cAUSDT:0x5fdDF65733e3d590463F68f93Cf16E8c04081271
EOF
export TOKEN_ROWS
mkdir -p "$(dirname "$OUT_JSON")"
python3 << PY
import json, subprocess, os, re, sys
rpc = os.environ.get("RPC", "http://192.168.11.211:8545")
deployer = os.environ.get("DEPLOYER", "")
recipient = os.environ.get("RECIPIENT", deployer)
bridge = os.environ.get("BRIDGE", "")
selectors = {
"Mainnet": 5009297550715157269,
"Optimism": 3734403246176062136,
"Cronos": 1456215246176062136,
"BSC": 11344663589394136015,
"Gnosis": 465200170687744372,
"Polygon": 4051577828743386545,
"Base": 15971525489660198786,
"Arbitrum": 4949039107694359620,
"Celo": 1346049177634351622,
"Avalanche": 6433500567565415381,
}
n_chains = len(selectors)
rows = []
for line in os.environ.get("TOKEN_ROWS", "").strip().split("\n"):
if not line.strip() or line.startswith("#"):
continue
sym, addr = line.split(":", 1)
rows.append((sym.strip(), addr.strip()))
def balance_of(addr):
if not deployer:
return None
r = subprocess.run(
["cast", "call", addr, "balanceOf(address)(uint256)", deployer, "--rpc-url", rpc],
capture_output=True, text=True,
)
if r.returncode != 0:
return None
m = re.match(r"^\s*(\d+)", r.stdout.strip())
return int(m.group(1)) if m else None
plan = {
"schema": "c138-bridge-75-split/v1",
"rpc_url": rpc,
"deployer": deployer,
"recipient": recipient,
"cw_l1_bridge": bridge,
"split": "75% of balance divided evenly across 10 networks (7.5% per chain, integer base units)",
"tokens": [],
}
for sym, addr in rows:
bal = balance_of(addr)
if bal is None:
plan["tokens"].append({"symbol": sym, "address": addr, "error": "balance_of_failed"})
continue
q75 = bal * 75 // 100
per = q75 // n_chains
rem = q75 % n_chains
entry = {
"symbol": sym,
"address": addr,
"balance_wei": str(bal),
"pct_75_wei": str(q75),
"per_chain_wei": str(per),
"remainder_wei": str(rem),
"chains": {},
}
for cname, sel in selectors.items():
entry["chains"][cname] = {"selector": str(sel), "amount_wei": str(per)}
plan["tokens"].append(entry)
path = os.environ.get("OUT_JSON", "")
with open(path, "w") as f:
json.dump(plan, f, indent=2)
print(json.dumps({"written": path, "tokens": len(plan["tokens"])}))
PY
export RPC DEPLOYER RECIPIENT BRIDGE OUT_JSON
export TOKEN_ROWS="$TOKEN_ROWS"
python3 - <<'PY'
import json, os
with open(os.environ["OUT_JSON"]) as f:
p = json.load(f)
print("\n=== c* 75% / 10 networks (per-chain amount, 6 dp human for fiat-style) ===\n")
print(f"Deployer: {p.get('deployer','?')}\nRecipient: {p.get('recipient','?')}\nBridge: {p.get('cw_l1_bridge') or '(unset)'}\n")
for t in p["tokens"]:
if "error" in t:
print(f"{t['symbol']}: {t['error']}")
continue
sym = t["symbol"]
per = int(t["per_chain_wei"])
if sym.startswith("cXAU"):
hu = per / 1e6
print(f"{sym:<10} per chain: {hu:,.6f} troy oz (wei={t['per_chain_wei']})")
else:
hu = per / 1e6
print(f"{sym:<10} per chain: {hu:,.6f} tokens (wei={t['per_chain_wei']})")
print("\nJSON:", os.environ["OUT_JSON"])
PY
if [[ "$MODE" == "plan" ]]; then
exit 0
fi
[[ -n "$BRIDGE" ]] || { echo "Set CW_L1_BRIDGE_CHAIN138"; exit 1; }
code=$(cast code "$BRIDGE" --rpc-url "$RPC" 2>/dev/null || echo "0x")
[[ -n "$code" && "$code" != "0x" ]] || { echo "No contract at CW_L1_BRIDGE_CHAIN138=$BRIDGE"; exit 1; }
if [[ "$MODE" == "check" ]]; then
echo ""
echo "=== Route checks: $BRIDGE ==="
while IFS= read -r line; do
[[ -z "$line" ]] && continue
sym="${line%%:*}"
addr="${line#*:}"
if sup_raw=$(cast call "$BRIDGE" "supportedCanonicalToken(address)(bool)" "$addr" --rpc-url "$RPC" 2>/dev/null); then
echo "$sym supported=$sup_raw"
else
echo "$sym supported=(query reverted — non-CW ABI or older build; rely on destinations below)"
fi
for net in Mainnet Optimism Cronos BSC Gnosis Polygon Base Arbitrum Celo Avalanche; do
sel="${SELECTOR[$net]}"
dest=$(cast call "$BRIDGE" "destinations(address,uint64)(address,bool)" "$addr" "$sel" --rpc-url "$RPC" 2>/dev/null || echo "ERR")
echo " $net ($sel): $dest"
done
done <<< "$TOKEN_ROWS"
exit 0
fi
if [[ "$MODE" == "emit" ]]; then
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required for --emit-cmds"; exit 1; }
[[ -n "$RECIPIENT" ]] || { echo "RECIPIENT_ADDRESS or deployer required"; exit 1; }
LINK_TOKEN="${LINK_TOKEN_CHAIN138:-${LINK_TOKEN:-}}"
[[ -n "$LINK_TOKEN" ]] || { echo "Set LINK_TOKEN or LINK_TOKEN_CHAIN138 for fee approval lines"; exit 1; }
export LINK_TOKEN
echo ""
echo "=== Review-only cast snippets (feeToken=LINK on this bridge: approve LINK, approve token, lockAndSend) ==="
OUT_CAST="${OUT_CAST:-$SMOM_ROOT/reports/status/c138-bridge-75-split-cast-commands.sh}"
export OUT_CAST
{
echo "#!/usr/bin/env bash"
echo "# Generated: c138-cw-bridge-75-split.sh --emit-cmds"
echo "# Review destinations + reserve verifier before running. Fund LINK for fees."
echo "set -euo pipefail"
# Quoted heredoc: do not let bash expand \$PRIVATE_KEY into the generated file.
python3 <<'PY'
import json, os, subprocess
rpc = os.environ["RPC"]
bridge = os.environ["BRIDGE"]
recipient = os.environ["RECIPIENT"]
link = os.environ["LINK_TOKEN"]
with open(os.environ["OUT_JSON"]) as f:
plan = json.load(f)
selectors = {
"Mainnet": 5009297550715157269,
"Optimism": 3734403246176062136,
"Cronos": 1456215246176062136,
"BSC": 11344663589394136015,
"Gnosis": 465200170687744372,
"Polygon": 4051577828743386545,
"Base": 15971525489660198786,
"Arbitrum": 4949039107694359620,
"Celo": 1346049177634351622,
"Avalanche": 6433500567565415381,
}
for t in plan["tokens"]:
if "error" in t or int(t.get("per_chain_wei", 0)) == 0:
continue
sym, token, amt = t["symbol"], t["address"], t["per_chain_wei"]
for cname, sel in selectors.items():
chk = subprocess.run(
["cast", "call", bridge, "destinations(address,uint64)(address,bool)", token, str(sel), "--rpc-url", rpc],
capture_output=True, text=True,
)
if chk.returncode != 0 or "true" not in chk.stdout:
continue
fee = subprocess.run(
["cast", "call", bridge,
"calculateFee(address,uint64,address,uint256)(uint256)",
token, str(sel), recipient, amt,
"--rpc-url", rpc],
capture_output=True, text=True,
)
fq = fee.stdout.strip().split()[0] if fee.returncode == 0 else "0"
print("")
print(f"# {sym} -> {cname} selector={sel} amount={amt} fee_wei={fq}")
print(f"cast send {link} \"approve(address,uint256)\" {bridge} {fq} --rpc-url {rpc} --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 120000")
print(f"cast send {token} \"approve(address,uint256)\" {bridge} {amt} --rpc-url {rpc} --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 400000")
print(f"cast send {bridge} \"lockAndSend(address,uint64,address,uint256)\" {token} {sel} {recipient} {amt} --rpc-url {rpc} --private-key \"$PRIVATE_KEY\" --legacy --gas-limit 4000000")
PY
} > "$OUT_CAST"
chmod +x "$OUT_CAST"
echo "Wrote enabled-routes-only commands: $OUT_CAST"
wc -l "$OUT_CAST"
exit 0
fi

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env bash
# Register GRU v2 (CompliantFiatTokenV2) addresses on CWMultiTokenBridgeL1 and
# configureDestination for the same 10 CCIP lanes as cUSDT/cUSDC (receivers from CW_BRIDGE_*).
#
# Note: The CWMultiTokenBridgeL1 deployed at CW_L1_BRIDGE_CHAIN138 on Chain 138 does **not**
# include configureSupportedCanonicalToken in runtime bytecode (older build). Do not send that
# call — it always reverts. Destinations + lockAndSend are the supported path.
#
# Usage:
# bash scripts/deployment/cw-l1-bootstrap-gru-v2-ccip-routes.sh [--dry-run]
#
# Env: PROJECT_ROOT load via proxmox scripts/lib/load-project-env.sh or smom-dbis-138/.env
# CW_L1_BRIDGE_CHAIN138, RPC_URL_138, PRIVATE_KEY (deployer = bridge admin)
# CW_BRIDGE_MAINNET, CW_BRIDGE_OPTIMISM, CW_BRIDGE_CRONOS, CW_BRIDGE_BSC,
# CW_BRIDGE_GNOSIS, CW_BRIDGE_POLYGON, CW_BRIDGE_BASE, CW_BRIDGE_ARBITRUM,
# CW_BRIDGE_CELO, CW_BRIDGE_AVALANCHE
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
cd "$SMOM_ROOT"
DRY_RUN=0
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
# shellcheck disable=SC1090
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
elif [[ -f .env ]]; then
set -a && source .env && set +a
fi
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
[[ -n "$BRIDGE" && "$BRIDGE" != "0x0000000000000000000000000000000000000000" ]] || {
echo "Set CW_L1_BRIDGE_CHAIN138" >&2
exit 1
}
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required" >&2; exit 1; }
# GRU v2 on Chain 138 (wallet-token-submissions.chain138.json)
V2=(
"0x243e6581Dc8a98d98B92265858b322b193555C81"
"0x2bAFA83d8fF8BaE9505511998987D0659791605B"
"0x707508D223103f5D2d9EFBc656302c9d48878b29"
"0xee17c18E10E55ce23F7457D018aAa2Fb1E64B281"
"0xfb37aFd415B70C5cEDc9bA58a72D517207b769Bb"
"0x2c751bBE4f299b989b3A8c333E0A966cdcA6Fd98"
"0x60B7FB8e0DD0Be8595AD12Fe80AE832861Be747c"
"0xe799033c87fE0CE316DAECcefBE3134CC74b76a9"
"0xF0F0F81bE3D033D8586bAfd2293e37eE2f615647"
"0x89477E982847023aaB5C3492082cd1bB4b1b9Ef1"
)
# selector|receiver (must match c138-cw-bridge-75-split.sh / BRIDGE_CONFIGURATION)
declare -a ROUTES=(
"5009297550715157269|${CW_BRIDGE_MAINNET:-}"
"3734403246176062136|${CW_BRIDGE_OPTIMISM:-}"
"1456215246176062136|${CW_BRIDGE_CRONOS:-}"
"11344663589394136015|${CW_BRIDGE_BSC:-}"
"465200170687744372|${CW_BRIDGE_GNOSIS:-}"
"4051577828743386545|${CW_BRIDGE_POLYGON:-}"
"15971525489660198786|${CW_BRIDGE_BASE:-}"
"4949039107694359620|${CW_BRIDGE_ARBITRUM:-}"
"1346049177634351622|${CW_BRIDGE_CELO:-}"
"6433500567565415381|${CW_BRIDGE_AVALANCHE:-}"
)
send() {
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "[dry-run] $*"
return 0
fi
"$@"
}
echo "=== configureDestination (10 GRU v2 tokens x up to 10 chains) ==="
for t in "${V2[@]}"; do
for row in "${ROUTES[@]}"; do
sel="${row%%|*}"
recv="${row#*|}"
if [[ -z "$recv" || "$recv" == "0x0000000000000000000000000000000000000000" ]]; then
echo "SKIP selector=$sel (receiver unset)"
continue
fi
echo "-> token=$t selector=$sel recv=$recv"
send cast send "$BRIDGE" "configureDestination(address,uint64,address,bool)" "$t" "$sel" "$recv" true \
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 500000
done
done
echo ""
echo "Done. Verify with: cast call $BRIDGE \"destinations(address,uint64)(address,bool)\" <token> <selector> --rpc-url $RPC"

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# After cw-l1-bootstrap-gru-v2-ccip-routes.sh: approve LINK + tokens, then lockAndSend
# 90% / 10 per-chain amounts to RECIPIENT_ADDRESS (default Ulysse EVM).
#
# Usage:
# RECIPIENT_ADDRESS=0x... bash scripts/deployment/cw-l1-bridge-gru-v2-90pct-lock-and-send.sh [--dry-run]
#
# Env: CW_L1_BRIDGE_CHAIN138, RPC_URL_138, PRIVATE_KEY
# LINK: fee token on bridge (defaults to feeToken() on bridge)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
cd "$SMOM_ROOT"
DRY_RUN=0
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
elif [[ -f .env ]]; then
set -a && source .env && set +a
fi
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
RPC="${RPC_URL_138:-http://192.168.11.211:8545}"
RECIPIENT="${RECIPIENT_ADDRESS:-0xCbe669ECe9e3Da7dD1688207e545EDDA9a64A514}"
[[ -n "$BRIDGE" ]] || { echo "Set CW_L1_BRIDGE_CHAIN138" >&2; exit 1; }
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required" >&2; exit 1; }
LINK_TOKEN="${LINK_TOKEN_CHAIN138:-${LINK_TOKEN:-}}"
if [[ -z "$LINK_TOKEN" ]]; then
LINK_TOKEN=$(cast call "$BRIDGE" "feeToken()(address)" --rpc-url "$RPC" 2>/dev/null | grep -oE '0x[a-fA-F0-9]{40}' | head -1)
fi
[[ -n "$LINK_TOKEN" && "$LINK_TOKEN" != "0x0000000000000000000000000000000000000000" ]] || {
echo "Could not resolve LINK fee token; set LINK_TOKEN_CHAIN138" >&2
exit 1
}
MAX_U256="115792089237316195423570985008687907853269984665640564039457584007913129639935"
# token|per_chain_wei (90% / 10 floors from operator plan)
declare -a PAIRS=(
"0x243e6581Dc8a98d98B92265858b322b193555C81|3822172740000"
"0x2bAFA83d8fF8BaE9505511998987D0659791605B|2778945930000"
"0x707508D223103f5D2d9EFBc656302c9d48878b29|4123219860000"
"0xee17c18E10E55ce23F7457D018aAa2Fb1E64B281|4744441350000"
"0xfb37aFd415B70C5cEDc9bA58a72D517207b769Bb|6522556410000"
"0x2c751bBE4f299b989b3A8c333E0A966cdcA6Fd98|3557466000000"
"0x60B7FB8e0DD0Be8595AD12Fe80AE832861Be747c|6810124410000"
"0xe799033c87fE0CE316DAECcefBE3134CC74b76a9|2471337720000"
"0xF0F0F81bE3D033D8586bAfd2293e37eE2f615647|2421904773"
"0x89477E982847023aaB5C3492082cd1bB4b1b9Ef1|2421904773"
)
# Same 10 selectors as bootstrap script (order must match)
declare -a SELECTORS=(
5009297550715157269
3734403246176062136
1456215246176062136
11344663589394136015
465200170687744372
4051577828743386545
15971525489660198786
4949039107694359620
1346049177634351622
6433500567565415381
)
send() {
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "[dry-run] $*"
return 0
fi
"$@"
}
echo "Bridge=$BRIDGE recipient=$RECIPIENT LINK=$LINK_TOKEN"
echo "=== Approvals: LINK + each GRU v2 token to bridge (max) ==="
send cast send "$LINK_TOKEN" "approve(address,uint256)" "$BRIDGE" "$MAX_U256" \
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 120000
for row in "${PAIRS[@]}"; do
token="${row%%|*}"
echo "approve token $token"
send cast send "$token" "approve(address,uint256)" "$BRIDGE" "$MAX_U256" \
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 120000
done
echo ""
echo "=== lockAndSend (10 tokens x 10 chains) ==="
for row in "${PAIRS[@]}"; do
token="${row%%|*}"
amt="${row#*|}"
for sel in "${SELECTORS[@]}"; do
echo "lockAndSend token=$token selector=$sel amount=$amt"
send cast send "$BRIDGE" "lockAndSend(address,uint64,address,uint256)" \
"$token" "$sel" "$RECIPIENT" "$amt" \
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy --gas-limit 5000000
done
done
echo "Done."

View File

@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# Admin template: CWMultiTokenBridgeL1.configureDestination for the eight chains
# that are not Mainnet / Avalanche (those typically already wired for cUSDT/cUSDC).
#
# Uses receiver addresses from CW_BRIDGE_OPTIMISM, CW_BRIDGE_CRONOS, CW_BRIDGE_BSC,
# CW_BRIDGE_GNOSIS, CW_BRIDGE_POLYGON, CW_BRIDGE_BASE, CW_BRIDGE_ARBITRUM, CW_BRIDGE_CELO
# (see smom-dbis-138/.env). CCIP selectors match docs/deployment/BRIDGE_CONFIGURATION.md
# and Chainlink CCIP directory — verify before mainnet use.
#
# Target contract: CW_L1_BRIDGE_CHAIN138 — onlyAdmin.
#
# Usage:
# bash scripts/deployment/cw-l1-configure-destination-8-chains.sh [--print-only]
# # default writes reports/status/cw-l1-configure-destination-8-chains.cast.sh
#
# Env:
# CW_L1_BRIDGE_CHAIN138, RPC_URL_138 (or CHAIN138_RPC), PRIVATE_KEY (only if you execute the .sh)
# CUSDT_ADDRESS_138, CUSDC_ADDRESS_138 (or override CANONICAL_TOKENS="0x...,0x...")
#
# Does not broadcast unless you run the generated file. Never commit private keys.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
cd "$SMOM_ROOT"
PRINT_ONLY=0
[[ "${1:-}" == "--print-only" ]] && PRINT_ONLY=1
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
# shellcheck disable=SC1090
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
elif [[ -f .env ]]; then
set -a && source .env && set +a
fi
BRIDGE="${CW_L1_BRIDGE_CHAIN138:-}"
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
OUT_SH="${OUT_SH:-$SMOM_ROOT/reports/status/cw-l1-configure-destination-8-chains.cast.sh}"
if [[ -z "$BRIDGE" || "$BRIDGE" == "0x0000000000000000000000000000000000000000" ]]; then
echo "Set CW_L1_BRIDGE_CHAIN138 in .env" >&2
exit 1
fi
# Remaining eight (exclude Mainnet + Avalanche)
declare -a ROWS=(
"Optimism|3734403246176062136|${CW_BRIDGE_OPTIMISM:-}"
"Cronos|1456215246176062136|${CW_BRIDGE_CRONOS:-}"
"BSC|11344663589394136015|${CW_BRIDGE_BSC:-}"
"Gnosis|465200170687744372|${CW_BRIDGE_GNOSIS:-}"
"Polygon|4051577828743386545|${CW_BRIDGE_POLYGON:-}"
"Base|15971525489660198786|${CW_BRIDGE_BASE:-}"
"Arbitrum|4949039107694359620|${CW_BRIDGE_ARBITRUM:-}"
"Celo|1346049177634351622|${CW_BRIDGE_CELO:-}"
)
TOKENS_STR="${CANONICAL_TOKENS:-}"
if [[ -z "$TOKENS_STR" ]]; then
T1="${CUSDT_ADDRESS_138:-${COMPLIANT_USDT_ADDRESS:-}}"
T2="${CUSDC_ADDRESS_138:-${COMPLIANT_USDC_ADDRESS:-}}"
TOKENS_STR="${T1},${T2}"
fi
IFS=',' read -r -a TOKEN_ADDRS <<< "$(echo "$TOKENS_STR" | tr -d '[:space:]')"
[[ -n "${TOKEN_ADDRS[0]:-}" && "${TOKEN_ADDRS[0]}" == 0x* ]] || { echo "Set CUSDT_ADDRESS_138 or CANONICAL_TOKENS" >&2; exit 1; }
gen() {
local sym="$1" token="$2"
echo ""
echo "# --- $sym $token ---"
for row in "${ROWS[@]}"; do
IFS='|' read -r name sel recv <<< "$row"
if [[ -z "$recv" || "$recv" == "0x0000000000000000000000000000000000000000" ]]; then
echo "# SKIP $name: CW_BRIDGE_* receiver unset"
continue
fi
echo "# $sym -> $name (selector $sel) receiver=$recv"
echo "cast send \"$BRIDGE\" \\"
echo " \"configureDestination(address,uint64,address,bool)\" \\"
echo " \"$token\" \\"
echo " \"$sel\" \\"
echo " \"$recv\" \\"
echo " true \\"
echo " --rpc-url \"$RPC\" \\"
echo " --private-key \"\$PRIVATE_KEY\" \\"
echo " --legacy \\"
echo " --gas-limit 500000"
echo ""
echo "# Verify: cast call \"$BRIDGE\" \"destinations(address,uint64)(address,bool)\" \"$token\" \"$sel\" --rpc-url \"$RPC\""
echo ""
done
}
{
echo "#!/usr/bin/env bash"
echo "# Generated: cw-l1-configure-destination-8-chains.sh"
echo "# Admin-only: configureDestination on CW L1 for eight chains (not Mainnet/Avalanche)."
echo "# Requires: admin key = deployer or bridge admin; source .env with PRIVATE_KEY."
echo "set -euo pipefail"
echo ""
if [[ -n "${TOKEN_ADDRS[1]:-}" && "${TOKEN_ADDRS[1]}" == 0x* ]]; then
gen "cUSDT" "${TOKEN_ADDRS[0]}"
gen "cUSDC" "${TOKEN_ADDRS[1]}"
else
gen "TOKEN" "${TOKEN_ADDRS[0]}"
fi
} > "$OUT_SH"
chmod +x "$OUT_SH"
echo "Wrote: $OUT_SH"
echo ""
if [[ "$PRINT_ONLY" -eq 1 ]] || [[ -t 1 ]]; then
cat "$OUT_SH"
fi

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# Deploy Tether-style ISO-4217 complements (cAUDT, cJPYT, cCHFT, cCADT) on Chain 138.
#
# Why not CREATE2 via CREATE2Factory (0x750E4a8adCe9f0e67A420aBE91342DC64Eb90825)?
# On Core/Besu, factory.deploy(bytes,uint256) with CompliantFiatToken initcode consistently
# OOGs (~2.6M8M+ gas) even with high limits; eth_call traces succeed — use standard CREATE here.
#
# Uses scoped Forge (scripts/forge/scope.sh create tokens) so solc does not pull the full repo.
# Compiles with --evm-version paris: Core 138 Besu may reject Cancun bytecode (invalid opcode 0x5f/PUSH0).
#
# Requires: PRIVATE_KEY; RPC via RPC_URL_138, CHAIN138_RPC, or RPC_URL (see below).
# Idempotent: skips if contract already has code at the address from env (if set).
#
# Usage: from repo root:
# bash smom-dbis-138/scripts/deployment/deploy-compliant-fiat-tether-iso-chain138.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROXMOX_ROOT="$(cd "$SMOM_ROOT/.." && pwd)"
cd "$SMOM_ROOT"
if [[ -f "$PROXMOX_ROOT/scripts/lib/load-project-env.sh" ]]; then
# shellcheck disable=SC1090
PROJECT_ROOT="$PROXMOX_ROOT" source "$PROXMOX_ROOT/scripts/lib/load-project-env.sh"
elif [[ -f .env ]]; then
set -a && source .env && set +a
fi
[[ -n "${PRIVATE_KEY:-}" ]] || { echo "PRIVATE_KEY required"; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq required (for receipt status check)"; exit 1; }
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
# Paris bytecode deploy ~2M gas; leave headroom for Besu.
GAS_LIMIT_DEPLOY="${GAS_LIMIT_DEPLOY:-3500000}"
EVM_VERSION_CHAIN138="${EVM_VERSION_CHAIN138:-paris}"
OWNER="$(cast wallet address "$PRIVATE_KEY")"
# Ensure scoped artifacts match chain EVM (Paris for Chain 138 Core).
bash scripts/forge/scope.sh build tokens --evm-version "$EVM_VERSION_CHAIN138" --force
declare -a SPECS=(
"cAUDT|Tether AUD (Compliant)|AUD|${CAUDT_ADDRESS_138:-}"
"cJPYT|Tether JPY (Compliant)|JPY|${CJPYT_ADDRESS_138:-}"
"cCHFT|Tether CHF (Compliant)|CHF|${CCHFT_ADDRESS_138:-}"
"cCADT|Tether CAD (Compliant)|CAD|${CCADT_ADDRESS_138:-}"
)
DEPLOYED_SNIPPET="# After deploy (paste into smom-dbis-138/.env next to other C*ADDRESS_138):"
while IFS='|' read -r sym name cc existing; do
if [[ -n "$existing" && "$existing" != "0x0000000000000000000000000000000000000000" ]]; then
code=$(cast code "$existing" --rpc-url "$RPC" 2>/dev/null || echo "0x")
if [[ -n "$code" && "$code" != "0x" ]]; then
echo "Skip $sym (already deployed at $existing)"
continue
fi
fi
echo "Deploy $sym..."
out=$(bash scripts/forge/scope.sh create tokens contracts/tokens/CompliantFiatToken.sol:CompliantFiatToken \
--rpc-url "$RPC" --private-key "$PRIVATE_KEY" --legacy \
--evm-version "$EVM_VERSION_CHAIN138" \
--gas-limit "$GAS_LIMIT_DEPLOY" \
--broadcast \
--constructor-args "$name" "$sym" 6 "$cc" "$OWNER" "$OWNER" 1000000000000 2>&1) || {
echo "$out"
exit 1
}
echo "$out"
txh=$(printf '%s\n' "$out" | sed -n 's/.*Transaction hash: \(0x[0-9a-fA-F]\{64\}\).*/\1/p' | head -1)
if [[ -n "$txh" ]]; then
st=$(cast receipt "$txh" --json --rpc-url "$RPC" 2>/dev/null | jq -r '.status // "0x0"')
if [[ "$st" != "0x1" ]]; then
echo "error: tx $txh failed on-chain (status=$st). Raise GAS_LIMIT_DEPLOY or inspect: cast run $txh --rpc-url $RPC" >&2
exit 1
fi
fi
addr=$(printf '%s\n' "$out" | sed -n 's/.*Deployed to: \(0x[0-9a-fA-F]\{40\}\).*/\1/p' | head -1)
if [[ -z "$addr" ]]; then
addr=$(printf '%s\n' "$out" | sed -n 's/.*"contract":"\(0x[0-9a-fA-F]\{40\}\)".*/\1/p' | head -1)
fi
if [[ -n "$addr" ]]; then
ccode=$(cast code "$addr" --rpc-url "$RPC" 2>/dev/null || echo "0x")
if [[ -z "$ccode" || "$ccode" == "0x" ]]; then
echo "error: no code at $addr after successful receipt — aborting" >&2
exit 1
fi
fi
if [[ -n "$addr" ]]; then
var=""
case "$sym" in
cAUDT) var="CAUDT_ADDRESS_138" ;;
cJPYT) var="CJPYT_ADDRESS_138" ;;
cCHFT) var="CCHFT_ADDRESS_138" ;;
cCADT) var="CCADT_ADDRESS_138" ;;
esac
DEPLOYED_SNIPPET+=$'\n'"${var}=${addr}"
fi
done < <(printf '%s\n' "${SPECS[@]}")
echo ""
echo "$DEPLOYED_SNIPPET"
echo ""
echo "Verify: cast code <addr> --rpc-url \"\$RPC_URL_138\""
echo "Optional mint (random 26M85M per token, deployer must hold minter role):"
echo " bash smom-dbis-138/scripts/mint-compliant-fiat-tether-iso-138.sh"

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Deploy DODOPMMIntegration on each L2 (BSC, Polygon, Base, Optimism, Arbitrum, Avalanche, Cronos, Gnosis).
# Deploy DODOPMMIntegration on each L2/public chain (BSC, Polygon, Base, Optimism, Arbitrum, Avalanche, Cronos, Gnosis, Celo).
# Uses .env for RPC and token addresses. Chains via tag: --chain bsc polygon ... (or DEPLOY_PMM_L2S_FILTER in .env).
# Usage: ./scripts/deployment/deploy-pmm-all-l2s.sh [--chain bsc polygon base]
set -euo pipefail
@@ -37,6 +37,7 @@ CHAINS=(
"AVALANCHE:43114:AVALANCHE_RPC_URL"
"CRONOS:25:CRONOS_RPC_URL"
"GNOSIS:100:GNOSIS_MAINNET_RPC"
"CELO:42220:CELO_MAINNET_RPC"
)
for entry in "${CHAINS[@]}"; do
@@ -59,14 +60,16 @@ for entry in "${CHAINS[@]}"; do
cusdc_var="${name}_COMPLIANT_USDC_ADDRESS"
cusdt_var_alt="COMPLIANT_USDT_${name}"
cusdc_var_alt="COMPLIANT_USDC_${name}"
cwusdt_var="CWUSDT_${name}"
cwusdc_var="CWUSDC_${name}"
# Per-chain cUSDT/cUSDC (optional): CUSDT_ADDRESS_<chainId> / CUSDC_ADDRESS_<chainId> or POLYGON_COMPLIANT_USDT_ADDRESS etc.
cusdt_chain="CUSDT_ADDRESS_${chain_id}"
cusdc_chain="CUSDC_ADDRESS_${chain_id}"
dvm="$(first_set_env "$dvm_var" "DODO_VENDING_MACHINE_ADDRESS" || true)"
usdt="$(first_set_env "$usdt_var" "$usdt_var_alt" "OFFICIAL_USDT_ADDRESS" || true)"
usdc="$(first_set_env "$usdc_var" "$usdc_var_alt" "OFFICIAL_USDC_ADDRESS" || true)"
compliant_usdt="$(first_set_env "$cusdt_var" "$cusdt_var_alt" "$cusdt_chain" || true)"
compliant_usdc="$(first_set_env "$cusdc_var" "$cusdc_var_alt" "$cusdc_chain" || true)"
compliant_usdt="$(first_set_env "$cusdt_var" "$cusdt_var_alt" "$cusdt_chain" "$cwusdt_var" || true)"
compliant_usdc="$(first_set_env "$cusdc_var" "$cusdc_var_alt" "$cusdc_chain" "$cwusdc_var" || true)"
compliant_usdt="${compliant_usdt:-$usdt}"
compliant_usdc="${compliant_usdc:-$usdc}"

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Seed Uniswap V2 pools on Chain 138: each canonical asset vs WETH (see script header).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
if [[ -f scripts/load-env.sh ]]; then
# shellcheck source=/dev/null
source scripts/load-env.sh
fi
export CHAIN138_RPC_URL="${CHAIN138_RPC_URL:-${RPC_URL_138:-http://192.168.11.211:8545}}"
exec npx hardhat run scripts/chain138/seed-uni-v2-weth-quote-pairs.js --network chain138

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$PROJECT_ROOT"
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1090
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
load_deployment_env --repo-root "${PROJECT_ROOT}"
elif [[ -f "$PROJECT_ROOT/.env" ]]; then
set -a
# shellcheck disable=SC1090
source "$PROJECT_ROOT/.env"
set +a
fi
require_env() {
local name="$1"
if [[ -z "${!name:-}" ]]; then
echo "ERROR: $name is required" >&2
exit 1
fi
}
require_env WEMIX_RPC
require_env CCIPWETH9_BRIDGE_WEMIX
require_env CCIPWETH10_BRIDGE_WEMIX
require_env CCIP_ROUTER_WEMIX
require_env WETH9_WEMIX
require_env WETH10_WEMIX
require_env LINK_TOKEN_WEMIX
require_env WEMIXSCAN_API_KEY
flatten_b64() {
local source_file="$1"
forge flatten "$source_file" | python3 -c 'import sys,base64; print(base64.b64encode(sys.stdin.buffer.read()).decode())'
}
constructor_args() {
local weth="$1"
cast abi-encode 'constructor(address,address,address)' \
"$CCIP_ROUTER_WEMIX" \
"$weth" \
"$LINK_TOKEN_WEMIX" | sed 's/^0x//'
}
submit_verify() {
local address="$1"
local contract_name="$2"
local source_file="$3"
local weth="$4"
local payload
payload="$(mktemp)"
python3 - "$payload" "$contract_name" "$(flatten_b64 "$source_file")" "$(constructor_args "$weth")" <<'PY'
import json
import sys
from pathlib import Path
path, contract_name, contract_source, constructor_arguments = sys.argv[1:]
body = {
"contract_name": contract_name,
"compiler": "v0.8.20+commit.a1b79de6",
"runs_optimizer": 200,
"optimization_enabled": 1,
"contract_source": contract_source,
"constructor_arguments": constructor_arguments,
"libraries": [],
}
Path(path).write_text(json.dumps(body))
PY
echo "Submitting verification for $contract_name at $address..."
local response_file
response_file="$(mktemp)"
local http_code
http_code="$(
curl --max-time 90 \
-sS \
-o "$response_file" \
-w '%{http_code}' \
-H "api-key: ${WEMIXSCAN_API_KEY}" \
-H 'Content-Type: application/json' \
-X POST \
--data @"$payload" \
"https://explorerapi.wemix.com/v1/contracts/${address}/verify"
)"
echo "HTTP ${http_code}"
cat "$response_file"
echo
if [[ "$http_code" == "200" ]]; then
echo "Verification request accepted for $contract_name"
elif [[ "$http_code" == "504" ]]; then
echo "WARNING: Wemix explorer timed out while compiling $contract_name. Check the explorer code tab manually." >&2
else
echo "WARNING: Verification not accepted for $contract_name" >&2
fi
}
check_status() {
local address="$1"
echo "Checking explorer code endpoint for $address..."
curl --max-time 30 \
-sS \
-H "api-key: ${WEMIXSCAN_API_KEY}" \
"https://explorerapi.wemix.com/v1/contracts/${address}/code" || true
echo
}
submit_verify "$CCIPWETH9_BRIDGE_WEMIX" "CCIPWETH9Bridge" "contracts/ccip/CCIPWETH9Bridge.sol" "$WETH9_WEMIX"
submit_verify "$CCIPWETH10_BRIDGE_WEMIX" "CCIPWETH10Bridge" "contracts/ccip/CCIPWETH10Bridge.sol" "$WETH10_WEMIX"
check_status "$CCIPWETH9_BRIDGE_WEMIX"
check_status "$CCIPWETH10_BRIDGE_WEMIX"