- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains - Omit embedded publish git dirs and empty placeholders from index Made-with: Cursor
253 lines
8.5 KiB
Bash
Executable File
253 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Verify the live GRU c* V2 readiness state on Chain 138.
|
|
# Usage:
|
|
# bash scripts/verify/check-gru-v2-chain138-readiness.sh
|
|
# bash scripts/verify/check-gru-v2-chain138-readiness.sh --report-only
|
|
# RPC_URL=https://rpc-http-pub.d-bis.org bash scripts/verify/check-gru-v2-chain138-readiness.sh
|
|
#
|
|
# The script checks:
|
|
# - local CompliantFiatTokenV2 Foundry suite (optional via RUN_LOCAL_TESTS=1)
|
|
# - deployed bytecode for cUSDT V2 / cUSDC V2
|
|
# - UniversalAssetRegistry activation as GRU assets
|
|
# - core version/signing surface (name/symbol/versionTag/currencyCode/assetId/assetVersionId/DOMAIN_SEPARATOR)
|
|
# - promotion blockers (forwardCanonical still false, missing governance/supervision metadata ABI)
|
|
# - token metadata path is populated and uses decentralized URI format
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
export PROJECT_ROOT
|
|
|
|
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
|
|
set +eu
|
|
# shellcheck source=../lib/load-project-env.sh
|
|
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true
|
|
set -euo pipefail
|
|
fi
|
|
|
|
REPORT_ONLY=0
|
|
RUN_LOCAL_TESTS="${RUN_LOCAL_TESTS:-0}"
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--report-only) REPORT_ONLY=1 ;;
|
|
--run-local-tests) RUN_LOCAL_TESTS=1 ;;
|
|
*)
|
|
echo "Unknown argument: $arg" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
need_cmd() {
|
|
command -v "$1" >/dev/null 2>&1 || {
|
|
echo "[FAIL] Missing required command: $1" >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
need_cmd cast
|
|
|
|
RPC_URL="${RPC_URL:-${RPC_URL_138_PUBLIC:-${RPC_URL_138:-https://rpc-http-pub.d-bis.org}}}"
|
|
REGISTRY="${UNIVERSAL_ASSET_REGISTRY:-0xAEE4b7fBe82E1F8295951584CBc772b8BBD68575}"
|
|
CUSDT_V2="${COMPLIANT_USDT_V2:-${CUSDT_V2_ADDRESS_138:-0x9FBfab33882Efe0038DAa608185718b772EE5660}}"
|
|
CUSDC_V2="${COMPLIANT_USDC_V2:-${CUSDC_V2_ADDRESS_138:-0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d}}"
|
|
|
|
BLOCKERS=()
|
|
WARNINGS=()
|
|
|
|
ok() { printf '[OK] %s\n' "$*"; }
|
|
warn() { printf '[WARN] %s\n' "$*"; WARNINGS+=("$*"); }
|
|
block() { printf '[BLOCKER] %s\n' "$*"; BLOCKERS+=("$*"); }
|
|
|
|
call_or_fail() {
|
|
local address="$1"
|
|
local signature="$2"
|
|
shift 2
|
|
cast call "$address" "$signature" "$@" --rpc-url "$RPC_URL" 2>/dev/null
|
|
}
|
|
|
|
call_or_placeholder() {
|
|
local address="$1"
|
|
local signature="$2"
|
|
shift 2
|
|
cast call "$address" "$signature" "$@" --rpc-url "$RPC_URL" 2>/dev/null || printf '__CALL_FAILED__'
|
|
}
|
|
|
|
trim_quotes() {
|
|
local value="$1"
|
|
value="${value#\"}"
|
|
value="${value%\"}"
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
run_local_suite() {
|
|
local smom_root="${PROJECT_ROOT}/smom-dbis-138"
|
|
need_cmd forge
|
|
(
|
|
cd "$smom_root"
|
|
rm -f cache/solidity-files-cache.json
|
|
forge test --match-contract CompliantFiatTokenV2Test
|
|
)
|
|
}
|
|
|
|
check_token() {
|
|
local label="$1"
|
|
local address="$2"
|
|
local expected_symbol="$3"
|
|
local expected_currency="$4"
|
|
|
|
printf '\n=== %s ===\n' "$label"
|
|
printf 'Address: %s\n' "$address"
|
|
|
|
local code
|
|
code="$(cast code "$address" --rpc-url "$RPC_URL" 2>/dev/null || true)"
|
|
if [[ -z "$code" || "$code" == "0x" ]]; then
|
|
block "$label has no bytecode on Chain 138 ($address)"
|
|
return
|
|
fi
|
|
ok "$label bytecode present."
|
|
|
|
local name symbol version_tag currency forward_canonical asset_id asset_version_id domain_separator
|
|
name="$(trim_quotes "$(call_or_fail "$address" 'name()(string)')")"
|
|
symbol="$(trim_quotes "$(call_or_fail "$address" 'symbol()(string)')")"
|
|
version_tag="$(trim_quotes "$(call_or_fail "$address" 'versionTag()(string)')")"
|
|
currency="$(trim_quotes "$(call_or_fail "$address" 'currencyCode()(string)')")"
|
|
forward_canonical="$(call_or_fail "$address" 'forwardCanonical()(bool)')"
|
|
asset_id="$(call_or_fail "$address" 'assetId()(bytes32)')"
|
|
asset_version_id="$(call_or_fail "$address" 'assetVersionId()(bytes32)')"
|
|
domain_separator="$(call_or_fail "$address" 'DOMAIN_SEPARATOR()(bytes32)')"
|
|
|
|
printf 'name: %s\n' "$name"
|
|
printf 'symbol: %s\n' "$symbol"
|
|
printf 'versionTag: %s\n' "$version_tag"
|
|
printf 'currencyCode: %s\n' "$currency"
|
|
printf 'forwardCanonical: %s\n' "$forward_canonical"
|
|
printf 'assetId: %s\n' "$asset_id"
|
|
printf 'assetVersionId: %s\n' "$asset_version_id"
|
|
printf 'DOMAIN_SEPARATOR: %s\n' "$domain_separator"
|
|
|
|
[[ "$symbol" == "$expected_symbol" ]] || block "$label symbol mismatch (expected $expected_symbol, got $symbol)"
|
|
[[ "$currency" == "$expected_currency" ]] || block "$label currencyCode mismatch (expected $expected_currency, got $currency)"
|
|
[[ "$version_tag" == "2" ]] || block "$label versionTag is not 2 (got $version_tag)"
|
|
[[ "$domain_separator" != "0x0000000000000000000000000000000000000000000000000000000000000000" ]] || \
|
|
block "$label DOMAIN_SEPARATOR is zero"
|
|
|
|
local registry_active asset_type
|
|
registry_active="$(call_or_fail "$REGISTRY" 'isAssetActive(address)(bool)' "$address")"
|
|
asset_type="$(call_or_fail "$REGISTRY" 'getAssetType(address)(uint8)' "$address")"
|
|
printf 'registryActive: %s\n' "$registry_active"
|
|
printf 'registryAssetType: %s\n' "$asset_type"
|
|
[[ "$registry_active" == "true" ]] || block "$label is not active in UniversalAssetRegistry"
|
|
[[ "$asset_type" == "2" ]] || block "$label is not registered as AssetType.GRU (expected 2, got $asset_type)"
|
|
|
|
if [[ "$forward_canonical" != "true" ]]; then
|
|
block "$label is deployed and GRU-registered, but forwardCanonical is still false"
|
|
fi
|
|
|
|
local symbol_display token_uri legacy_aliases
|
|
symbol_display="$(trim_quotes "$(call_or_placeholder "$address" 'symbolDisplay()(string)')")"
|
|
token_uri="$(trim_quotes "$(call_or_placeholder "$address" 'tokenURI()(string)')")"
|
|
legacy_aliases="$(call_or_placeholder "$address" 'legacyAliases()(string[])')"
|
|
printf 'symbolDisplay: %s\n' "$symbol_display"
|
|
printf 'tokenURI: %s\n' "${token_uri:-<empty>}"
|
|
printf 'legacyAliases: %s\n' "$legacy_aliases"
|
|
|
|
if [[ -z "$token_uri" ]]; then
|
|
block "$label tokenURI is empty"
|
|
elif [[ "$token_uri" != ipfs://* ]]; then
|
|
block "$label tokenURI is not an ipfs:// URI ($token_uri)"
|
|
fi
|
|
|
|
local required_surface=(
|
|
"governanceProfileId()(bytes32)"
|
|
"supervisionProfileId()(bytes32)"
|
|
"storageNamespace()(bytes32)"
|
|
"governanceController()(address)"
|
|
"primaryJurisdiction()(string)"
|
|
"regulatoryDisclosureURI()(string)"
|
|
"reportingURI()(string)"
|
|
"canonicalUnderlyingAsset()(address)"
|
|
"supervisionRequired()(bool)"
|
|
"governmentApprovalRequired()(bool)"
|
|
"minimumUpgradeNoticePeriod()(uint256)"
|
|
"wrappedTransport()(bool)"
|
|
)
|
|
|
|
local fn value
|
|
for fn in "${required_surface[@]}"; do
|
|
value="$(call_or_placeholder "$address" "$fn")"
|
|
if [[ "$value" == "__CALL_FAILED__" ]]; then
|
|
block "$label is missing required GRU v2 surface: $fn"
|
|
else
|
|
printf '%s => %s\n' "$fn" "$value"
|
|
fi
|
|
done
|
|
}
|
|
|
|
audit_registry_orphans() {
|
|
local raw
|
|
raw="$(call_or_placeholder "$REGISTRY" 'getAssetsByType(uint8)(address[])' 2)"
|
|
if [[ "$raw" == "__CALL_FAILED__" ]]; then
|
|
warn "Could not enumerate AssetType.GRU entries from UniversalAssetRegistry."
|
|
return
|
|
fi
|
|
|
|
raw="${raw#[}"
|
|
raw="${raw%]}"
|
|
|
|
local registry_addresses=()
|
|
local item address code
|
|
IFS=',' read -r -a registry_addresses <<< "$raw"
|
|
for item in "${registry_addresses[@]}"; do
|
|
address="$(printf '%s' "$item" | xargs)"
|
|
[[ -z "$address" ]] && continue
|
|
code="$(cast code "$address" --rpc-url "$RPC_URL" 2>/dev/null || true)"
|
|
if [[ -z "$code" || "$code" == "0x" ]]; then
|
|
warn "UniversalAssetRegistry has an active GRU entry with no bytecode: $address"
|
|
fi
|
|
done
|
|
}
|
|
|
|
printf '=== GRU v2 Chain 138 readiness ===\n'
|
|
printf 'RPC: %s\n' "$RPC_URL"
|
|
printf 'UniversalAssetRegistry: %s\n' "$REGISTRY"
|
|
printf 'cUSDT V2: %s\n' "$CUSDT_V2"
|
|
printf 'cUSDC V2: %s\n' "$CUSDC_V2"
|
|
|
|
if [[ "$RUN_LOCAL_TESTS" == "1" ]]; then
|
|
printf '\nRunning local CompliantFiatTokenV2 Foundry suite...\n'
|
|
run_local_suite
|
|
ok "Local CompliantFiatTokenV2 suite passed."
|
|
else
|
|
warn "Skipping local Foundry suite. Re-run with --run-local-tests or RUN_LOCAL_TESTS=1 to include it."
|
|
fi
|
|
|
|
check_token "cUSDT V2" "$CUSDT_V2" "cUSDT" "USD"
|
|
check_token "cUSDC V2" "$CUSDC_V2" "cUSDC" "USD"
|
|
audit_registry_orphans
|
|
|
|
printf '\n=== Summary ===\n'
|
|
printf 'Warnings: %s\n' "${#WARNINGS[@]}"
|
|
printf 'Blockers: %s\n' "${#BLOCKERS[@]}"
|
|
|
|
if [[ "${#WARNINGS[@]}" -gt 0 ]]; then
|
|
printf '\nWarnings:\n'
|
|
for item in "${WARNINGS[@]}"; do
|
|
printf ' - %s\n' "$item"
|
|
done
|
|
fi
|
|
|
|
if [[ "${#BLOCKERS[@]}" -gt 0 ]]; then
|
|
printf '\nPromotion blockers:\n'
|
|
for item in "${BLOCKERS[@]}"; do
|
|
printf ' - %s\n' "$item"
|
|
done
|
|
if [[ "$REPORT_ONLY" -eq 1 ]]; then
|
|
exit 0
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
ok "GRU v2 assets are ready for forward-canonical promotion on Chain 138."
|