- Submodule pins: dbis_core, cross-chain-pmm-lps, mcp-proxmox (local, push may be pending), metamask-integration, smom-dbis-138 - Atomic swap + cross-chain-pmm-lops-publish, deploy-portal workflow, phoenix deploy-targets, routing/aggregator matrices - Docs, token-lists, forge proxy, phoenix API, runbooks, verify scripts Made-with: Cursor
192 lines
7.0 KiB
Bash
Executable File
192 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Price cW* wrapped stables on Ethereum Mainnet using:
|
|
# (1) Accounting peg assumption ($1 per token for USD-stable wrappers — document in reporting)
|
|
# (2) DODO PMM integration: getPoolPrice / getPoolPriceOrOracle (1e18 = $1 when oracle aligned)
|
|
# (3) Implied ratio from pool vault reserves (USDC per cWUSDT if base/quote match manifest)
|
|
# (4) Optional Chainlink ETH/USD (macro reference only; not cW* direct)
|
|
#
|
|
# Usage (from repo root):
|
|
# source scripts/lib/load-project-env.sh
|
|
# ./scripts/deployment/price-cw-token-mainnet.sh
|
|
# ./scripts/deployment/price-cw-token-mainnet.sh --json
|
|
# POOL_CWUSDT_USDC_MAINNET=0x... ./scripts/deployment/price-cw-token-mainnet.sh
|
|
#
|
|
# Requires: cast (Foundry), jq (for --json)
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
# shellcheck disable=SC1091
|
|
[[ -f "$PROJECT_ROOT/scripts/lib/load-project-env.sh" ]] && source "$PROJECT_ROOT/scripts/lib/load-project-env.sh"
|
|
|
|
JSON=false
|
|
for a in "$@"; do [[ "$a" == "--json" ]] && JSON=true; done
|
|
|
|
RPC="${ETHEREUM_MAINNET_RPC:-https://ethereum-rpc.publicnode.com}"
|
|
INT="${DODO_PMM_INTEGRATION_MAINNET:-0xa9F284eD010f4F7d7F8F201742b49b9f58e29b84}"
|
|
CWUSDT="${CWUSDT_MAINNET:-0xaF5017d0163ecb99D9B5D94e3b4D7b09Af44D8AE}"
|
|
# Mainnet canonical USDC (do not use Chain 138 OFFICIAL_USDC_ADDRESS for this script)
|
|
USDC_MAINNET_CANON="${USDC_MAINNET:-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48}"
|
|
USDC_OFFICIAL="${USDC_MAINNET_CANON}"
|
|
POOL="${POOL_CWUSDT_USDC_MAINNET:-0x27f3aE7EE71Be3d77bAf17d4435cF8B895DD25D2}"
|
|
# Chainlink ETH/USD — verified on mainnet; optional macro reference
|
|
CHAINLINK_ETH_USD="${CHAINLINK_ETH_USD_FEED:-0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419}"
|
|
|
|
first_word() {
|
|
awk '{print $1}' | head -1
|
|
}
|
|
|
|
to_dec() {
|
|
local x="$1"
|
|
[[ -z "$x" ]] && echo "" && return
|
|
echo "$x" | sed 's/\[.*//g' | awk '{print $1}'
|
|
}
|
|
|
|
get_decimals() {
|
|
local tok="$1"
|
|
to_dec "$(cast call "$tok" "decimals()(uint8)" --rpc-url "$RPC" 2>/dev/null || echo "")"
|
|
}
|
|
|
|
price_oracle_or_mid() {
|
|
local pool="$1"
|
|
local out
|
|
out=$(cast call "$INT" "getPoolPriceOrOracle(address)(uint256)" "$pool" --rpc-url "$RPC" 2>/dev/null | first_word) || true
|
|
if [[ -n "$out" && "$out" != *Error* ]]; then
|
|
printf '%s|%s\n' "getPoolPriceOrOracle" "$out"
|
|
return
|
|
fi
|
|
out=$(cast call "$INT" "getPoolPrice(address)(uint256)" "$pool" --rpc-url "$RPC" 2>/dev/null | first_word) || true
|
|
printf '%s|%s\n' "getPoolPrice" "$out"
|
|
}
|
|
|
|
parse_two_uints() {
|
|
python3 -c "
|
|
import re, sys
|
|
s = sys.stdin.read()
|
|
# cast may print scientific notation for large numbers
|
|
parts = re.findall(r'-?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?', s)
|
|
if len(parts) >= 2:
|
|
print(int(float(parts[0])), int(float(parts[1])))
|
|
"
|
|
}
|
|
|
|
reserves() {
|
|
local pool="$1"
|
|
cast call "$INT" "getPoolReserves(address)(uint256,uint256)" "$pool" --rpc-url "$RPC" 2>/dev/null | parse_two_uints
|
|
}
|
|
|
|
eth_usd_chainlink() {
|
|
local feed="$1"
|
|
[[ -z "$feed" ]] && { echo ""; return; }
|
|
local code
|
|
code=$(cast code "$feed" --rpc-url "$RPC" 2>/dev/null || true)
|
|
[[ -z "$code" || "$code" == "0x" ]] && { echo ""; return; }
|
|
cast call "$feed" "latestRoundData()(uint80,int256,uint256,uint256,uint80)" --rpc-url "$RPC" 2>/dev/null
|
|
}
|
|
|
|
db=$(get_decimals "$CWUSDT")
|
|
dq=$(get_decimals "$USDC_OFFICIAL")
|
|
read -r br_raw qr_raw < <(reserves "$POOL" | awk '{print $1, $2}')
|
|
br_raw=$(to_dec "${br_raw:-0}")
|
|
qr_raw=$(to_dec "${qr_raw:-0}")
|
|
|
|
_pom=$(price_oracle_or_mid "$POOL")
|
|
pm_method="${_pom%%|*}"
|
|
price_raw=$(to_dec "${_pom#*|}")
|
|
|
|
# price from integration is 1e18-scaled "USD" in docs
|
|
price_human=""
|
|
if [[ -n "$price_raw" && "$price_raw" =~ ^[0-9]+$ ]]; then
|
|
price_human=$(python3 -c "print(int('$price_raw')/1e18)" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
implied_usdc_per_cwusdt=""
|
|
if [[ -n "$br_raw" && -n "$qr_raw" && "$br_raw" != "0" && -n "$db" && -n "$dq" ]]; then
|
|
implied_usdc_per_cwusdt=$(python3 -c "br=int('$br_raw'); qr=int('$qr_raw'); db=int('$db'); dq=int('$dq'); print((qr/10**dq)/(br/10**db))" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
cl_eth=$(eth_usd_chainlink "$CHAINLINK_ETH_USD")
|
|
cl_eth_price=""
|
|
if [[ -n "$cl_eth" ]]; then
|
|
# latestRoundData(): cast prints one value per line; answer is the 2nd field (int256, 1e8 USD for ETH/USD)
|
|
ans=$(echo "$cl_eth" | awk 'NR == 2 { print $1; exit }')
|
|
[[ -n "$ans" ]] && cl_eth_price=$(python3 -c "a=int(float('$ans')); print(a/1e8)" 2>/dev/null || echo "")
|
|
fi
|
|
|
|
if [[ "$JSON" == true ]]; then
|
|
jq_n() { [[ -z "$1" ]] && echo null || echo "$1"; }
|
|
jq -n \
|
|
--arg network "ethereum-mainnet" \
|
|
--arg token "cWUSDT" \
|
|
--arg token_addr "$CWUSDT" \
|
|
--arg pool "$POOL" \
|
|
--arg integration "$INT" \
|
|
--argjson accounting_usd_assumption 1 \
|
|
--arg pool_price_method "${pm_method:-none}" \
|
|
--argjson pool_price_raw "$(jq_n "$price_raw")" \
|
|
--argjson pool_price_usd_scale "$(jq_n "$price_human")" \
|
|
--argjson base_reserve_raw "$(jq_n "$br_raw")" \
|
|
--argjson quote_reserve_raw "$(jq_n "$qr_raw")" \
|
|
--arg implied_str "${implied_usdc_per_cwusdt:-}" \
|
|
--argjson chainlink_eth_usd "$(jq_n "$cl_eth_price")" \
|
|
'{
|
|
network: $network,
|
|
token: $token,
|
|
tokenAddress: $token_addr,
|
|
poolAddress: $pool,
|
|
integrationAddress: $integration,
|
|
accountingUsdAssumptionPerToken: $accounting_usd_assumption,
|
|
poolMidOrOracle: {
|
|
method: $pool_price_method,
|
|
priceRaw1e18: $pool_price_raw,
|
|
priceAsUsdIf1e18EqualsOneDollar: $pool_price_usd_scale
|
|
},
|
|
impliedFromReserves: {
|
|
baseReserveRaw: $base_reserve_raw,
|
|
quoteReserveRaw: $quote_reserve_raw,
|
|
impliedUsdcPerCwusdtIfBaseIsCwusdtAndQuoteIsUsdc: (if $implied_str == "" then null else ($implied_str | tonumber) end)
|
|
},
|
|
chainlinkEthUsd: $chainlink_eth_usd
|
|
}'
|
|
exit 0
|
|
fi
|
|
|
|
cat << EOF
|
|
========================================
|
|
cWUSDT (Mainnet) — USD pricing toolkit
|
|
========================================
|
|
RPC: ${RPC%%\?*}
|
|
Integration: $INT
|
|
Pool (cWUSDT/USDC): $POOL
|
|
cWUSDT token: $CWUSDT
|
|
USDC (mainnet): $USDC_OFFICIAL
|
|
|
|
(1) ACCOUNTING ASSUMPTION (treasury / reporting)
|
|
Treat 1 cWUSDT ≈ 1 USD only if your policy says the wrapper tracks USDT 1:1.
|
|
Use for: internal books, dashboards where Etherscan shows \$0.
|
|
|
|
(2) POOL MID / ORACLE (DODOPMMIntegration)
|
|
Method: ${pm_method:-n/a}
|
|
Raw price (uint256, doc scale 1e18 = \$1): ${price_raw:-n/a}
|
|
As decimal (if 1e18 = \$1): ${price_human:-n/a}
|
|
|
|
(3) IMPLIED FROM VAULT RESERVES (USDC per cWUSDT)
|
|
Base reserve (cWUSDT, ${db:-?} dp): ${br_raw:-n/a}
|
|
Quote reserve (USDC, ${dq:-?} dp): ${qr_raw:-n/a}
|
|
Implied USDC per 1 cWUSDT: ${implied_usdc_per_cwusdt:-n/a}
|
|
(Thin or imbalanced pools skew this; use for sanity check, not sole mark.)
|
|
|
|
(4) CHAINLINK ETH/USD (macro only — not cWUSDT)
|
|
Feed: ${CHAINLINK_ETH_USD}
|
|
ETH/USD: ${cl_eth_price:-n/a}
|
|
|
|
Optional env overrides:
|
|
ETHEREUM_MAINNET_RPC, DODO_PMM_INTEGRATION_MAINNET,
|
|
CWUSDT_MAINNET, POOL_CWUSDT_USDC_MAINNET, USDC_MAINNET,
|
|
CHAINLINK_ETH_USD_FEED
|
|
|
|
See: docs/03-deployment/CW_TOKEN_USD_PRICING_RUNBOOK.md
|
|
========================================
|
|
EOF
|