feat: add hybx omnl stack and gas pmm tooling
Some checks failed
CI/CD Pipeline / Lint and Format (push) Failing after 46s
CI/CD Pipeline / Terraform Validation (push) Failing after 35s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 37s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 1m50s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 2m19s
Validation / validate-genesis (push) Successful in 51s
Validation / validate-terraform (push) Failing after 39s
Validation / validate-kubernetes (push) Failing after 10s
CI/CD Pipeline / Solidity Contracts (push) Failing after 12m56s
Validation / validate-smart-contracts (push) Failing after 12s
CI/CD Pipeline / Security Scanning (push) Failing after 15m52s
Validation / validate-security (push) Failing after 10m59s
Validation / validate-documentation (push) Failing after 17s
Validate Token List / validate (push) Failing after 30s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 26s
Verify Deployment / Verify Deployment (push) Failing after 56s

This commit is contained in:
defiQUG
2026-04-24 12:56:40 -07:00
parent c3d4c786fa
commit f3d2961b97
80 changed files with 7192 additions and 2 deletions

View File

@@ -0,0 +1,404 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROJECT_ROOT="$(cd "$REPO_ROOT/.." && pwd)"
BUNDLE_JSON="$REPO_ROOT/config/gas-pmm-execution-bundle.json"
OUT_DIR="$PROJECT_ROOT/reports/status"
OUT_JSON="$OUT_DIR/gas-pmm-execution-bundle-latest.json"
OUT_MD="$OUT_DIR/gas-pmm-execution-bundle-latest.md"
source "$REPO_ROOT/scripts/lib/deployment/dotenv.sh"
load_deployment_env --repo-root "$REPO_ROOT"
mkdir -p "$OUT_DIR"
json_escape() {
jq -Rn --arg v "${1:-}" '$v'
}
resolve_env_from_list() {
local joined="${1:-}"
local value=""
local key
[[ -z "$joined" ]] && return 0
IFS='|' read -r -a keys <<< "$joined"
for key in "${keys[@]}"; do
if [[ -n "${!key:-}" ]]; then
value="${!key}"
break
fi
done
printf '%s' "$value"
}
hex_to_addr() {
local value="${1#0x}"
[[ ${#value} -ge 40 ]] || return 1
printf '0x%s\n' "${value: -40}"
}
code_status() {
local rpc="$1"
local address="$2"
if [[ -z "$rpc" || -z "$address" ]]; then
printf 'missing'
return
fi
local code=""
code="$(timeout 12s cast code --rpc-url "$rpc" "$address" 2>/dev/null || true)"
if [[ -n "$code" && "$code" != "0x" ]]; then
printf 'has_code'
else
printf 'no_code'
fi
}
symbol_call() {
local rpc="$1"
local address="$2"
local value
value="$(timeout 8s cast call --rpc-url "$rpc" "$address" "symbol()(string)" 2>/dev/null || true)"
value="${value#\"}"
value="${value%\"}"
printf '%s' "$value"
}
normalize_key() {
printf '%s' "$1" | tr '[:lower:]- ' '[:upper:]__'
}
append_finding() {
local finding="$1"
if [[ -n "$FINDINGS_JOINED" ]]; then
FINDINGS_JOINED+="|$finding"
else
FINDINGS_JOINED="$finding"
fi
}
emit_findings_json() {
local joined="${1:-}"
if [[ -z "$joined" ]]; then
printf '[]'
return
fi
jq -Rn --arg joined "$joined" '$joined | split("|") | map(select(length > 0))'
}
rollout_order_json="$(jq -c '[.chains[] | {chainId, network}]' "$BUNDLE_JSON")"
generated_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
json_file="$(mktemp)"
md_file="$(mktemp)"
{
echo "{"
echo " \"generatedAt\": $(json_escape "$generated_at"),"
echo " \"bundle\": $(jq '.bundleName' "$BUNDLE_JSON"),"
echo " \"description\": $(jq '.description' "$BUNDLE_JSON"),"
echo " \"poolDefaults\": $(jq '.poolDefaults' "$BUNDLE_JSON"),"
echo " \"rolloutOrder\": $rollout_order_json,"
echo " \"chains\": ["
} >"$json_file"
{
echo "# Gas PMM Execution Bundle"
echo
echo "Generated at: \`$generated_at\`"
echo
echo "This bundle covers the remaining \`22\` planned gas-family DODO PMM rows across \`11\` public chains."
echo
echo "Execution posture:"
echo
echo "- Each chain has two target pools: wrapped-native self-quote and wrapped-native versus stable."
echo "- The current expected pool addresses are still scaffold slots, so \`no_code\` is the normal pre-deploy state."
echo "- Stable-quote pool creation needs a fresh \`STABLE_PRICE_1E18\` input at execution time."
echo "- Wrapped-native quote defaults in this bundle use the repo's canonical or documented live contracts instead of the old \`0xaa...\` placeholders."
echo "- The deploy step reuses the legacy integration constructor slots; those seed addresses are only deployment anchors, while the gas pairs themselves are created through generic \`createPool(...)\` calls."
} >"$md_file"
first_chain=1
chain_count=0
ready_count=0
blocked_count=0
while IFS= read -r chain_row; do
chain_id="$(jq -r '.chainId' <<<"$chain_row")"
network="$(jq -r '.network' <<<"$chain_row")"
chain_key="$(jq -r '.chainKey' <<<"$chain_row")"
rpc_envs_joined="$(jq -r '.rpcEnv | join("|")' <<<"$chain_row")"
integration_env="$(jq -r '.integrationEnv' <<<"$chain_row")"
dvm_envs_joined="$(jq -r '.dodoVendingMachineEnv | join("|")' <<<"$chain_row")"
rpc="$(resolve_env_from_list "$rpc_envs_joined")"
integration="${!integration_env:-}"
dvm="$(resolve_env_from_list "$dvm_envs_joined")"
base_symbol="$(jq -r '.baseToken.symbol' <<<"$chain_row")"
base_address="$(jq -r '.baseToken.address' <<<"$chain_row")"
wrapped_symbol="$(jq -r '.wrappedNativeQuote.symbol' <<<"$chain_row")"
wrapped_envs_joined="$(jq -r '.wrappedNativeQuote.env | join("|")' <<<"$chain_row")"
wrapped_default="$(jq -r '.wrappedNativeQuote.default' <<<"$chain_row")"
wrapped_note="$(jq -r '.wrappedNativeQuote.note' <<<"$chain_row")"
wrapped_address="$(resolve_env_from_list "$wrapped_envs_joined")"
wrapped_address="${wrapped_address:-$wrapped_default}"
stable_symbol="$(jq -r '.stableQuote.symbol' <<<"$chain_row")"
stable_envs_joined="$(jq -r '.stableQuote.env | join("|")' <<<"$chain_row")"
stable_default="$(jq -r '.stableQuote.default' <<<"$chain_row")"
stable_note="$(jq -r '.stableQuote.note' <<<"$chain_row")"
stable_address="$(resolve_env_from_list "$stable_envs_joined")"
stable_address="${stable_address:-$stable_default}"
integration_status="$(code_status "$rpc" "$integration")"
dvm_status="$(code_status "$rpc" "$dvm")"
base_status="$(code_status "$rpc" "$base_address")"
wrapped_status="$(code_status "$rpc" "$wrapped_address")"
stable_status="$(code_status "$rpc" "$stable_address")"
wrapped_pair_row="$(jq -c '.pairs[] | select(.quoteSymbol == $quote)' --arg quote "$wrapped_symbol" <<<"$chain_row")"
stable_pair_row="$(jq -c '.pairs[] | select(.quoteSymbol == $quote)' --arg quote "$stable_symbol" <<<"$chain_row")"
wrapped_pair_name="$(jq -r '.pair' <<<"$wrapped_pair_row")"
stable_pair_name="$(jq -r '.pair' <<<"$stable_pair_row")"
wrapped_pool_expected="$(jq -r '.expectedPoolAddress' <<<"$wrapped_pair_row")"
stable_pool_expected="$(jq -r '.expectedPoolAddress' <<<"$stable_pair_row")"
wrapped_pool_status="$(code_status "$rpc" "$wrapped_pool_expected")"
stable_pool_status="$(code_status "$rpc" "$stable_pool_expected")"
FINDINGS_JOINED=""
[[ -z "$rpc" ]] && append_finding "missing_rpc"
[[ "$dvm_status" != "has_code" ]] && append_finding "missing_or_invalid_dodo_vending_machine"
[[ "$base_status" != "has_code" ]] && append_finding "gas_mirror_base_missing_or_no_code"
[[ "$wrapped_status" != "has_code" ]] && append_finding "wrapped_native_quote_missing_or_no_code"
[[ "$stable_status" != "has_code" ]] && append_finding "stable_quote_missing_or_no_code"
if [[ -n "$integration" && "$integration_status" == "no_code" ]]; then
append_finding "integration_env_set_but_no_code"
fi
if [[ -z "$integration" || "$integration_status" != "has_code" ]]; then
append_finding "integration_needs_deploy"
fi
[[ "$wrapped_pool_status" != "has_code" ]] && append_finding "wrapped_native_pool_not_live"
[[ "$stable_pool_status" != "has_code" ]] && append_finding "stable_quote_pool_not_live"
readiness="blocked"
if [[ -n "$rpc" && "$dvm_status" == "has_code" && "$base_status" == "has_code" && "$wrapped_status" == "has_code" && "$stable_status" == "has_code" ]]; then
readiness="ready_for_execution"
fi
if [[ "$readiness" == "ready_for_execution" ]]; then
ready_count=$((ready_count + 1))
else
blocked_count=$((blocked_count + 1))
fi
chain_count=$((chain_count + 1))
normalized_chain_key="$(normalize_key "$chain_key")"
forge_profile="default"
if [[ "$chain_id" == "25" ]]; then
forge_profile="cronos_legacy"
fi
dvm_primary_env="$(jq -r '.dodoVendingMachineEnv[0]' <<<"$chain_row")"
deploy_cmd=$(cat <<EOF
cd $REPO_ROOT
DODO_VENDING_MACHINE_ADDRESS=\${$dvm_primary_env:-$dvm} \\
OFFICIAL_USDT_ADDRESS=$stable_address \\
OFFICIAL_USDC_ADDRESS=$wrapped_address \\
COMPLIANT_USDT_ADDRESS=$base_address \\
COMPLIANT_USDC_ADDRESS=$base_address \\
FOUNDRY_PROFILE=$forge_profile \\
forge script script/dex/DeployDODOPMMIntegration.s.sol:DeployDODOPMMIntegration \\
--rpc-url $rpc \\
--chain-id $chain_id \\
--broadcast \\
--private-key "\$PRIVATE_KEY" \\
-vvv
jq -r '.transactions[] | select(.transactionType=="CREATE" and .contractName=="DODOPMMIntegration") | .contractAddress' \\
broadcast/DeployDODOPMMIntegration.s.sol/$chain_id/run-latest.json
EOF
)
create_cmd=$(cat <<EOF
cd $REPO_ROOT
INTEGRATION_ADDRESS=\${$integration_env:-REPLACE_WITH_DEPLOYED_INTEGRATION}
LP_FEE_RATE=\${LP_FEE_RATE:-3}
SELF_QUOTE_PRICE_1E18=1000000000000000000
STABLE_PRICE_1E18=\${STABLE_PRICE_1E18:-REPLACE_WITH_LIVE_${normalized_chain_key}_GAS_PRICE_1E18}
K_FACTOR=\${K_FACTOR:-500000000000000000}
ENABLE_TWAP=\${ENABLE_TWAP:-true}
cast send "\$INTEGRATION_ADDRESS" \\
"createPool(address,address,uint256,uint256,uint256,bool)" \\
$base_address \\
$wrapped_address \\
"\$LP_FEE_RATE" \\
"\$SELF_QUOTE_PRICE_1E18" \\
"\$K_FACTOR" \\
"\$ENABLE_TWAP" \\
--rpc-url $rpc \\
--private-key "\$PRIVATE_KEY" \\
--legacy \\
-vv
cast send "\$INTEGRATION_ADDRESS" \\
"createPool(address,address,uint256,uint256,uint256,bool)" \\
$base_address \\
$stable_address \\
"\$LP_FEE_RATE" \\
"\$STABLE_PRICE_1E18" \\
"\$K_FACTOR" \\
"\$ENABLE_TWAP" \\
--rpc-url $rpc \\
--private-key "\$PRIVATE_KEY" \\
--legacy \\
-vv
EOF
)
verify_cmd=$(cat <<EOF
cast code --rpc-url $rpc $wrapped_pool_expected
cast code --rpc-url $rpc $stable_pool_expected
cast call --rpc-url $rpc \${$integration_env:-REPLACE_WITH_DEPLOYED_INTEGRATION} 'pools(address,address)(address)' $base_address $wrapped_address
cast call --rpc-url $rpc \${$integration_env:-REPLACE_WITH_DEPLOYED_INTEGRATION} 'pools(address,address)(address)' $base_address $stable_address
EOF
)
if [[ $first_chain -eq 0 ]]; then
echo "," >>"$json_file"
fi
first_chain=0
cat >>"$json_file" <<EOF
{
"chainId": $chain_id,
"network": $(json_escape "$network"),
"chainKey": $(json_escape "$chain_key"),
"rpc": $(json_escape "$rpc"),
"readiness": $(json_escape "$readiness"),
"blockingFindings": $(emit_findings_json "$FINDINGS_JOINED"),
"integrationEnv": $(json_escape "$integration_env"),
"integrationAddress": $(json_escape "$integration"),
"integrationStatus": $(json_escape "$integration_status"),
"dodoVendingMachine": {
"envCandidates": $(jq '.dodoVendingMachineEnv' <<<"$chain_row"),
"address": $(json_escape "$dvm"),
"status": $(json_escape "$dvm_status")
},
"baseToken": {
"symbol": $(json_escape "$base_symbol"),
"address": $(json_escape "$base_address"),
"status": $(json_escape "$base_status"),
"symbolOnChain": $(json_escape "$(symbol_call "$rpc" "$base_address")")
},
"wrappedNativeQuote": {
"symbol": $(json_escape "$wrapped_symbol"),
"envCandidates": $(jq '.wrappedNativeQuote.env' <<<"$chain_row"),
"address": $(json_escape "$wrapped_address"),
"default": $(json_escape "$wrapped_default"),
"status": $(json_escape "$wrapped_status"),
"symbolOnChain": $(json_escape "$(symbol_call "$rpc" "$wrapped_address")"),
"note": $(json_escape "$wrapped_note")
},
"stableQuote": {
"symbol": $(json_escape "$stable_symbol"),
"envCandidates": $(jq '.stableQuote.env' <<<"$chain_row"),
"address": $(json_escape "$stable_address"),
"default": $(json_escape "$stable_default"),
"status": $(json_escape "$stable_status"),
"symbolOnChain": $(json_escape "$(symbol_call "$rpc" "$stable_address")"),
"note": $(json_escape "$stable_note")
},
"pairs": [
{
"pair": $(json_escape "$wrapped_pair_name"),
"expectedPoolAddress": $(json_escape "$wrapped_pool_expected"),
"poolCodeStatus": $(json_escape "$wrapped_pool_status"),
"executionPriceMode": "self_quote_fixed_1e18"
},
{
"pair": $(json_escape "$stable_pair_name"),
"expectedPoolAddress": $(json_escape "$stable_pool_expected"),
"poolCodeStatus": $(json_escape "$stable_pool_status"),
"executionPriceMode": "live_stable_spot_required"
}
],
"commands": {
"deployIntegration": $(json_escape "$deploy_cmd"),
"createPools": $(json_escape "$create_cmd"),
"verifyPools": $(json_escape "$verify_cmd")
}
}
EOF
{
echo
echo "## $network ($chain_id)"
echo
echo "Readiness: \`$readiness\`"
echo
echo "| Item | Address | Status |"
echo "| --- | --- | --- |"
echo "| DODO vending machine | \`${dvm:-unset}\` | \`$dvm_status\` |"
echo "| Integration env \`$integration_env\` | \`${integration:-unset}\` | \`$integration_status\` |"
echo "| $base_symbol | \`$base_address\` | \`$base_status\` |"
echo "| $wrapped_symbol | \`$wrapped_address\` | \`$wrapped_status\` |"
echo "| $stable_symbol | \`$stable_address\` | \`$stable_status\` |"
echo "| Pool $wrapped_pair_name | \`$wrapped_pool_expected\` | \`$wrapped_pool_status\` |"
echo "| Pool $stable_pair_name | \`$stable_pool_expected\` | \`$stable_pool_status\` |"
echo
echo "Blocking findings:"
if [[ -n "$FINDINGS_JOINED" ]]; then
IFS='|' read -r -a findings_array <<< "$FINDINGS_JOINED"
for finding in "${findings_array[@]}"; do
echo "- \`$finding\`"
done
else
echo "- None"
fi
echo
echo "Quote token notes:"
echo
echo "- $wrapped_symbol: $wrapped_note"
echo "- $stable_symbol: $stable_note"
echo
echo "Deploy integration:"
echo
echo '```bash'
echo "$deploy_cmd"
echo '```'
echo
echo "Create pools:"
echo
echo '```bash'
echo "$create_cmd"
echo '```'
echo
echo "Verify pools:"
echo
echo '```bash'
echo "$verify_cmd"
echo '```'
} >>"$md_file"
done < <(jq -c '.chains[]' "$BUNDLE_JSON")
{
echo
echo " ],"
echo " \"summary\": {"
echo " \"chains\": $chain_count,"
echo " \"pairs\": $((chain_count * 2)),"
echo " \"readyForExecution\": $ready_count,"
echo " \"blocked\": $blocked_count"
echo " }"
echo "}"
} >>"$json_file"
jq . "$json_file" >"$OUT_JSON"
mv "$md_file" "$OUT_MD"
rm -f "$json_file"
echo "Wrote:"
echo " $OUT_JSON"
echo " $OUT_MD"

View File

@@ -0,0 +1,284 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
PROJECT_ROOT="$(cd "$REPO_ROOT/.." && pwd)"
BUNDLE_JSON="$REPO_ROOT/config/optimism-cronos-dodo-4-pools-execution-bundle.json"
OUT_DIR="$PROJECT_ROOT/reports/status"
OUT_JSON="$OUT_DIR/optimism-cronos-dodo-4-pools-execution-bundle-latest.json"
OUT_MD="$OUT_DIR/optimism-cronos-dodo-4-pools-execution-bundle-latest.md"
source "$REPO_ROOT/scripts/lib/deployment/dotenv.sh"
load_deployment_env --repo-root "$REPO_ROOT"
mkdir -p "$OUT_DIR"
json_escape() {
jq -Rn --arg v "${1:-}" '$v'
}
resolve_env_from_list() {
local joined="$1"
local value=""
local key
IFS='|' read -r -a _keys <<< "$joined"
for key in "${_keys[@]}"; do
if [[ -n "${!key:-}" ]]; then
value="${!key}"
break
fi
done
printf '%s' "$value"
}
code_status() {
local rpc="$1"
local address="$2"
if [[ -z "$rpc" || -z "$address" ]]; then
printf 'missing'
return
fi
local code=""
code="$(cast code --rpc-url "$rpc" "$address" 2>/dev/null || true)"
if [[ -n "$code" && "$code" != "0x" ]]; then
printf 'has_code'
else
printf 'no_code'
fi
}
symbol_call() {
local rpc="$1"
local address="$2"
local value
value="$(cast call --rpc-url "$rpc" "$address" "symbol()(string)" 2>/dev/null || true)"
value="${value#\"}"
value="${value%\"}"
printf '%s' "$value"
}
json_file="$(mktemp)"
md_file="$(mktemp)"
generated_at="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
{
echo "{"
echo " \"generatedAt\": $(json_escape "$generated_at"),"
echo " \"bundle\": $(jq '.bundleName' "$BUNDLE_JSON"),"
echo " \"chains\": ["
} >"$json_file"
{
echo "# Optimism/Cronos DODO 4-Pool Execution Bundle"
echo
echo "Generated at: \`$generated_at\`"
echo
echo "This bundle covers the four remaining \`configured_no_code\` DODO PMM pools:"
echo
echo "- Optimism: \`cWUSDC/USDC\`, \`cWUSDT/USDT\`"
echo "- Cronos: \`cWUSDC/USDC\`, \`cWUSDT/USDT\`"
echo
echo "Execution posture:"
echo
echo "- DODO vending-machine addresses are present in env."
echo "- Compliant \`cWUSDT\` / \`cWUSDC\` token contracts have bytecode on both chains."
echo "- The expected pool addresses still have no code on both chains."
echo "- The canonical quote anchors for Optimism/Cronos are the chain-native official USDT/USDC addresses below."
} >"$md_file"
first_chain=1
while IFS= read -r chain_row; do
chain_id="$(jq -r '.chainId' <<<"$chain_row")"
network="$(jq -r '.network' <<<"$chain_row")"
integration_env="$(jq -r '.integrationEnv' <<<"$chain_row")"
dvm_env="$(jq -r '.dodoVendingMachineEnv' <<<"$chain_row")"
rpc_envs_joined="$(jq -r '.rpcEnv | join("|")' <<<"$chain_row")"
rpc="$(resolve_env_from_list "$rpc_envs_joined")"
dvm="${!dvm_env:-}"
integration="${!integration_env:-}"
official_usdc_env="$(jq -r '.official.USDC.env' <<<"$chain_row")"
official_usdc_default="$(jq -r '.official.USDC.default' <<<"$chain_row")"
official_usdt_env="$(jq -r '.official.USDT.env' <<<"$chain_row")"
official_usdt_default="$(jq -r '.official.USDT.default' <<<"$chain_row")"
official_usdc="${!official_usdc_env:-$official_usdc_default}"
official_usdt="${!official_usdt_env:-$official_usdt_default}"
cwusdc_env="$(jq -r '.compliant.cWUSDC.env' <<<"$chain_row")"
cwusdt_env="$(jq -r '.compliant.cWUSDT.env' <<<"$chain_row")"
cwusdc="${!cwusdc_env:-}"
cwusdt="${!cwusdt_env:-}"
integration_status="$(code_status "$rpc" "$integration")"
dvm_status="$(code_status "$rpc" "$dvm")"
official_usdc_status="$(code_status "$rpc" "$official_usdc")"
official_usdt_status="$(code_status "$rpc" "$official_usdt")"
cwusdc_status="$(code_status "$rpc" "$cwusdc")"
cwusdt_status="$(code_status "$rpc" "$cwusdt")"
pool_usdc_expected="$(jq -r '.pairs[] | select(.pair=="cWUSDC/USDC") | .expectedPoolAddress' <<<"$chain_row")"
pool_usdt_expected="$(jq -r '.pairs[] | select(.pair=="cWUSDT/USDT") | .expectedPoolAddress' <<<"$chain_row")"
pool_usdc_status="$(code_status "$rpc" "$pool_usdc_expected")"
pool_usdt_status="$(code_status "$rpc" "$pool_usdt_expected")"
forge_profile="default"
if [[ "$chain_id" == "25" ]]; then
forge_profile="cronos_legacy"
fi
deploy_cmd=$(cat <<EOF
cd $REPO_ROOT
DODO_VENDING_MACHINE_ADDRESS=$dvm \\
OFFICIAL_USDT_ADDRESS=$official_usdt \\
OFFICIAL_USDC_ADDRESS=$official_usdc \\
COMPLIANT_USDT_ADDRESS=$cwusdt \\
COMPLIANT_USDC_ADDRESS=$cwusdc \\
FOUNDRY_PROFILE=$forge_profile \\
forge script script/dex/DeployDODOPMMIntegration.s.sol:DeployDODOPMMIntegration \\
--rpc-url $rpc \\
--chain-id $chain_id \\
--broadcast \\
--private-key "\$PRIVATE_KEY" \\
-vvv
jq -r '.transactions[] | select(.transactionType=="CREATE" and .contractName=="DODOPMMIntegration") | .contractAddress' \\
broadcast/DeployDODOPMMIntegration.s.sol/$chain_id/run-latest.json
EOF
)
create_cmd=$(cat <<EOF
cd $REPO_ROOT
DODO_PMM_INTEGRATION_ADDRESS=\${$integration_env:-REPLACE_WITH_DEPLOYED_INTEGRATION} \\
RPC_URL=$rpc \\
COMPLIANT_USDT_ADDRESS=$cwusdt \\
COMPLIANT_USDC_ADDRESS=$cwusdc \\
OFFICIAL_USDT_ADDRESS=$official_usdt \\
OFFICIAL_USDC_ADDRESS=$official_usdc \\
PRIVATE_KEY="\$PRIVATE_KEY" \\
bash scripts/setup-dodo-pools.sh
EOF
)
verify_cmd=$(cat <<EOF
cast code --rpc-url $rpc $pool_usdc_expected
cast code --rpc-url $rpc $pool_usdt_expected
cast call --rpc-url $rpc \${$integration_env:-REPLACE_WITH_DEPLOYED_INTEGRATION} 'pools(address,address)(address)' $cwusdc $official_usdc
cast call --rpc-url $rpc \${$integration_env:-REPLACE_WITH_DEPLOYED_INTEGRATION} 'pools(address,address)(address)' $cwusdt $official_usdt
EOF
)
if [[ $first_chain -eq 0 ]]; then
echo "," >>"$json_file"
fi
first_chain=0
cat >>"$json_file" <<EOF
{
"chainId": $chain_id,
"network": $(json_escape "$network"),
"rpc": $(json_escape "$rpc"),
"integrationEnv": $(json_escape "$integration_env"),
"integrationAddress": $(json_escape "$integration"),
"integrationStatus": $(json_escape "$integration_status"),
"dodoVendingMachine": {
"env": $(json_escape "$dvm_env"),
"address": $(json_escape "$dvm"),
"status": $(json_escape "$dvm_status")
},
"officialTokens": {
"USDC": {
"env": $(json_escape "$official_usdc_env"),
"address": $(json_escape "$official_usdc"),
"status": $(json_escape "$official_usdc_status"),
"symbol": $(json_escape "$(symbol_call "$rpc" "$official_usdc")")
},
"USDT": {
"env": $(json_escape "$official_usdt_env"),
"address": $(json_escape "$official_usdt"),
"status": $(json_escape "$official_usdt_status"),
"symbol": $(json_escape "$(symbol_call "$rpc" "$official_usdt")")
}
},
"compliantTokens": {
"cWUSDC": {
"env": $(json_escape "$cwusdc_env"),
"address": $(json_escape "$cwusdc"),
"status": $(json_escape "$cwusdc_status"),
"symbol": $(json_escape "$(symbol_call "$rpc" "$cwusdc")")
},
"cWUSDT": {
"env": $(json_escape "$cwusdt_env"),
"address": $(json_escape "$cwusdt"),
"status": $(json_escape "$cwusdt_status"),
"symbol": $(json_escape "$(symbol_call "$rpc" "$cwusdt")")
}
},
"pairs": [
{
"pair": "cWUSDC/USDC",
"expectedPoolAddress": $(json_escape "$pool_usdc_expected"),
"poolCodeStatus": $(json_escape "$pool_usdc_status")
},
{
"pair": "cWUSDT/USDT",
"expectedPoolAddress": $(json_escape "$pool_usdt_expected"),
"poolCodeStatus": $(json_escape "$pool_usdt_status")
}
],
"commands": {
"deployIntegration": $(json_escape "$deploy_cmd"),
"createPools": $(json_escape "$create_cmd"),
"verifyPools": $(json_escape "$verify_cmd")
}
}
EOF
{
echo
echo "## $network ($chain_id)"
echo
echo "| Item | Address | Status |"
echo "| --- | --- | --- |"
echo "| DODO vending machine | \`$dvm\` | \`$dvm_status\` |"
echo "| Integration env \`$integration_env\` | \`${integration:-unset}\` | \`$integration_status\` |"
echo "| Official USDC | \`$official_usdc\` | \`$official_usdc_status\` |"
echo "| Official USDT | \`$official_usdt\` | \`$official_usdt_status\` |"
echo "| cWUSDC | \`$cwusdc\` | \`$cwusdc_status\` |"
echo "| cWUSDT | \`$cwusdt\` | \`$cwusdt_status\` |"
echo "| Pool cWUSDC/USDC | \`$pool_usdc_expected\` | \`$pool_usdc_status\` |"
echo "| Pool cWUSDT/USDT | \`$pool_usdt_expected\` | \`$pool_usdt_status\` |"
echo
echo "Deploy integration:"
echo
echo '```bash'
echo "$deploy_cmd"
echo '```'
echo
echo "Create pools:"
echo
echo '```bash'
echo "$create_cmd"
echo '```'
echo
echo "Verify pools:"
echo
echo '```bash'
echo "$verify_cmd"
echo '```'
} >>"$md_file"
done < <(jq -c '.chains[]' "$BUNDLE_JSON")
{
echo
echo " ]"
echo "}"
} >>"$json_file"
jq . "$json_file" >"$OUT_JSON"
mv "$md_file" "$OUT_MD"
rm -f "$json_file"
echo "Wrote:"
echo " $OUT_JSON"
echo " $OUT_MD"

View File

@@ -0,0 +1,232 @@
#!/usr/bin/env bash
# Chain 138 → Ethereum Mainnet only: bridge PCT of each canonical c* balance via CWMultiTokenBridgeL1.
# Default PCT: 17.25% (= 1725 basis points of 10000; override with PCT_BP).
#
# Modes:
# --plan-only Write JSON + summary (default)
# --check-routes destinations(token, Mainnet selector) for each token
# --emit-cmds Print approve + lockAndSend cast lines (review before running)
#
# Env: PRIVATE_KEY (for --emit-cmds deployer address), RPC_URL_138, CW_L1_BRIDGE_CHAIN138,
# LINK_TOKEN_CHAIN138, RECIPIENT_ADDRESS (default: deployer)
# PCT_BP (default 1725 = 17.25%)
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 -22
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
PCT_BP="${PCT_BP:-1725}"
RPC="${RPC_URL_138:-${CHAIN138_RPC:-${RPC_URL:-http://192.168.11.211:8545}}}"
OUT_JSON="${OUT_JSON:-$SMOM_ROOT/reports/status/c138-bridge-mainnet-pct-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}"
MAINNET_SEL=5009297550715157269
export RPC OUT_JSON DEPLOYER RECIPIENT BRIDGE PCT_BP MAINNET_SEL
# Canonical c* only (EXPLORER_TOKEN_LIST_CROSSCHECK §5)
read -r -d '' TOKEN_ROWS << 'EOF' || true
cUSDT:0x93E66202A11B1772E55407B32B44e5Cd8eda7f22
cUSDC:0xf22258f57794CC8E06237084b353Ab30fFfa640b
cEURC:0x8085961F9cF02b4d800A3c6d386D31da4B34266a
cEURT:0xdf4b71c61E5912712C1Bdd451416B9aC26949d72
cGBPC:0x003960f16D9d34F2e98d62723B6721Fb92074aD2
cGBPT:0x350f54e4D23795f86A9c03988c7135357CCaD97c
cAUDC:0xD51482e567c03899eecE3CAe8a058161FD56069D
cJPYC:0xEe269e1226a334182aace90056EE4ee5Cc8A6770
cCHFC:0x873990849DDa5117d7C644f0aF24370797C03885
cCADC:0x54dBd40cF05e15906A2C21f600937e96787f5679
cXAUC:0x290E52a8819A4fbD0714E517225429aA2B70EC6b
cXAUT:0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E
EOF
export TOKEN_ROWS
mkdir -p "$(dirname "$OUT_JSON")"
python3 << PY
import json, subprocess, os, re
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", "")
pct_bp = int(os.environ.get("PCT_BP", "1725"))
sel = int(os.environ.get("MAINNET_SEL", "5009297550715157269"))
rows = []
for line in os.environ.get("TOKEN_ROWS", "").strip().split("\n"):
if not line.strip():
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-mainnet-pct/v1",
"rpc_url": rpc,
"deployer": deployer,
"recipient": recipient,
"cw_l1_bridge": bridge,
"destination": "Ethereum Mainnet",
"chain_selector": str(sel),
"pct_basis_points": pct_bp,
"pct_human": f"{pct_bp / 100:.2f}%",
"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
amt = bal * pct_bp // 10000
plan["tokens"].append({
"symbol": sym,
"address": addr,
"balance_wei": str(bal),
"amount_to_bridge_wei": str(amt),
})
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
python3 - <<'PY'
import json, os
with open(os.environ["OUT_JSON"]) as f:
p = json.load(f)
print("\n=== c* → Mainnet only:", p.get("pct_human"), "of balance (integer base units) ===\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"]
amt = int(t["amount_to_bridge_wei"])
if sym.startswith("cXAU"):
print(f"{sym:<10} bridge: {amt / 1e6:,.6f} troy oz (wei={t['amount_to_bridge_wei']})")
else:
print(f"{sym:<10} bridge: {amt / 1e6:,.6f} tokens (wei={t['amount_to_bridge_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 "=== Mainnet route: $BRIDGE ==="
while IFS= read -r line; do
[[ -z "$line" ]] && continue
sym="${line%%:*}"
addr="${line#*:}"
dest=$(cast call "$BRIDGE" "destinations(address,uint64)(address,bool)" "$addr" "$MAINNET_SEL" --rpc-url "$RPC" 2>/dev/null || echo "ERR")
echo "$sym ($addr): destinations(Mainnet)=$dest"
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
OUT_CAST="${OUT_CAST:-$SMOM_ROOT/reports/status/c138-bridge-mainnet-pct-cast-commands.sh}"
export OUT_CAST
{
echo "#!/usr/bin/env bash"
echo "# Generated: c138-cw-bridge-mainnet-pct.sh --emit-cmds"
echo "# Review fee + reserve verifier. Fund LINK on deployer for CCIP fees."
echo "set -euo pipefail"
python3 <<'PY'
import json, os, subprocess
rpc = os.environ["RPC"]
bridge = os.environ["BRIDGE"]
recipient = os.environ["RECIPIENT"]
link = os.environ["LINK_TOKEN"]
sel = int(os.environ["MAINNET_SEL"])
with open(os.environ["OUT_JSON"]) as f:
plan = json.load(f)
for t in plan["tokens"]:
if "error" in t:
continue
amt = t.get("amount_to_bridge_wei", "0")
if int(amt) == 0:
continue
sym, token = t["symbol"], t["address"]
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:
print(f"# SKIP {sym}: destination Mainnet not enabled or query failed")
print(f"# cast output: {chk.stdout.strip()}")
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} -> Mainnet 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: $OUT_CAST"
wc -l "$OUT_CAST"
exit 0
fi

View File

@@ -0,0 +1,231 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [[ -f "$REPO_ROOT/scripts/load-env.sh" ]]; then
# shellcheck disable=SC1090
source "$REPO_ROOT/scripts/load-env.sh" >/dev/null
elif [[ -f "$REPO_ROOT/../scripts/lib/load-project-env.sh" ]]; then
# shellcheck disable=SC1090
source "$REPO_ROOT/../scripts/lib/load-project-env.sh" >/dev/null
fi
APPLY=false
CHAIN_FILTER=""
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=true ;;
--chain=*) CHAIN_FILTER="${1#*=}" ;;
--chain)
CHAIN_FILTER="${2:-}"
shift
;;
*)
echo "Unknown arg: $1" >&2
echo "Usage: $0 [--apply] [--chain <chain_id>]" >&2
exit 1
;;
esac
shift
done
declare -A CHAIN_NAME=(
[1]="Ethereum Mainnet"
[10]="Optimism"
[25]="Cronos"
[56]="BSC"
[100]="Gnosis"
[137]="Polygon"
[8453]="Base"
[42161]="Arbitrum"
[42220]="Celo"
[43114]="Avalanche"
)
declare -A CHAIN_SELECTOR=(
[1]="5009297550715157269"
[10]="3734403246176062136"
[25]="1456215246176062136"
[56]="11344663589394136015"
[100]="465200170687744372"
[137]="4051577828743386545"
[8453]="15971525489660198786"
[42161]="4949039107694359620"
[42220]="1346049177634351622"
[43114]="6433500567565415381"
)
rpc_for_chain() {
case "$1" in
1) echo "${MAINNET_RPC_URL:-${ETHEREUM_MAINNET_RPC:-${ETHEREUM_RPC_URL:-}}}" ;;
10) echo "${OPTIMISM_RPC_URL:-${OPTIMISM_MAINNET_RPC:-}}" ;;
25) echo "${CRONOS_RPC_URL:-}" ;;
56) echo "${BSC_RPC_URL:-${BSC_MAINNET_RPC:-}}" ;;
100) echo "${GNOSIS_RPC_URL:-${GNOSIS_MAINNET_RPC:-${GNOSIS_RPC:-}}}" ;;
137) echo "${POLYGON_RPC_URL:-${POLYGON_MAINNET_RPC:-}}" ;;
8453) echo "${BASE_RPC_URL:-${BASE_MAINNET_RPC:-}}" ;;
42161) echo "${ARBITRUM_RPC_URL:-${ARBITRUM_MAINNET_RPC:-}}" ;;
42220) echo "${CELO_RPC_URL:-}" ;;
43114) echo "${AVALANCHE_RPC_URL:-${AVALANCHE_MAINNET_RPC:-}}" ;;
*) echo "" ;;
esac
}
bridge_for_chain() {
case "$1" in
1) echo "${CW_BRIDGE_MAINNET:-}" ;;
10) echo "${CW_BRIDGE_OPTIMISM:-}" ;;
25) echo "${CW_BRIDGE_CRONOS:-}" ;;
56) echo "${CW_BRIDGE_BSC:-}" ;;
100) echo "${CW_BRIDGE_GNOSIS:-}" ;;
137) echo "${CW_BRIDGE_POLYGON:-}" ;;
8453) echo "${CW_BRIDGE_BASE:-}" ;;
42161) echo "${CW_BRIDGE_ARBITRUM:-}" ;;
42220) echo "${CW_BRIDGE_CELO:-}" ;;
43114) echo "${CW_BRIDGE_AVALANCHE:-}" ;;
*) echo "" ;;
esac
}
probe_generation() {
local bridge="$1" rpc="$2"
if timeout 10 cast call "$bridge" 'sendRouter()(address)' --rpc-url "$rpc" >/dev/null 2>&1; then
echo "new"
return
fi
if timeout 10 cast call "$bridge" 'ccipRouter()(address)' --rpc-url "$rpc" >/dev/null 2>&1; then
echo "old"
return
fi
echo "unknown"
}
old_has_key() {
local bridge="$1" rpc="$2" key="$3"
local destinations
destinations="$(timeout 12 cast call "$bridge" 'getDestinationChains()(uint64[])' --rpc-url "$rpc" 2>/dev/null || true)"
[[ -z "$destinations" ]] && return 1
DESTINATIONS_PAYLOAD="$destinations" python3 - "$key" <<'PY'
import re
import os
import sys
target = sys.argv[1]
payload = os.environ.get("DESTINATIONS_PAYLOAD", "")
numbers = re.findall(r'\d+', payload)
sys.exit(0 if target in numbers else 1)
PY
}
new_read_destination() {
local bridge="$1" rpc="$2" key="$3"
timeout 12 cast call "$bridge" 'destinations(uint64)((address,bool))' "$key" --rpc-url "$rpc" 2>/dev/null || true
}
normalize_addr() {
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
}
new_matches() {
local raw="$1" expected="$2"
local lower_raw lower_expected
lower_raw="$(normalize_addr "$raw")"
lower_expected="$(normalize_addr "$expected")"
[[ "$lower_raw" == *"$lower_expected"* && "$lower_raw" == *"true"* ]]
}
send_tx() {
local rpc="$1" bridge="$2" signature="$3"
shift 3
cast send "$bridge" "$signature" "$@" --rpc-url "$rpc" --private-key "$PRIVATE_KEY"
}
configure_old() {
local source_chain="$1" target_key="$2" target_bridge="$3"
local rpc bridge op
rpc="$(rpc_for_chain "$source_chain")"
bridge="$(bridge_for_chain "$source_chain")"
if old_has_key "$bridge" "$rpc" "$target_key"; then
op="updateDestination(uint64,address)"
else
op="addDestination(uint64,address)"
fi
if [[ "$APPLY" == true ]]; then
send_tx "$rpc" "$bridge" "$op" "$target_key" "$target_bridge"
else
echo "DRY-RUN old chain=$source_chain key=$target_key bridge=$target_bridge op=$op"
fi
}
configure_new() {
local source_chain="$1" target_key="$2" target_bridge="$3"
local rpc bridge raw
rpc="$(rpc_for_chain "$source_chain")"
bridge="$(bridge_for_chain "$source_chain")"
raw="$(new_read_destination "$bridge" "$rpc" "$target_key")"
if new_matches "$raw" "$target_bridge"; then
echo "SKIP new chain=$source_chain key=$target_key already=$raw"
return
fi
if [[ "$APPLY" == true ]]; then
send_tx "$rpc" "$bridge" 'configureDestination(uint64,address,bool)' "$target_key" "$target_bridge" true
else
echo "DRY-RUN new chain=$source_chain key=$target_key bridge=$target_bridge"
fi
}
configure_key() {
local source_chain="$1" target_key="$2" target_bridge="$3" generation="$4"
case "$generation" in
old) configure_old "$source_chain" "$target_key" "$target_bridge" ;;
new) configure_new "$source_chain" "$target_key" "$target_bridge" ;;
*)
echo "ERROR unknown generation for chain $source_chain" >&2
return 1
;;
esac
}
echo "Public cW bridge mesh configuration"
echo "Apply mode: $APPLY"
echo
declare -A GENERATION=()
for chain_id in 1 10 25 56 100 137 8453 42161 42220 43114; do
[[ -n "$CHAIN_FILTER" && "$CHAIN_FILTER" != "$chain_id" ]] && continue
rpc="$(rpc_for_chain "$chain_id")"
bridge="$(bridge_for_chain "$chain_id")"
if [[ -z "$rpc" || -z "$bridge" ]]; then
echo "WARN chain=$chain_id missing rpc or bridge env; skipping"
continue
fi
generation="$(probe_generation "$bridge" "$rpc")"
GENERATION["$chain_id"]="$generation"
echo "chain=$chain_id name='${CHAIN_NAME[$chain_id]}' generation=$generation bridge=$bridge"
done
echo
for source_chain in 1 10 25 56 100 137 8453 42161 42220 43114; do
[[ -n "$CHAIN_FILTER" && "$CHAIN_FILTER" != "$source_chain" ]] && continue
[[ -z "${GENERATION[$source_chain]:-}" ]] && continue
for target_chain in 1 10 25 56 100 137 8453 42161 42220 43114; do
[[ "$source_chain" == "$target_chain" ]] && continue
target_bridge="$(bridge_for_chain "$target_chain")"
[[ -z "$target_bridge" ]] && continue
# Outbound path on source uses the target CCIP selector.
configure_key "$source_chain" "${CHAIN_SELECTOR[$target_chain]}" "$target_bridge" "${GENERATION[$source_chain]}"
# Inbound receive path on source uses the remote public chain id.
configure_key "$source_chain" "$target_chain" "$target_bridge" "${GENERATION[$source_chain]}"
done
done
echo
echo "Done."

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1090
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
load_deployment_env --repo-root "$REPO_ROOT"
fi
FACTORY="${FACTORY:-${UNISWAP_V3_FACTORY:-}}"
RPC_URL="${RPC_URL:-}"
TOKEN_A="${TOKEN_A:-}"
TOKEN_B="${TOKEN_B:-}"
FEE="${FEE:-500}"
EXECUTE="${EXECUTE:-0}"
SQRT_PRICE_X96="${SQRT_PRICE_X96:-79228162514264337593543950336}"
PRIVATE_KEY="${PRIVATE_KEY:-}"
if [[ -z "$FACTORY" || -z "$RPC_URL" || -z "$TOKEN_A" || -z "$TOKEN_B" ]]; then
echo "Required: FACTORY RPC_URL TOKEN_A TOKEN_B" >&2
exit 1
fi
get_pool() {
cast call "$FACTORY" \
"getPool(address,address,uint24)(address)" \
"$TOKEN_A" "$TOKEN_B" "$FEE" \
--rpc-url "$RPC_URL" 2>/dev/null || true
}
pool="$(get_pool)"
pool="${pool//$'\n'/}"
if [[ -z "$pool" || "$pool" == "0x0000000000000000000000000000000000000000" ]]; then
echo "Pool does not exist yet for $TOKEN_A / $TOKEN_B fee $FEE"
if [[ "$EXECUTE" != "1" ]]; then
echo "Dry run: set EXECUTE=1 to call createPool on $FACTORY"
exit 0
fi
if [[ -z "$PRIVATE_KEY" ]]; then
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
exit 1
fi
cast send "$FACTORY" \
"createPool(address,address,uint24)" \
"$TOKEN_A" "$TOKEN_B" "$FEE" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
-vv
pool="$(get_pool)"
pool="${pool//$'\n'/}"
fi
echo "Pool address: $pool"
slot0="$(cast call "$pool" "slot0()((uint160,int24,uint16,uint16,uint16,uint8,bool))" --rpc-url "$RPC_URL" 2>/dev/null || true)"
slot0_no_ws="$(printf '%s' "$slot0" | tr -d '[:space:]')"
if [[ "$slot0_no_ws" == "(0,0,0,0,0,0,false)" || -z "$slot0_no_ws" ]]; then
echo "Pool is not initialized"
if [[ "$EXECUTE" != "1" ]]; then
echo "Dry run: set EXECUTE=1 to initialize at sqrtPriceX96=$SQRT_PRICE_X96"
exit 0
fi
if [[ -z "$PRIVATE_KEY" ]]; then
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
exit 1
fi
cast send "$pool" \
"initialize(uint160)" \
"$SQRT_PRICE_X96" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
-vv
echo "Initialized pool at sqrtPriceX96=$SQRT_PRICE_X96"
else
echo "Pool already initialized: $slot0"
fi

View File

@@ -0,0 +1,299 @@
#!/usr/bin/env bash
# Reconcile cW* token roles so only the configured per-network bridge keeps MINTER/BURNER.
#
# The script is intentionally conservative:
# - It grants MINTER/BURNER to the configured bridge if missing
# - It revokes MINTER/BURNER from the deployer/admin by default
# - It can revoke MINTER/BURNER from an explicit denylist of extra addresses
# - It can optionally freeze future operational role changes on newer cW* contracts
#
# Usage:
# bash scripts/deployment/cw-enforce-bridge-only-roles.sh --dry-run
# bash scripts/deployment/cw-enforce-bridge-only-roles.sh
#
# Optional env:
# CW_ROLE_CHAINS="1 10 25 56 100 137 42161 42220 43114 8453"
# CW_ROLE_TOKENS="CWUSDT CWUSDC CWEURC"
# CW_ROLE_EXTRA_REVOKE="0xabc...,0xdef..."
# CW_ROLE_KEEP_ALLOWLIST="0xabc...,0xdef..."
# CW_ROLE_REVOKE_DEPLOYER=1 # default 1
# CW_ROLE_FREEZE_IF_SUPPORTED=1 # default 1
# CW_ROLE_FROM_BLOCK=earliest # optional log scan start block for role event discovery
# PRIVATE_KEY # required unless --dry-run
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
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=1
fi
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
if ! command -v cast >/dev/null 2>&1; then
echo "cast is required" >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "jq is required" >&2
exit 1
fi
CHAIN_ROWS=(
"1|MAINNET|ETHEREUM_MAINNET_RPC|CW_BRIDGE_MAINNET"
"10|OPTIMISM|OPTIMISM_MAINNET_RPC|CW_BRIDGE_OPTIMISM"
"25|CRONOS|CRONOS_MAINNET_RPC|CW_BRIDGE_CRONOS"
"56|BSC|BSC_MAINNET_RPC|CW_BRIDGE_BSC"
"100|GNOSIS|GNOSIS_MAINNET_RPC|CW_BRIDGE_GNOSIS"
"137|POLYGON|POLYGON_MAINNET_RPC|CW_BRIDGE_POLYGON"
"42161|ARBITRUM|ARBITRUM_MAINNET_RPC|CW_BRIDGE_ARBITRUM"
"42220|CELO|CELO_MAINNET_RPC|CW_BRIDGE_CELO"
"43114|AVALANCHE|AVALANCHE_MAINNET_RPC|CW_BRIDGE_AVALANCHE"
"8453|BASE|BASE_MAINNET_RPC|CW_BRIDGE_BASE"
)
TOKENS=(
"CWUSDT"
"CWUSDC"
"CWAUSDT"
"CWUSDW"
"CWEURC"
"CWEURT"
"CWGBPC"
"CWGBPT"
"CWAUDC"
"CWJPYC"
"CWCHFC"
"CWCADC"
"CWXAUC"
"CWXAUT"
)
CHAIN_FILTER="${CW_ROLE_CHAINS:-1 10 25 56 100 137 42161 42220 43114 8453}"
TOKEN_FILTER="${CW_ROLE_TOKENS:-}"
REVOKE_DEPLOYER="${CW_ROLE_REVOKE_DEPLOYER:-1}"
FREEZE_IF_SUPPORTED="${CW_ROLE_FREEZE_IF_SUPPORTED:-1}"
EXTRA_REVOKE_RAW="${CW_ROLE_EXTRA_REVOKE:-}"
KEEP_ALLOWLIST_RAW="${CW_ROLE_KEEP_ALLOWLIST:-}"
ROLE_FROM_BLOCK="${CW_ROLE_FROM_BLOCK:-earliest}"
MINTER_ROLE="$(cast keccak "MINTER_ROLE")"
BURNER_ROLE="$(cast keccak "BURNER_ROLE")"
DEFAULT_ADMIN_ROLE="0x0000000000000000000000000000000000000000000000000000000000000000"
send_cmd() {
if [[ "$DRY_RUN" -eq 1 ]]; then
local rendered=()
local redact_next=0
local arg
for arg in "$@"; do
if [[ "$redact_next" -eq 1 ]]; then
rendered+=("<redacted>")
redact_next=0
continue
fi
if [[ "$arg" == "--private-key" ]]; then
rendered+=("$arg")
redact_next=1
continue
fi
rendered+=("$arg")
done
echo "[dry-run] ${rendered[*]}"
return 0
fi
"$@"
}
bool_call() {
local rpc="$1"
local contract="$2"
local sig="$3"
shift 3
cast call "$contract" "$sig" "$@" --rpc-url "$rpc" 2>/dev/null
}
normalize_address() {
local value="${1,,}"
value="${value#0x}"
if [[ ${#value} -lt 40 ]]; then
return 1
fi
echo "0x${value: -40}"
}
append_unique_address() {
local __var_name="$1"
local candidate="${2,,}"
[[ -n "$candidate" ]] || return 0
local current="${!__var_name:-}"
if [[ " $current " != *" $candidate "* ]]; then
printf -v "$__var_name" '%s%s ' "$current" "$candidate"
fi
}
load_csv_addresses() {
local raw="$1"
local __out_var="$2"
local item normalized
IFS=',' read -r -a _addr_items <<< "$raw"
for item in "${_addr_items[@]}"; do
item="${item//[[:space:]]/}"
[[ -n "$item" ]] || continue
normalized="$(normalize_address "$item" || true)"
[[ -n "$normalized" ]] || continue
append_unique_address "$__out_var" "$normalized"
done
}
discover_role_holders() {
local rpc="$1"
local token="$2"
local role="$3"
cast logs "RoleGranted(bytes32,address,address)" "$role" \
--from-block "$ROLE_FROM_BLOCK" --to-block latest --address "$token" --rpc-url "$rpc" --json 2>/dev/null |
jq -r '.[].topics[2] // empty' |
while read -r topic; do
normalize_address "$topic" || true
done
}
function_exists() {
local rpc="$1"
local contract="$2"
local sig="$3"
cast call "$contract" "$sig" --rpc-url "$rpc" >/dev/null 2>&1
}
grant_role_if_supported() {
local rpc="$1"
local token="$2"
local role="$3"
local target="$4"
local current
current="$(bool_call "$rpc" "$token" "hasRole(bytes32,address)(bool)" "$role" "$target" || true)"
if [[ "$current" != "true" ]]; then
send_cmd cast send "$token" "grantRole(bytes32,address)" "$role" "$target" \
--rpc-url "$rpc" --private-key "$PRIVATE_KEY"
fi
}
revoke_role_if_present() {
local rpc="$1"
local token="$2"
local role="$3"
local target="$4"
local current
current="$(bool_call "$rpc" "$token" "hasRole(bytes32,address)(bool)" "$role" "$target" || true)"
if [[ "$current" == "true" ]]; then
send_cmd cast send "$token" "revokeRole(bytes32,address)" "$role" "$target" \
--rpc-url "$rpc" --private-key "$PRIVATE_KEY"
fi
}
role_label() {
case "$1" in
"$DEFAULT_ADMIN_ROLE") echo "DEFAULT_ADMIN_ROLE" ;;
"$MINTER_ROLE") echo "MINTER_ROLE" ;;
"$BURNER_ROLE") echo "BURNER_ROLE" ;;
*) echo "$1" ;;
esac
}
for row in "${CHAIN_ROWS[@]}"; do
IFS='|' read -r chain_id chain_key rpc_var bridge_var <<< "$row"
if [[ " $CHAIN_FILTER " != *" $chain_id "* ]]; then
continue
fi
rpc="${!rpc_var:-}"
bridge="${!bridge_var:-}"
deployer="${DEPLOYER_ADDRESS:-${DEPLOYER_WALLET:-${DEPLOYER_EOA:-${DEFAULT_FROM_ADDRESS:-}}}}"
if [[ -z "$deployer" && -n "${PRIVATE_KEY:-}" ]]; then
deployer="$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || true)"
fi
bridge="$(normalize_address "$bridge" || true)"
deployer="$(normalize_address "$deployer" || true)"
if [[ -z "$rpc" || -z "$bridge" ]]; then
echo "Skip chain $chain_id ($chain_key): missing rpc or bridge env"
continue
fi
echo "=== Chain $chain_id ($chain_key) ==="
echo "Bridge: $bridge"
for token_prefix in "${TOKENS[@]}"; do
if [[ -n "$TOKEN_FILTER" && " $TOKEN_FILTER " != *" $token_prefix "* ]]; then
continue
fi
token_var="${token_prefix}_${chain_key}"
token="${!token_var:-}"
if [[ -z "$token" || "$token" == "0x0000000000000000000000000000000000000000" ]]; then
continue
fi
echo "-- $token_var $token"
grant_role_if_supported "$rpc" "$token" "$MINTER_ROLE" "$bridge"
grant_role_if_supported "$rpc" "$token" "$BURNER_ROLE" "$bridge"
keep_allowlist=""
append_unique_address keep_allowlist "$bridge"
load_csv_addresses "$KEEP_ALLOWLIST_RAW" keep_allowlist
revoke_candidates=""
while read -r discovered; do
[[ -n "$discovered" ]] || continue
append_unique_address revoke_candidates "$discovered"
done < <(discover_role_holders "$rpc" "$token" "$MINTER_ROLE")
while read -r discovered; do
[[ -n "$discovered" ]] || continue
append_unique_address revoke_candidates "$discovered"
done < <(discover_role_holders "$rpc" "$token" "$BURNER_ROLE")
if [[ "$REVOKE_DEPLOYER" == "1" && -n "$deployer" ]]; then
append_unique_address revoke_candidates "$deployer"
fi
load_csv_addresses "$EXTRA_REVOKE_RAW" revoke_candidates
for addr in $revoke_candidates; do
[[ " $keep_allowlist " == *" ${addr,,} "* ]] && continue
revoke_role_if_present "$rpc" "$token" "$MINTER_ROLE" "$addr"
revoke_role_if_present "$rpc" "$token" "$BURNER_ROLE" "$addr"
done
if [[ "$FREEZE_IF_SUPPORTED" == "1" ]]; then
if function_exists "$rpc" "$token" "operationalRolesFrozen()(bool)"; then
frozen="$(cast call "$token" "operationalRolesFrozen()(bool)" --rpc-url "$rpc" 2>/dev/null || true)"
if [[ "$frozen" != "true" ]]; then
send_cmd cast send "$token" "freezeOperationalRoles()" \
--rpc-url "$rpc" --private-key "$PRIVATE_KEY" --gas-limit 80000
fi
fi
fi
for role in "$DEFAULT_ADMIN_ROLE" "$MINTER_ROLE" "$BURNER_ROLE"; do
summary_holders=""
while read -r discovered; do
[[ -n "$discovered" ]] || continue
current="$(bool_call "$rpc" "$token" "hasRole(bytes32,address)(bool)" "$role" "$discovered" || true)"
if [[ "$current" == "true" ]]; then
append_unique_address summary_holders "$discovered"
fi
done < <(discover_role_holders "$rpc" "$token" "$role")
echo " $(role_label "$role"): ${summary_holders:-<none discovered>}"
done
done
done

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1090
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
load_deployment_env --repo-root "$REPO_ROOT"
fi
RPC_URL="${RPC_URL_138:-${CHAIN138_RPC:-http://192.168.11.211:8545}}"
PRIVATE_KEY="${PRIVATE_KEY:-}"
GAS_PRICE="${GAS_PRICE:-1000000000}"
EXECUTE="${EXECUTE:-0}"
ONLY_FAMILY="${GAS_FAMILY:-${1:-}}"
if [[ -z "$PRIVATE_KEY" ]]; then
echo "PRIVATE_KEY is required" >&2
exit 1
fi
cmd=(
bash scripts/forge/scope.sh script tokens
script/deploy/DeployGasCanonicalTokens.s.sol:DeployGasCanonicalTokens
--rpc-url "$RPC_URL"
--broadcast
--private-key "$PRIVATE_KEY"
--legacy
--with-gas-price "$GAS_PRICE"
-vvv
)
export FOUNDRY_PROFILE="${FOUNDRY_PROFILE:-chain138_legacy}"
if [[ -n "$ONLY_FAMILY" ]]; then
export GAS_FAMILY="$ONLY_FAMILY"
fi
echo "Chain 138 gas-canonical deploy"
echo " profile: $FOUNDRY_PROFILE"
echo " rpc: $RPC_URL"
echo " gas price: $GAS_PRICE"
echo " family: ${GAS_FAMILY:-all}"
if [[ "$EXECUTE" != "1" ]]; then
echo "Dry run only. Re-run with EXECUTE=1 to broadcast."
printf ' %q' "${cmd[@]}"
echo
exit 0
fi
cd "$REPO_ROOT"
"${cmd[@]}"

View File

@@ -0,0 +1,297 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$SMOM_ROOT"
if [[ -f "$SMOM_ROOT/scripts/lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1091
source "$SMOM_ROOT/scripts/lib/deployment/dotenv.sh"
load_deployment_env --repo-root "$SMOM_ROOT"
else
source "$SMOM_ROOT/scripts/load-env.sh" >/dev/null 2>&1 || true
fi
PLAN_JSON="${PLAN_JSON:-$SMOM_ROOT/config/chain138-eth-pmm-liquidity-plan.json}"
PROFILE="${PROFILE:-}"
EXECUTE="${EXECUTE:-0}"
RPC_URL_138="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
DODO_PMM_INTEGRATION_ADDRESS="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-}}"
PRIVATE_KEY="${PRIVATE_KEY:-}"
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
command -v cast >/dev/null 2>&1 || { echo "cast is required" >&2; exit 1; }
[[ -f "$PLAN_JSON" ]] || { echo "PLAN_JSON not found: $PLAN_JSON" >&2; exit 1; }
[[ -n "$DODO_PMM_INTEGRATION_ADDRESS" ]] || { echo "DODO_PMM_INTEGRATION_ADDRESS not set" >&2; exit 1; }
require_private_key_env || exit 1
EXECUTION_CONFIG="$(jq -r '.executionConfig' "$PLAN_JSON")"
if [[ "$EXECUTION_CONFIG" != /* ]]; then
EXECUTION_CONFIG="$SMOM_ROOT/${EXECUTION_CONFIG#smom-dbis-138/}"
fi
[[ -f "$EXECUTION_CONFIG" ]] || { echo "Execution config not found: $EXECUTION_CONFIG" >&2; exit 1; }
if [[ -z "$PROFILE" ]]; then
PROFILE="$(jq -r '.defaultProfile' "$PLAN_JSON")"
fi
FIAT_BASE_UNITS="$(jq -r --arg p "$PROFILE" '.profiles[$p].fiatBaseUnits' "$PLAN_JSON")"
XAU_BASE_UNITS="$(jq -r --arg p "$PROFILE" '.profiles[$p].xauBaseUnits' "$PLAN_JSON")"
[[ "$FIAT_BASE_UNITS" != "null" ]] || { echo "Unknown PROFILE: $PROFILE" >&2; exit 1; }
[[ "$XAU_BASE_UNITS" != "null" ]] || { echo "Unknown PROFILE: $PROFILE" >&2; exit 1; }
APPROVE_GAS_LIMIT="$(jq -r '.execution.approveGasLimit' "$PLAN_JSON")"
MINT_GAS_LIMIT="$(jq -r '.execution.mintGasLimit' "$PLAN_JSON")"
WRAP_GAS_LIMIT="$(jq -r '.execution.wrapGasLimit' "$PLAN_JSON")"
ADD_LIQUIDITY_GAS_LIMIT="$(jq -r '.execution.addLiquidityGasLimit' "$PLAN_JSON")"
GAS_PRICE_WEI="${CHAIN_GAS_PRICE:-$(jq -r '.execution.legacyGasPriceWei' "$PLAN_JSON")}"
WRAP_WHEN_NEEDED="$(jq -r '.execution.wrapNativeEthWhenNeeded' "$PLAN_JSON")"
MINT_WHEN_OWNER="$(jq -r '.execution.mintMissingBaseWhenOwner' "$PLAN_JSON")"
APPROVE_MAX="$(jq -r '.execution.approveMax' "$PLAN_JSON")"
DEPLOYER="$(derive_deployer_address || true)"
[[ -n "$DEPLOYER" ]] || { echo "ERROR: Could not derive DEPLOYER_ADDRESS from PRIVATE_KEY." >&2; exit 1; }
WETH_ADDRESS="$(jq -r '.tokens.WETH' "$EXECUTION_CONFIG")"
MAX_UINT="0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
ZERO_ADDR="0x0000000000000000000000000000000000000000"
log() {
printf '%s\n' "$*"
}
get_pool_reserves() {
local pool="$1"
local lines
mapfile -t lines < <(cast call "$pool" 'getVaultReserve()(uint256,uint256)' --rpc-url "$RPC_URL_138")
printf '%s\t%s\n' "$(uint_clean "${lines[0]:-0}")" "$(uint_clean "${lines[1]:-0}")"
}
uint_clean() {
printf '%s\n' "$1" | awk '{print $1}'
}
hex_to_addr() {
local raw="${1#0x}"
if [[ ${#raw} -lt 40 ]]; then
printf '%s\n' "$ZERO_ADDR"
return 0
fi
printf '0x%s\n' "${raw: -40}"
}
send_tx() {
local to="$1"
shift
cast send "$to" "$@" \
--rpc-url "$RPC_URL_138" \
--private-key "$PRIVATE_KEY" \
--legacy \
--gas-price "$GAS_PRICE_WEI" \
-q
}
big_add() {
python3 - "$1" "$2" <<'PY'
import sys
print(int(sys.argv[1]) + int(sys.argv[2]))
PY
}
big_sub_if_positive() {
python3 - "$1" "$2" <<'PY'
import sys
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a - b if a > b else 0)
PY
}
big_lt() {
python3 - "$1" "$2" <<'PY'
import sys
print("1" if int(sys.argv[1]) < int(sys.argv[2]) else "0")
PY
}
raw_to_human_6() {
awk -v v="$1" 'BEGIN { printf "%.6f", v / 1000000 }'
}
raw_to_human_18() {
awk -v v="$1" 'BEGIN { printf "%.18f", v / 1000000000000000000 }'
}
mul_div_round() {
awk -v a="$1" -v b="$2" -v c="$3" 'BEGIN { printf "%.0f", (a * b) / c }'
}
derive_usd_per_eth() {
local pool base_reserve quote_reserve
local cusdt quote
cusdt="$(jq -r '.tokens.cUSDT' "$EXECUTION_CONFIG")"
quote="$WETH_ADDRESS"
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$cusdt" "$quote" --rpc-url "$RPC_URL_138")")"
[[ "$pool" != "$ZERO_ADDR" ]] || { echo "0"; return 1; }
read -r base_reserve quote_reserve < <(get_pool_reserves "$pool")
awk -v base="$base_reserve" -v quote="$quote_reserve" 'BEGIN { printf "%.12f", (base / 1000000) / (quote / 1000000000000000000) }'
}
derive_xau_usd_per_unit() {
local pool base_symbol quote_symbol base_addr quote_addr base_reserve quote_reserve fallback
base_symbol="$(jq -r '.xauPricing.poolBaseSymbol' "$PLAN_JSON")"
quote_symbol="$(jq -r '.xauPricing.poolQuoteSymbol' "$PLAN_JSON")"
fallback="$(jq -r '.xauPricing.fallbackUsdPerUnit' "$PLAN_JSON")"
base_addr="$(jq -r --arg sym "$base_symbol" '.tokens[$sym]' "$EXECUTION_CONFIG")"
quote_addr="$(jq -r --arg sym "$quote_symbol" '.tokens[$sym]' "$EXECUTION_CONFIG")"
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$base_addr" "$quote_addr" --rpc-url "$RPC_URL_138")")"
if [[ "$pool" == "$ZERO_ADDR" ]]; then
echo "$fallback"
return 0
fi
read -r base_reserve quote_reserve < <(get_pool_reserves "$pool")
if [[ "$base_reserve" == "0" || "$quote_reserve" == "0" ]]; then
echo "$fallback"
return 0
fi
awk -v base="$base_reserve" -v quote="$quote_reserve" 'BEGIN { printf "%.12f", (quote / 1000000) / (base / 1000000) }'
}
usd_per_eth="$(derive_usd_per_eth)"
[[ "$usd_per_eth" != "0" ]] || { echo "Could not derive USD per ETH from live cUSDT/WETH pool" >&2; exit 1; }
xau_usd_per_unit="$(derive_xau_usd_per_unit)"
log "=== Chain 138 ETH PMM Liquidity Funding ==="
log "Mode: $( [[ "$EXECUTE" == "1" ]] && echo EXECUTE || echo DRY_RUN )"
log "Profile: $PROFILE"
log "Plan: $PLAN_JSON"
log "Config: $EXECUTION_CONFIG"
log "Integration: $DODO_PMM_INTEGRATION_ADDRESS"
log "RPC: $RPC_URL_138"
log "Deployer: $DEPLOYER"
log "USD per ETH: $usd_per_eth"
log "XAU USD: $xau_usd_per_unit"
log ""
total_quote_units=0
wrap_needed=0
declare -a ROWS=()
while IFS=$'\t' read -r base_symbol base_address; do
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$base_address" "$WETH_ADDRESS" --rpc-url "$RPC_URL_138")")"
[[ "$pool" != "$ZERO_ADDR" ]] || continue
case "$base_symbol" in
cXAUC|cXAUT)
base_units="$XAU_BASE_UNITS"
usd_per_unit="$xau_usd_per_unit"
;;
*)
base_units="$FIAT_BASE_UNITS"
usd_per_unit="$(jq -r --arg sym "$base_symbol" '.usdPerUnit[$sym]' "$PLAN_JSON")"
[[ "$usd_per_unit" != "null" ]] || { echo "Missing usdPerUnit for $base_symbol" >&2; exit 1; }
;;
esac
quote_units="$(awk -v base="$base_units" -v usd="$usd_per_unit" -v usd_eth="$usd_per_eth" 'BEGIN { printf "%.0f", ((base / 1000000) * usd / usd_eth) * 1000000000000000000 }')"
read -r current_base_reserve current_quote_reserve < <(get_pool_reserves "$pool")
base_delta="$(big_sub_if_positive "$base_units" "$current_base_reserve")"
quote_delta="$(big_sub_if_positive "$quote_units" "$current_quote_reserve")"
base_balance="$(cast call "$base_address" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL_138" 2>/dev/null || echo 0)"
base_balance="$(uint_clean "$base_balance")"
owner_addr="$(cast call "$base_address" 'owner()(address)' --rpc-url "$RPC_URL_138" 2>/dev/null || echo "$ZERO_ADDR")"
ROWS+=("$base_symbol|$base_address|$pool|$base_units|$quote_units|$current_base_reserve|$current_quote_reserve|$base_delta|$quote_delta|$base_balance|$owner_addr")
total_quote_units="$(big_add "$total_quote_units" "$quote_delta")"
done < <(jq -r '.explicitPairs[] | [.baseSymbol, (.baseSymbol as $s | .baseSymbol)] | @tsv' "$EXECUTION_CONFIG" | while IFS=$'\t' read -r base_symbol _; do
base_address="$(jq -r --arg sym "$base_symbol" '.tokens[$sym]' "$EXECUTION_CONFIG")"
printf '%s\t%s\n' "$base_symbol" "$base_address"
done)
weth_balance="$(cast call "$WETH_ADDRESS" 'balanceOf(address)(uint256)' "$DEPLOYER" --rpc-url "$RPC_URL_138" 2>/dev/null || echo 0)"
weth_balance="$(uint_clean "$weth_balance")"
eth_balance="$(cast balance "$DEPLOYER" --rpc-url "$RPC_URL_138" 2>/dev/null || echo 0)"
if [[ "$(big_lt "$weth_balance" "$total_quote_units")" == "1" ]]; then
wrap_needed="$(big_sub_if_positive "$total_quote_units" "$weth_balance")"
fi
log "Current ETH: $(raw_to_human_18 "$eth_balance")"
log "Current WETH: $(raw_to_human_18 "$weth_balance")"
log "Total WETH needed for profile: $(raw_to_human_18 "$total_quote_units")"
log "Additional WETH to wrap: $(raw_to_human_18 "$wrap_needed")"
log ""
for row in "${ROWS[@]}"; do
IFS='|' read -r base_symbol base_address pool base_units quote_units current_base_reserve current_quote_reserve base_delta quote_delta base_balance owner_addr <<< "$row"
need_mint=0
if [[ "$(big_lt "$base_balance" "$base_delta")" == "1" ]]; then
need_mint="$(big_sub_if_positive "$base_delta" "$base_balance")"
fi
log "$base_symbol pool=$pool"
log " target base: $(raw_to_human_6 "$base_units")"
log " target quote: $(raw_to_human_18 "$quote_units") WETH"
log " current base: $(raw_to_human_6 "$current_base_reserve")"
log " current quote: $(raw_to_human_18 "$current_quote_reserve") WETH"
log " top-up base: $(raw_to_human_6 "$base_delta")"
log " top-up quote: $(raw_to_human_18 "$quote_delta") WETH"
log " base balance: $(raw_to_human_6 "$base_balance")"
if [[ "$need_mint" != "0" ]]; then
log " mint needed: $(raw_to_human_6 "$need_mint")"
else
log " mint needed: no"
fi
done
if [[ "$EXECUTE" != "1" ]]; then
log ""
log "Dry run only. Re-run with EXECUTE=1 to mint/wrap/approve/add liquidity."
exit 0
fi
if [[ "$wrap_needed" != "0" ]]; then
if [[ "$WRAP_WHEN_NEEDED" != "true" ]]; then
echo "Need additional WETH but wrapNativeEthWhenNeeded=false" >&2
exit 1
fi
if [[ "$(big_lt "$eth_balance" "$wrap_needed")" == "1" ]]; then
echo "Insufficient ETH to wrap required WETH" >&2
exit 1
fi
log "Wrapping $(raw_to_human_18 "$wrap_needed") ETH into WETH"
send_tx "$WETH_ADDRESS" 'deposit()' --value "$wrap_needed" --gas-limit "$WRAP_GAS_LIMIT" >/dev/null
fi
for row in "${ROWS[@]}"; do
IFS='|' read -r base_symbol base_address pool base_units quote_units current_base_reserve current_quote_reserve base_delta quote_delta base_balance owner_addr <<< "$row"
if [[ "$base_delta" == "0" && "$quote_delta" == "0" ]]; then
log "Skipping $base_symbol/WETH; already at or above target."
continue
fi
if [[ "$(big_lt "$base_balance" "$base_delta")" == "1" ]]; then
mint_amount="$(big_sub_if_positive "$base_delta" "$base_balance")"
if [[ "$MINT_WHEN_OWNER" != "true" ]]; then
echo "Need to mint $base_symbol but mintMissingBaseWhenOwner=false" >&2
exit 1
fi
if [[ "${owner_addr,,}" != "${DEPLOYER,,}" ]]; then
echo "Cannot mint $base_symbol; deployer is not owner ($owner_addr)" >&2
exit 1
fi
log "Minting $(raw_to_human_6 "$mint_amount") $base_symbol"
send_tx "$base_address" 'mint(address,uint256)' "$DEPLOYER" "$mint_amount" --gas-limit "$MINT_GAS_LIMIT" >/dev/null
fi
if [[ "$APPROVE_MAX" == "true" ]]; then
log "Approving $base_symbol to integration"
send_tx "$base_address" 'approve(address,uint256)' "$DODO_PMM_INTEGRATION_ADDRESS" "$MAX_UINT" --gas-limit "$APPROVE_GAS_LIMIT" >/dev/null
log "Approving WETH for $base_symbol pair"
send_tx "$WETH_ADDRESS" 'approve(address,uint256)' "$DODO_PMM_INTEGRATION_ADDRESS" "$MAX_UINT" --gas-limit "$APPROVE_GAS_LIMIT" >/dev/null
fi
log "Adding liquidity to $base_symbol/WETH at $pool"
send_tx "$DODO_PMM_INTEGRATION_ADDRESS" 'addLiquidity(address,uint256,uint256)' "$pool" "$base_delta" "$quote_delta" --gas-limit "$ADD_LIQUIDITY_GAS_LIMIT" >/dev/null
done
log ""
log "Liquidity funding complete."

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1090
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
load_deployment_env --repo-root "$REPO_ROOT"
fi
POSITION_MANAGER="${POSITION_MANAGER:-}"
RPC_URL="${RPC_URL:-}"
TOKEN_A="${TOKEN_A:-}"
TOKEN_B="${TOKEN_B:-}"
AMOUNT_A="${AMOUNT_A:-}"
AMOUNT_B="${AMOUNT_B:-}"
FEE="${FEE:-500}"
EXECUTE="${EXECUTE:-0}"
DEADLINE_SECONDS="${DEADLINE_SECONDS:-3600}"
RECIPIENT="${RECIPIENT:-}"
PRIVATE_KEY="${PRIVATE_KEY:-}"
if [[ -z "$POSITION_MANAGER" || -z "$RPC_URL" || -z "$TOKEN_A" || -z "$TOKEN_B" || -z "$AMOUNT_A" || -z "$AMOUNT_B" ]]; then
echo "Required: POSITION_MANAGER RPC_URL TOKEN_A TOKEN_B AMOUNT_A AMOUNT_B" >&2
exit 1
fi
if [[ -z "$RECIPIENT" ]]; then
if [[ -z "$PRIVATE_KEY" ]]; then
echo "Set RECIPIENT or PRIVATE_KEY" >&2
exit 1
fi
RECIPIENT="$(cast wallet address --private-key "$PRIVATE_KEY")"
fi
lower_hex="$(printf '%s\n%s\n' "$TOKEN_A" "$TOKEN_B" | tr '[:upper:]' '[:lower:]' | sort | sed -n '1p')"
upper_hex="$(printf '%s\n%s\n' "$TOKEN_A" "$TOKEN_B" | tr '[:upper:]' '[:lower:]' | sort | sed -n '2p')"
token0="$lower_hex"
token1="$upper_hex"
amount0="$AMOUNT_A"
amount1="$AMOUNT_B"
if [[ "${TOKEN_A,,}" != "$token0" ]]; then
amount0="$AMOUNT_B"
amount1="$AMOUNT_A"
fi
case "$FEE" in
100) tick_spacing=1 ;;
500) tick_spacing=10 ;;
3000) tick_spacing=60 ;;
10000) tick_spacing=200 ;;
*)
echo "Unsupported fee tier for tick-spacing inference: $FEE" >&2
exit 1
;;
esac
min_tick=-887272
max_tick=887272
tick_lower=$(( (min_tick / tick_spacing) * tick_spacing ))
tick_upper=$(( (max_tick / tick_spacing) * tick_spacing ))
deadline="$(($(date +%s) + DEADLINE_SECONDS))"
echo "Funding Uniswap v3 pool"
echo " token0: $token0 amount0: $amount0"
echo " token1: $token1 amount1: $amount1"
echo " fee: $FEE ticks: [$tick_lower, $tick_upper]"
echo " recipient: $RECIPIENT"
if [[ "$EXECUTE" != "1" ]]; then
echo "Dry run only."
echo "Approve token0:"
echo " cast send \"$token0\" 'approve(address,uint256)' \"$POSITION_MANAGER\" \"$amount0\" --rpc-url \"$RPC_URL\" --private-key \"\$PRIVATE_KEY\""
echo "Approve token1:"
echo " cast send \"$token1\" 'approve(address,uint256)' \"$POSITION_MANAGER\" \"$amount1\" --rpc-url \"$RPC_URL\" --private-key \"\$PRIVATE_KEY\""
echo "Mint position:"
echo " cast send \"$POSITION_MANAGER\" \"mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256))\" \"($token0,$token1,$FEE,$tick_lower,$tick_upper,$amount0,$amount1,0,0,$RECIPIENT,$deadline)\" --rpc-url \"$RPC_URL\" --private-key \"\$PRIVATE_KEY\" -vv"
exit 0
fi
if [[ -z "$PRIVATE_KEY" ]]; then
echo "PRIVATE_KEY is required when EXECUTE=1" >&2
exit 1
fi
cast send "$token0" \
"approve(address,uint256)" \
"$POSITION_MANAGER" \
"$amount0" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
-q
cast send "$token1" \
"approve(address,uint256)" \
"$POSITION_MANAGER" \
"$amount1" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
-q
cast send "$POSITION_MANAGER" \
"mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256))" \
"($token0,$token1,$FEE,$tick_lower,$tick_upper,$amount0,$amount1,0,0,$RECIPIENT,$deadline)" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
-vv

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Run all explorer verification entrypoints: Chain 138 Blockscout, Ethereum, multichain cW*,
# Avax/Arb bridges, optional Cronos CCIP, optional CCIPLogger (Hardhat), optional Wemix.
# Steps are best-effort (continue on failure); check log for FAILED lines.
#
# Usage: cd smom-dbis-138 && ./scripts/deployment/verify-all-networks-explorers.sh
set -uo pipefail
set +e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SMOM="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Parent of smom-dbis-138 = proxmox workspace root
PROXMOX="$(cd "$SMOM/.." && pwd)"
STEPS_FAILED=0
step() {
local title="$1"
shift
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$title"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if "$@"; then
echo "[ok] $title"
else
echo "[fail] $title (exit $?)" >&2
STEPS_FAILED=$((STEPS_FAILED + 1))
fi
}
# Load env for keys
if [[ -f "$SCRIPT_DIR/../lib/deployment/dotenv.sh" ]]; then
# shellcheck disable=SC1090
source "$SCRIPT_DIR/../lib/deployment/dotenv.sh"
load_deployment_env --repo-root "${SMOM}"
fi
if [[ -f "$PROXMOX/scripts/verify/run-contract-verification-with-proxy.sh" ]]; then
step "Chain 138 Blockscout (forge proxy)" bash "$PROXMOX/scripts/verify/run-contract-verification-with-proxy.sh" || true
else
echo "WARN: Proxmox verify proxy not found at $PROXMOX/scripts/verify/" >&2
fi
step "Ethereum Mainnet CCIP bridges" bash "$SCRIPT_DIR/verify-mainnet-etherscan.sh" || true
step "Ethereum Mainnet cW* (CompliantWrappedToken)" bash "$SCRIPT_DIR/verify-mainnet-cw-etherscan.sh" || true
step "Multichain cW* (all CW*_* except MAINNET)" bash "$SCRIPT_DIR/verify-multichain-cw-etherscan.sh" || true
step "Avalanche + Arbitrum WETH/CCIP bridges" bash "$SCRIPT_DIR/verify-deployed-contracts.sh" || true
# Bytecode + manual-verify instructions (no explorer API key required).
step "Cronos deployments + manual verify runbook" bash "$SCRIPT_DIR/verify-cronos-contracts.sh" || true
if command -v npx &>/dev/null && [[ -f "$SMOM/hardhat.config.ts" || -f "$SMOM/hardhat.config.js" ]]; then
step "CCIPLogger (Hardhat verify, multichain)" bash "$SCRIPT_DIR/verify-ccip-logger-other-chains.sh" || true
else
echo "Skip CCIPLogger: npx/hardhat config not available"
fi
if [[ -n "${WEMIXSCAN_API_KEY:-}" && -n "${WEMIX_RPC:-}" && -n "${CCIPWETH9_BRIDGE_WEMIX:-}" ]]; then
step "Wemix CCIP bridges" bash "$SCRIPT_DIR/verify-wemix-bridges.sh" || true
else
echo "Skip Wemix: WEMIXSCAN_API_KEY / WEMIX_RPC / CCIPWETH9_BRIDGE_WEMIX not all set"
fi
echo ""
echo "=== verify-all-networks-explorers finished: step failures=$STEPS_FAILED ==="
exit 0

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# Verify Ethereum Mainnet CompliantWrappedToken (cWUSDT…cWXAUT) on Etherscan.
# Constructor matches script/deploy/DeployCWTokens.s.sol (name, symbol, 6 decimals, deployer admin).
#
# Requires: ETHERSCAN_API_KEY, and either CW_VERIFY_ADMIN=0x... (deployer used at deploy) or PRIVATE_KEY
# (address derived via cast wallet address) to ABI-encode constructor args.
#
# Usage:
# cd smom-dbis-138 && ./scripts/deployment/verify-mainnet-cw-etherscan.sh
# ./scripts/deployment/verify-mainnet-cw-etherscan.sh --dry-run # print forge commands only
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$PROJECT_ROOT"
DRY_RUN=false
for a in "$@"; do
case "$a" in
--dry-run) DRY_RUN=true ;;
esac
done
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
if [[ -z "${ETHERSCAN_API_KEY:-}" ]]; then
echo "ETHERSCAN_API_KEY not set (see https://etherscan.io/myapikey)" >&2
exit 1
fi
ADMIN="${CW_VERIFY_ADMIN:-}"
if [[ -z "$ADMIN" && -n "${PRIVATE_KEY:-}" ]]; then
ADMIN="$(cast wallet address --private-key "$PRIVATE_KEY")"
fi
if [[ -z "$ADMIN" || "$ADMIN" == "0x0000000000000000000000000000000000000000" ]]; then
echo "Set CW_VERIFY_ADMIN to the deployer address used in CompliantWrappedToken constructor, or set PRIVATE_KEY." >&2
exit 1
fi
verify_one() {
local envkey="$1" cname="$2" sym="$3"
local addr
addr="${!envkey:-}"
if [[ -z "$addr" ]]; then
echo " skip $sym: $envkey unset"
return 0
fi
local enc
enc="$(cast abi-encode "constructor(string,string,uint8,address)" "$cname" "$sym" 6 "$ADMIN")"
if $DRY_RUN; then
echo "forge verify-contract --chain-id 1 --num-of-optimizations 200 --via-ir --constructor-args \"$enc\" --etherscan-api-key \"\$ETHERSCAN_API_KEY\" \"$addr\" contracts/tokens/CompliantWrappedToken.sol:CompliantWrappedToken"
return 0
fi
set +e
out="$(forge verify-contract \
--chain-id 1 \
--num-of-optimizations 200 \
--via-ir \
--constructor-args "$enc" \
--etherscan-api-key "$ETHERSCAN_API_KEY" \
"$addr" \
"contracts/tokens/CompliantWrappedToken.sol:CompliantWrappedToken" 2>&1)"
rc=$?
set -e
if [[ $rc -eq 0 ]]; then
echo " ok $sym $addr"
elif echo "$out" | grep -qiE 'already verified|Already Verified'; then
echo " already verified $sym $addr"
else
echo " FAILED $sym $addr (exit $rc)" >&2
echo "$out" >&2
return "$rc"
fi
}
echo "=== Verify Mainnet CompliantWrappedToken (admin=$ADMIN) ==="
# envVar|constructor name|symbol — must match DeployCWTokens.s.sol
while IFS='|' read -r envkey cname sym; do
[[ -z "$envkey" ]] && continue
verify_one "$envkey" "$cname" "$sym"
done <<'EOF'
CWUSDT_MAINNET|Wrapped cUSDT|cWUSDT
CWUSDC_MAINNET|Wrapped cUSDC|cWUSDC
CWEURC_MAINNET|Wrapped cEURC|cWEURC
CWEURT_MAINNET|Wrapped cEURT|cWEURT
CWGBPC_MAINNET|Wrapped cGBPC|cWGBPC
CWGBPT_MAINNET|Wrapped cGBPT|cWGBPT
CWAUDC_MAINNET|Wrapped cAUDC|cWAUDC
CWJPYC_MAINNET|Wrapped cJPYC|cWJPYC
CWCHFC_MAINNET|Wrapped cCHFC|cWCHFC
CWCADC_MAINNET|Wrapped cCADC|cWCADC
CWXAUC_MAINNET|Wrapped cXAUC|cWXAUC
CWXAUT_MAINNET|Wrapped cXAUT|cWXAUT
EOF
echo "Done."

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
# Verify CompliantWrappedToken (cW*) on all chains where addresses exist in .env.
# Skips CW*_MAINNET (use verify-mainnet-cw-etherscan.sh). Same constructor as DeployCWTokens.
#
# Requires: ETHERSCAN_API_KEY (Etherscan v2 / unified explorers); CRONOSCAN_API_KEY for Cronos.
# CW_VERIFY_ADMIN or PRIVATE_KEY for constructor admin address.
#
# Usage: cd smom-dbis-138 && ./scripts/deployment/verify-multichain-cw-etherscan.sh
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
if [[ -z "${ETHERSCAN_API_KEY:-}" ]]; then
echo "ETHERSCAN_API_KEY not set" >&2
exit 1
fi
ADMIN="${CW_VERIFY_ADMIN:-}"
if [[ -z "$ADMIN" && -n "${PRIVATE_KEY:-}" ]]; then
ADMIN="$(cast wallet address --private-key "$PRIVATE_KEY")"
fi
if [[ -z "$ADMIN" || "$ADMIN" == "0x0000000000000000000000000000000000000000" ]]; then
echo "Set CW_VERIFY_ADMIN or PRIVATE_KEY" >&2
exit 1
fi
# Uppercase chain suffix -> forge --chain name (Foundry)
forge_chain() {
case "$1" in
BSC) echo bsc ;;
POLYGON) echo polygon ;;
GNOSIS) echo gnosis ;;
OPTIMISM) echo optimism ;;
BASE) echo base ;;
ARBITRUM) echo arbitrum ;;
AVALANCHE) echo avalanche ;;
CELO) echo celo ;;
CRONOS) echo cronos ;;
*) echo "" ;;
esac
}
# API key for forge (Cronos uses cronoscan)
api_key_for() {
case "$1" in
CRONOS) echo "${CRONOSCAN_API_KEY:-}" ;;
*) echo "${ETHERSCAN_API_KEY}" ;;
esac
}
# cWUSDT -> Wrapped cUSDT / cWUSDT
name_sym_from_cw() {
local cw="$1"
local core="${cw#CW}"
echo "Wrapped c${core}|cW${core}"
}
OK=0
SKIP=0
FAIL=0
while IFS= read -r line; do
[[ "$line" =~ ^CW[A-Z0-9]+_[A-Z0-9]+= ]] || continue
key="${line%%=*}"
addr="${line#*=}"
[[ -z "$addr" || "$addr" == "0x0000000000000000000000000000000000000000" ]] && continue
chain="${key##*_}"
[[ "$chain" == "MAINNET" ]] && continue
# Cronos: Foundry + Etherscan API v2 often errors ("chainid"); use verify-cronos-contracts.sh or explorer.cronos.org manual upload.
[[ "$chain" == "CRONOS" ]] && { echo " skip $key (Cronos: use verify-cronos-contracts.sh or CRONOS_VERIFICATION_RUNBOOK)"; SKIP=$((SKIP + 1)); continue; }
fc="$(forge_chain "$chain")"
if [[ -z "$fc" ]]; then
echo " skip $key (unknown chain suffix $chain)"
SKIP=$((SKIP + 1))
continue
fi
api="$(api_key_for "$chain")"
if [[ -z "$api" ]]; then
echo " skip $key (no API key for $chain; set CRONOSCAN_API_KEY for Cronos)" >&2
SKIP=$((SKIP + 1))
continue
fi
cwpart="${key%_*}"
IFS='|' read -r cname sym <<< "$(name_sym_from_cw "$cwpart")"
enc="$(cast abi-encode "constructor(string,string,uint8,address)" "$cname" "$sym" 6 "$ADMIN")"
echo "[$fc] $sym $addr ..."
set +e
out="$(forge verify-contract \
--chain "$fc" \
--num-of-optimizations 200 \
--via-ir \
--constructor-args "$enc" \
--etherscan-api-key "$api" \
"$addr" \
"contracts/tokens/CompliantWrappedToken.sol:CompliantWrappedToken" 2>&1)"
rc=$?
set -e
if [[ $rc -eq 0 ]]; then
echo " ok"
OK=$((OK + 1))
elif echo "$out" | grep -qiE 'already verified|Already Verified|Contract source code already verified'; then
echo " already verified"
OK=$((OK + 1))
else
echo " FAILED (exit $rc)" >&2
echo "$out" >&2
FAIL=$((FAIL + 1))
fi
sleep 1
done < <(env | grep '^CW[A-Z0-9]*_' | sort)
echo ""
echo "=== multichain cW*: ok/already=$OK skipped=$SKIP failed=$FAIL ==="

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
bash scripts/hybx-omnl/verify-deployment.sh
echo "== omnl reconcile artifact =="
bash scripts/hybx-omnl/omnl-reconcile-artifact.sh
cd services/token-aggregation
if command -v pnpm >/dev/null 2>&1; then
pnpm run build
else
npm run build
fi
echo "ci-omnl-validation: OK"

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Run OMNL IPSAS + journal-matrix anchor and persist JSON + sha256 under artifacts/omnl-reconcile/.
# Usage: from repo root — bash scripts/hybx-omnl/omnl-reconcile-artifact.sh
# Env:
# OMNL_RECONCILE_ARTIFACT_DIR — output directory (default: $REPO_ROOT/artifacts/omnl-reconcile)
# OMNL_IPSAS_GL_REGISTRY, OMNL_JOURNAL_MATRIX_PATH — passed through to omnl-reconcile-report.mjs
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OUT="${OMNL_RECONCILE_ARTIFACT_DIR:-$ROOT/artifacts/omnl-reconcile}"
TS="$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$OUT"
# Invoke the report script with node so stdout is JSON only (npm run adds lifecycle lines).
node "$ROOT/services/token-aggregation/scripts/omnl-reconcile-report.mjs" > "$OUT/omnl-reconcile-${TS}.json"
cp -f "$OUT/omnl-reconcile-${TS}.json" "$OUT/omnl-reconcile-latest.json"
node -e "
const fs = require('fs');
const j = JSON.parse(fs.readFileSync('$OUT/omnl-reconcile-latest.json', 'utf8'));
fs.writeFileSync('$OUT/omnl-reconcile-sha256.txt', String(j.sha256).trim() + '\n');
"
if [[ -n "${GITHUB_SHA:-}" ]] || [[ -n "${GITHUB_RUN_ID:-}" ]]; then
node -e "
const fs = require('fs');
const o = {
generatedAt: new Date().toISOString(),
githubSha: process.env.GITHUB_SHA || null,
runId: process.env.GITHUB_RUN_ID || null,
repository: process.env.GITHUB_REPOSITORY || null,
};
fs.writeFileSync('$OUT/omnl-reconcile-ci-meta.json', JSON.stringify(o, null, 2) + '\n');
" 2>/dev/null || true
fi
echo "OMNL reconcile artifacts written:"
echo " $OUT/omnl-reconcile-${TS}.json"
echo " $OUT/omnl-reconcile-latest.json"
echo " $OUT/omnl-reconcile-sha256.txt ($(cat "$OUT/omnl-reconcile-sha256.txt" | tr -d '\n'))"

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Copy HYBX OMNLrelated paths from smom-dbis-138 → smom-dbis-138-publish (adjust PUBLISH_ROOT if needed).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
PUBLISH_ROOT="${PUBLISH_ROOT:-$ROOT/../smom-dbis-138-publish}"
if [[ ! -d "$PUBLISH_ROOT" ]]; then
echo "error: PUBLISH_ROOT not found: $PUBLISH_ROOT" >&2
exit 1
fi
rsync -a "$ROOT/contracts/hybx-omnl/" "$PUBLISH_ROOT/contracts/hybx-omnl/"
rsync -a "$ROOT/script/hybx-omnl/" "$PUBLISH_ROOT/script/hybx-omnl/"
rsync -a "$ROOT/test/hybx-omnl/" "$PUBLISH_ROOT/test/hybx-omnl/"
rsync -a "$ROOT/docs/hybx-omnl/" "$PUBLISH_ROOT/docs/hybx-omnl/"
for f in hybx-omnl-policy.json hybx-omnl-cross-chain-lines.json omnl-ipsas-gl-registry.json omnl-journal-matrix.json deployment-omnl.example.env; do
[[ -f "$ROOT/config/$f" ]] && cp -f "$ROOT/config/$f" "$PUBLISH_ROOT/config/$f"
done
rsync -a "$ROOT/scripts/hybx-omnl/" "$PUBLISH_ROOT/scripts/hybx-omnl/"
rsync -a "$ROOT/scripts/forge/scope.sh" "$PUBLISH_ROOT/scripts/forge/scope.sh"
mkdir -p "$PUBLISH_ROOT/.github/workflows"
for w in hybx-omnl-ts.yml omnl-reconcile.yml; do
[[ -f "$ROOT/.github/workflows/$w" ]] && cp -f "$ROOT/.github/workflows/$w" "$PUBLISH_ROOT/.github/workflows/$w"
done
TA="$ROOT/services/token-aggregation"
PTA="$PUBLISH_ROOT/services/token-aggregation"
for f in omnl-webhooks.ts omnl-ipsas-gl.ts omnl-journal-matrix.ts omnl-compliance.ts omnl-policy-math.ts omnl-reconcile-anchor.ts omnl-integration-status.ts omnl-api-catalog.ts; do
cp -f "$TA/src/services/$f" "$PTA/src/services/$f"
done
cp -f "$TA/src/services/omnl-reconcile-anchor.test.ts" "$PTA/src/services/omnl-reconcile-anchor.test.ts" 2>/dev/null || true
cp -f "$TA/src/api/routes/omnl.ts" "$PTA/src/api/routes/omnl.ts"
cp -f "$TA/src/api/routes/omnl-ipsas.ts" "$PTA/src/api/routes/omnl-ipsas.ts"
cp -f "$TA/src/indexer/omnl-event-poller.ts" "$PTA/src/indexer/omnl-event-poller.ts"
cp -f "$TA/src/indexer/omnl-poller-state.ts" "$PTA/src/indexer/omnl-poller-state.ts"
cp -f "$TA/src/api/server.ts" "$PTA/src/api/server.ts"
cp -f "$TA/src/api/middleware/rate-limit.ts" "$PTA/src/api/middleware/rate-limit.ts"
mkdir -p "$PTA/src/api/middleware"
cp -f "$TA/src/api/middleware/omnl-guards.ts" "$PTA/src/api/middleware/omnl-guards.ts"
cp -f "$TA/src/api/middleware/omnl-guards.test.ts" "$PTA/src/api/middleware/omnl-guards.test.ts" 2>/dev/null || true
cp -f "$TA/src/services/omnl-webhooks.test.ts" "$PTA/src/services/omnl-webhooks.test.ts" 2>/dev/null || true
mkdir -p "$PTA/src/resources"
cp -f "$TA/src/resources/omnl-openapi.json" "$PTA/src/resources/omnl-openapi.json"
cp -f "$TA/public/omnl-dashboard.html" "$PTA/public/omnl-dashboard.html"
cp -f "$TA/package.json" "$PTA/package.json"
cp -f "$ROOT/docs/deployment/DEPLOYMENT_INDEX.md" "$PUBLISH_ROOT/docs/deployment/DEPLOYMENT_INDEX.md" 2>/dev/null || true
cp -f "$ROOT/docs/MASTER_DOCUMENTATION_INDEX.md" "$PUBLISH_ROOT/docs/MASTER_DOCUMENTATION_INDEX.md" 2>/dev/null || true
cp -f "$ROOT/README.md" "$PUBLISH_ROOT/README.md" 2>/dev/null || true
# Publish verify-deployment stays lightweight (no Forge)
cat > "$PUBLISH_ROOT/scripts/hybx-omnl/verify-deployment.sh" <<'EOS'
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
echo "== validate cross-chain config =="
node scripts/hybx-omnl/validate-cross-chain-config.mjs
cd services/token-aggregation
echo "== token-aggregation tsc =="
pnpm exec tsc --noEmit
echo "== omnl reconcile report =="
node scripts/omnl-reconcile-report.mjs >/dev/null
echo "verify-deployment (publish): OK"
EOS
chmod +x "$PUBLISH_ROOT/scripts/hybx-omnl/verify-deployment.sh"
echo "sync-to-publish: OK → $PUBLISH_ROOT"

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env node
/**
* Validate hybx-omnl-cross-chain-lines.json: structure, lineId hex, token addresses.
* Exit 0 on success; exit 1 on validation errors (stderr).
*/
import { readFileSync } from 'fs';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
function isAddr(s) {
return typeof s === 'string' && /^0x[a-fA-F0-9]{40}$/.test(s);
}
const root = resolve(__dirname, '../..');
const configPath = process.env.OMNL_CROSS_CHAIN_CONFIG || resolve(root, 'config/hybx-omnl-cross-chain-lines.json');
function fail(msg) {
console.error(msg);
process.exit(1);
}
const raw = readFileSync(configPath, 'utf8');
let data;
try {
data = JSON.parse(raw);
} catch (e) {
fail(`Invalid JSON: ${configPath}: ${e}`);
}
if (!data.lines || !Array.isArray(data.lines)) {
fail('Expected top-level { lines: [...] }');
}
for (let i = 0; i < data.lines.length; i++) {
const row = data.lines[i];
const lid = row.lineId;
if (typeof lid !== 'string' || !/^0x[a-fA-F0-9]{64}$/.test(lid)) {
fail(`lines[${i}].lineId must be bytes32 hex: ${lid}`);
}
if (!row.chains || typeof row.chains !== 'object') {
fail(`lines[${i}].chains must be an object`);
}
for (const [cid, pair] of Object.entries(row.chains)) {
if (!/^\d+$/.test(cid)) fail(`lines[${i}].chains key must be chain id string: ${cid}`);
if (!pair || typeof pair !== 'object') fail(`lines[${i}].chains[${cid}] invalid`);
const p = pair;
if (!isAddr(p.tokenM0)) fail(`lines[${i}].chains[${cid}].tokenM0 invalid address: ${p.tokenM0}`);
if (!isAddr(p.tokenM1)) fail(`lines[${i}].chains[${cid}].tokenM1 invalid address: ${p.tokenM1}`);
}
}
console.log(`OK: ${configPath} (${data.lines.length} line(s))`);

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
echo "== validate cross-chain config =="
node scripts/hybx-omnl/validate-cross-chain-config.mjs
echo "== forge test hybx-omnl =="
bash scripts/forge/scope.sh test hybx-omnl
echo "== token-aggregation build =="
cd services/token-aggregation
pnpm exec tsc --noEmit
echo "verify-deployment: OK"