#!/usr/bin/env bash # Export BSC cW* verification artifacts: pinned profile build + Standard JSON for BscScan manual upload. # # Usage (from smom-dbis-138/): # ./scripts/deployment/export-bsc-cw-verification-artifacts.sh # ./scripts/deployment/export-bsc-cw-verification-artifacts.sh --verify # also submit forge verify (cWBNB may fail) # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" REPO_ROOT="$(cd "$PROJECT_ROOT/.." && pwd)" OUT_DIR="$REPO_ROOT/reports/status/bsc-cw-verification" VERIFY=0 [[ "${1:-}" == "--verify" ]] && VERIFY=1 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" fi [[ -n "${ETHERSCAN_API_KEY:-}" ]] || { echo "ETHERSCAN_API_KEY required" >&2; exit 1; } ADMIN="${CW_VERIFY_ADMIN:-}" if [[ -z "$ADMIN" && -n "${PRIVATE_KEY:-}" ]]; then ADMIN="$(cast wallet address --private-key "$PRIVATE_KEY")" fi [[ -n "$ADMIN" ]] || { echo "CW_VERIFY_ADMIN or PRIVATE_KEY required" >&2; exit 1; } CWBNB="${CWBNB_BSC:-0x179034a08ac2c9c35d2e41239f68c79dca6f18fa}" CONTRACT_PATH="contracts/tokens/CompliantWrappedToken.sol:CompliantWrappedToken" CTOR="$(cast abi-encode 'constructor(string,string,uint8,address)' \ 'Wrapped BNB Gas (Compliant)' 'cWBNB' 18 "$ADMIN")" mkdir -p "$OUT_DIR" echo "==> Pinned profile: bsc_tokens_verify (see foundry.toml + config/bsc-cw-verify-profile.v1.json)" echo "==> Scoped build: scripts/forge/scope.sh build tokens" export FOUNDRY_PROFILE=bsc_tokens_verify export FOUNDRY_SRC="contracts/tokens,contracts/interfaces" export FOUNDRY_OUT="out/scopes/tokens" export FOUNDRY_CACHE_PATH="cache/scopes/tokens" bash "$PROJECT_ROOT/scripts/forge/scope.sh" build tokens echo "==> Export Standard JSON Input (upload at https://bscscan.com/verifyContract solidity-standard-json-input)" JSON_OUT="$OUT_DIR/CompliantWrappedToken_cWBNB_standard_input.json" # forge may print ERROR log lines before the JSON object on stdout — keep only the JSON line forge verify-contract \ --chain bsc \ --num-of-optimizations 200 \ --via-ir \ --evm-version london \ --compiler-version "0.8.20+commit.a1b79de6" \ --constructor-args "$CTOR" \ --show-standard-json-input \ "$CWBNB" \ "$CONTRACT_PATH" 2>/dev/null | awk '/^\{/{buf=$0} END{if(buf) print buf}' | jq -c . > "$JSON_OUT" [[ -s "$JSON_OUT" ]] || { echo "Failed to write Standard JSON (forge output empty?)" >&2; exit 5; } echo " wrote $JSON_OUT" echo "==> Export flattened source (reference only; prefer Standard-JSON for via-ir)" forge flatten contracts/tokens/CompliantWrappedToken.sol > "$OUT_DIR/CompliantWrappedToken_cWBNB_flattened.sol" 2>/dev/null echo " wrote $OUT_DIR/CompliantWrappedToken_cWBNB_flattened.sol" RPC="${BSC_RPC_URL:-${BSC_MAINNET_RPC:-}}" if [[ -n "$RPC" ]]; then ONCHAIN="$(cast code "$CWBNB" --rpc-url "$RPC" | sed 's/^0x//' | tr '[:upper:]' '[:lower:]')" BUILT="$(jq -r .deployedBytecode.object "$FOUNDRY_OUT/CompliantWrappedToken.sol/CompliantWrappedToken.json" | sed 's/^0x//' | tr '[:upper:]' '[:lower:]')" MATCH=false [[ "$ONCHAIN" == "$BUILT" ]] && MATCH=true FIRST_DIFF_OFFSET="" if [[ "$MATCH" == false ]]; then FIRST_DIFF_OFFSET="$(python3 -c " on, b = '''$ONCHAIN''', '''$BUILT''' n = min(len(on), len(b)) // 2 for i in range(n): if on[2*i:2*i+2] != b[2*i:2*i+2]: print(i) break else: print(n if len(on)//2 != len(b)//2 else '') " 2>/dev/null || true)" fi jq -n \ --arg generatedAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --arg address "$CWBNB" \ --argjson onChainRuntimeBytes $(( ${#ONCHAIN} / 2 )) \ --argjson pinnedBuildRuntimeBytes $(( ${#BUILT} / 2 )) \ --argjson runtimeBytecodeMatch "$([[ "$MATCH" == true ]] && echo true || echo false)" \ --arg firstRuntimeDiffOffset "${FIRST_DIFF_OFFSET:-null}" \ --arg constructorArgs "$CTOR" \ --arg foundryProfile "bsc_tokens_verify" \ --arg standardJson "$JSON_OUT" \ '{ generatedAt: $generatedAt, address: $address, onChainRuntimeBytes: $onChainRuntimeBytes, pinnedBuildRuntimeBytes: $pinnedBuildRuntimeBytes, runtimeBytecodeMatch: $runtimeBytecodeMatch, firstRuntimeDiffOffset: (if $firstRuntimeDiffOffset == "" or $firstRuntimeDiffOffset == "null" then null else ($firstRuntimeDiffOffset|tonumber) end), constructorArgs: $constructorArgs, foundryProfile: $foundryProfile, standardJson: $standardJson }' > "$OUT_DIR/cWBNB_bytecode_compare.json" echo " wrote $OUT_DIR/cWBNB_bytecode_compare.json" fi cat > "$OUT_DIR/README.md" < \`\`\` ## If verification still fails On-chain runtime bytecode is **~45 bytes longer** than the current pinned scoped build (see \`cWBNB_bytecode_compare.json\`). That indicates deploy used a slightly different artifact (solc patch, source revision, or metadata), not just constructor naming. Options: - Locate the deploy transaction and pin the exact \`solc\` commit from that date. - Compare \`git log contracts/tokens/CompliantWrappedToken.sol\` at deploy block. - Redeploy cWBNB only if a new address is acceptable. ## Automated verify (may fail until bytecode pin found) \`\`\`bash cd smom-dbis-138 FOUNDRY_PROFILE=bsc_tokens_verify \\ FOUNDRY_SRC=contracts/tokens FOUNDRY_OUT=out/scopes/tokens \\ forge verify-contract --chain bsc --num-of-optimizations 200 --via-ir --evm-version london \\ --compiler-version 0.8.20+commit.a1b79de6 \\ --etherscan-api-key "\$ETHERSCAN_API_KEY" \\ --constructor-args "\$CTOR" \\ $CWBNB $CONTRACT_PATH \`\`\` EOF echo " wrote $OUT_DIR/README.md" if [[ "$VERIFY" -eq 1 ]]; then echo "==> forge verify-contract (automated)" set +e forge verify-contract \ --chain bsc \ --num-of-optimizations 200 \ --via-ir \ --evm-version london \ --compiler-version "0.8.20+commit.a1b79de6" \ --etherscan-api-key "${ETHERSCAN_API_KEY}" \ --constructor-args "$CTOR" \ --watch --rpc-timeout 120 \ "$CWBNB" \ "$CONTRACT_PATH" set -e fi echo "" echo "Done. Artifacts: $OUT_DIR/"