2026-02-12 15:46:57 -08:00
#!/usr/bin/env bash
# Verify deployed contracts on Blockscout (Chain 138)
# Usage: ./scripts/verify-contracts-blockscout.sh [--only contract1,contract2] [--skip contract3]
2026-03-27 18:51:02 -07:00
# Before each verify, uses `cast code` on RPC to skip EOAs / empty code (avoids Blockscout
# "not a smart contract" noise for addresses like WETH10 when nothing is deployed there).
# Set VERIFY_SKIP_BYTECODE_CHECK=1 to attempt forge verify even when code lookup fails or is empty.
chore: sync workspace — configs, docs, scripts, CI, pnpm, submodules
- 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
2026-04-21 22:01:33 -07:00
# Version: 2026-04-22 (DODO PMM + TransactionMirror: explicit constructor args + deploy profile)
2026-02-12 15:46:57 -08:00
set -euo pipefail
SCRIPT_DIR = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " && pwd ) "
PROJECT_ROOT = " $( cd " $SCRIPT_DIR /.. " && pwd ) "
2026-03-02 11:37:34 -08:00
# Load env and contract addresses (RPC_URL_138, IP_BLOCKSCOUT, ORACLE_PROXY, CCIP_SENDER, etc.)
if [ [ -f " ${ SCRIPT_DIR } /lib/load-project-env.sh " ] ] ; then
source " ${ SCRIPT_DIR } /lib/load-project-env.sh "
fi
2026-02-12 15:46:57 -08:00
[ [ -f " ${ PROJECT_ROOT } /config/contract-addresses.conf " ] ] && source " ${ PROJECT_ROOT } /config/contract-addresses.conf " 2>/dev/null || true
SMOM = " ${ SMOM_DIR :- ${ PROJECT_ROOT } /smom-dbis-138 } "
ALLTRA = " ${ PROJECT_ROOT } /alltra-lifi-settlement "
2026-02-21 15:46:06 -08:00
RPC = " ${ RPC_URL_138 :- http : //192.168.11.211 : 8545 } "
2026-02-12 15:46:57 -08:00
IP_BLOCKSCOUT = " ${ IP_BLOCKSCOUT :- 192 .168.11.140 } "
2026-03-02 11:37:34 -08:00
# Forge sends POST to this URL; proxy adds module/action and forwards to Blockscout
2026-02-12 15:46:57 -08:00
VERIFIER_URL = " ${ FORGE_VERIFIER_URL :- http : //127.0.0.1 : 3080 /api } "
# Parse --only and --skip
ONLY_LIST = ""
SKIP_LIST = ""
while [ [ $# -gt 0 ] ] ; do
case " $1 " in
--only) ONLY_LIST = " ${ 2 :- } " ; shift 2 ; ;
--skip) SKIP_LIST = " ${ 2 :- } " ; shift 2 ; ;
*) shift ; ;
esac
done
should_verify( ) {
local name = " $1 "
[ [ -n " $ONLY_LIST " ] ] && [ [ " , ${ ONLY_LIST } , " != *" , ${ name } , " * ] ] && return 1
[ [ -n " $SKIP_LIST " ] ] && [ [ " , ${ SKIP_LIST } , " = *" , ${ name } , " * ] ] && return 1
return 0
}
cd " $SMOM "
2026-03-27 18:51:02 -07:00
# Returns 0 if address has non-empty runtime bytecode on RPC, 1 otherwise (or if check skipped).
has_contract_bytecode( ) {
local addr = " $1 "
local code
[ [ " ${ VERIFY_SKIP_BYTECODE_CHECK :- 0 } " = = "1" ] ] && return 0
command -v cast >/dev/null 2>& 1 || return 0
code = $( cast code " $addr " --rpc-url " $RPC " 2>/dev/null | tr -d '\n\r \t' | tr '[:upper:]' '[:lower:]' ) || true
[ [ -n " $code " && " $code " != "0x" && " $code " != "0x0" ] ]
}
2026-02-12 15:46:57 -08:00
verify_one( ) {
local addr = " $1 " contract = " $2 " path = " $3 "
echo " Verifying $contract at $addr ... "
2026-03-27 18:51:02 -07:00
if ! has_contract_bytecode " $addr " ; then
echo " skip: no contract bytecode at $addr on RPC (EOA or undeployed; verify would fail in Blockscout) "
return 0
fi
2026-02-12 15:46:57 -08:00
if forge verify-contract " $addr " " $path " \
--chain-id 138 \
--verifier blockscout \
--verifier-url " $VERIFIER_URL " \
--rpc-url " $RPC " \
--flatten 2>& 1; then
echo " OK"
else
echo " (skip: may already be verified, or path/Solidity version mismatch - check contract path and compiler version)"
fi
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Blockscout Contract Verification (Chain 138)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
2026-03-02 11:37:34 -08:00
# Canonical Chain 138 addresses (prefer smom-dbis-138/.env and config; fallbacks from CONTRACT_ADDRESSES_REFERENCE / reconcile-env)
2026-02-21 15:46:06 -08:00
ADDR_MULTICALL = " ${ ADDR_MULTICALL :- 0xF4AA429BE277d1a1a1A744C9e5B3aD821a9b96f7 } "
2026-03-02 11:37:34 -08:00
ADDR_ORACLE_AGGREGATOR = " ${ AGGREGATOR_ADDRESS :- ${ ADDR_ORACLE_AGGREGATOR :- 0x99b3511a2d315a497c8112c1fdd8d508d4b1e506 } } "
ADDR_ORACLE_PROXY = " ${ ORACLE_PROXY :- ${ ADDR_ORACLE_PROXY :- 0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 } } "
2026-02-21 15:46:06 -08:00
ADDR_MULTISIG = " ${ ADDR_MULTISIG :- 0xb9E29cFa1f89d369671E640d0BB3aD94Cab43965 } "
ADDR_CCIP_RECEIVER = " ${ ADDR_CCIP_RECEIVER :- 0xC12236C03b28e675d376774FCE2C2C052488430F } "
ADDR_VOTING = " ${ ADDR_VOTING :- 0x022267b26400114aF01BaCcb92456Fe36cfccD93 } "
2026-03-02 11:37:34 -08:00
ADDR_CCIP_SENDER = " ${ CCIP_SENDER :- ${ ADDR_CCIP_SENDER :- 0x105F8A15b819948a89153505762444Ee9f324684 } } "
ADDR_CCIPWETH10 = " ${ CCIPWETH10_BRIDGE_CHAIN138 :- ${ ADDR_CCIPWETH10_BRIDGE :- 0xe0E93247376aa097dB308B92e6Ba36bA015535D0 } } "
ADDR_CCIPWETH9 = " ${ CCIPWETH9_BRIDGE_CHAIN138 :- ${ ADDR_CCIPWETH9_BRIDGE :- 0x971cD9D156f193df8051E48043C476e53ECd4693 } } "
ADDR_WETH10 = " ${ WETH10_CHAIN138 :- 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f } "
chore: sync workspace — configs, docs, scripts, CI, pnpm, submodules
- 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
2026-04-21 22:01:33 -07:00
# Used early for AccessControl admin resolution (mirror admin often matches DODO admin on Chain 138).
ADDR_TX_MIRROR = " ${ TRANSACTION_MIRROR_ADDRESS :- } "
2026-02-12 15:46:57 -08:00
2026-03-02 11:37:34 -08:00
should_verify WETH10 && verify_one " $ADDR_WETH10 " "WETH10" "contracts/tokens/WETH10.sol:WETH10"
2026-02-21 15:46:06 -08:00
should_verify Multicall && verify_one " $ADDR_MULTICALL " "Multicall" "contracts/utils/Multicall.sol:Multicall"
should_verify Aggregator && verify_one " $ADDR_ORACLE_AGGREGATOR " "Aggregator" "contracts/oracle/Aggregator.sol:Aggregator"
2026-02-12 15:46:57 -08:00
should_verify Proxy && verify_one " $ADDR_ORACLE_PROXY " "Proxy" "contracts/oracle/Proxy.sol:Proxy"
2026-02-21 15:46:06 -08:00
should_verify MultiSig && verify_one " $ADDR_MULTISIG " "MultiSig" "contracts/governance/MultiSig.sol:MultiSig"
should_verify CCIPReceiver && verify_one " $ADDR_CCIP_RECEIVER " "CCIPReceiver" "contracts/ccip/CCIPReceiver.sol:CCIPReceiver"
should_verify Voting && verify_one " $ADDR_VOTING " "Voting" "contracts/governance/Voting.sol:Voting"
should_verify CCIPSender && verify_one " $ADDR_CCIP_SENDER " "CCIPSender" "contracts/ccip/CCIPSender.sol:CCIPSender"
2026-02-12 15:46:57 -08:00
should_verify CCIPWETH10Bridge && verify_one " $ADDR_CCIPWETH10 " "CCIPWETH10Bridge" "contracts/ccip/CCIPWETH10Bridge.sol:CCIPWETH10Bridge"
should_verify CCIPWETH9Bridge && verify_one " $ADDR_CCIPWETH9 " "CCIPWETH9Bridge" "contracts/ccip/CCIPWETH9Bridge.sol:CCIPWETH9Bridge"
if [ [ -d " $ALLTRA " ] ] ; then
ADDR_MERCHANT = " ${ ADDR_MERCHANT_SETTLEMENT :- 0x16D9A2cB94A0b92721D93db4A6Cd8023D3338800 } "
ADDR_ESCROW = " ${ ADDR_WITHDRAWAL_ESCROW :- 0xe77cb26eA300e2f5304b461b0EC94c8AD6A7E46D } "
should_verify MerchantSettlementRegistry && { echo " Verifying MerchantSettlementRegistry at $ADDR_MERCHANT ... " ; ( cd " $ALLTRA " && forge verify-contract " $ADDR_MERCHANT " "contracts/settlement/MerchantSettlementRegistry.sol:MerchantSettlementRegistry" --chain-id 138 --verifier blockscout --verifier-url " $VERIFIER_URL " --rpc-url " $RPC " --flatten 2>& 1) && echo " OK" || echo " (skip)" ; }
should_verify WithdrawalEscrow && { echo " Verifying WithdrawalEscrow at $ADDR_ESCROW ... " ; ( cd " $ALLTRA " && forge verify-contract " $ADDR_ESCROW " "contracts/settlement/WithdrawalEscrow.sol:WithdrawalEscrow" --chain-id 138 --verifier blockscout --verifier-url " $VERIFIER_URL " --rpc-url " $RPC " --flatten 2>& 1) && echo " OK" || echo " (skip)" ; }
fi
chore: sync workspace — configs, docs, scripts, CI, pnpm, submodules
- 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
2026-04-21 22:01:33 -07:00
# Optional DODO PMM + TransactionMirror (addresses from smom-dbis-138/.env).
# Uses explicit constructor args read from chain (immutables + AccessControl admin via hasRole).
# Bytecode match: FOUNDRY_PROFILE=deploy (optimizer 100, via_ir, cancun) — override with VERIFY_CHAIN138_FOUNDRY_PROFILE=default if your deployment used the default profile instead.
DEFAULT_ADMIN_ROLE_ZERO = "0x0000000000000000000000000000000000000000000000000000000000000000"
# Resolve DEFAULT_ADMIN_ROLE holder for AccessControl (tries env candidates, then TransactionMirror admin()).
resolve_access_control_admin( ) {
local contract = " $1 "
local rpc = " $2 "
local role = " $DEFAULT_ADMIN_ROLE_ZERO "
local cand adm hr
local -a cands = ( )
[ [ -n " ${ DODO_INTEGRATION_ADMIN :- } " ] ] && cands += ( " ${ DODO_INTEGRATION_ADMIN } " )
[ [ -n " ${ DODO_PMM_PROVIDER_ADMIN :- } " ] ] && cands += ( " ${ DODO_PMM_PROVIDER_ADMIN } " )
if [ [ -n " ${ ADDR_TX_MIRROR :- } " ] ] ; then
adm = $( cast call " ${ ADDR_TX_MIRROR } " "admin()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || true
[ [ -n " $adm " && " $adm " != "0x0000000000000000000000000000000000000000" ] ] && cands += ( " $adm " )
fi
[ [ -n " ${ GOVERNANCE_CONTROLLER :- } " ] ] && cands += ( " ${ GOVERNANCE_CONTROLLER } " )
[ [ -n " ${ ADDR_MULTISIG :- } " ] ] && cands += ( " ${ ADDR_MULTISIG } " )
for cand in " ${ cands [@] } " ; do
[ [ -z " $cand " ] ] && continue
hr = $( cast call " $contract " "hasRole(bytes32,address)(bool)" " $role " " $cand " --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || continue
if [ [ " $hr " = = "true" ] ] ; then
echo " $cand "
return 0
fi
done
return 1
}
dodo_integration_constructor_hex( ) {
local int = " $1 " rpc = " $2 " admin = " $3 "
local dvm appr usdt usdc cusdt cusdc
dvm = $( cast call " $int " "dodoVendingMachine()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
appr = $( cast call " $int " "dodoApprove()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
usdt = $( cast call " $int " "officialUSDT()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
usdc = $( cast call " $int " "officialUSDC()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
cusdt = $( cast call " $int " "compliantUSDT()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
cusdc = $( cast call " $int " "compliantUSDC()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
cast abi-encode 'constructor(address,address,address,address,address,address,address)' \
" $admin " " $dvm " " $appr " " $usdt " " $usdc " " $cusdt " " $cusdc " 2>/dev/null | tr -d '\n\r \t'
}
dodo_provider_constructor_hex( ) {
local prov = " $1 " rpc = " $2 " admin = " $3 "
local di
di = $( cast call " $prov " "dodoIntegration()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
cast abi-encode 'constructor(address,address)' " $di " " $admin " 2>/dev/null | tr -d '\n\r \t'
}
transaction_mirror_constructor_hex( ) {
local mir = " $1 " rpc = " $2 "
local adm
adm = $( cast call " $mir " "admin()(address)" --rpc-url " $rpc " 2>/dev/null | tr -d '\n\r \t' ) || return 1
cast abi-encode 'constructor(address)' " $adm " 2>/dev/null | tr -d '\n\r \t'
}
verify_one_explicit( ) {
local addr = " $1 " path = " $2 " name = " $3 " ctor_hex = " $4 "
local profile = " ${ VERIFY_CHAIN138_FOUNDRY_PROFILE :- deploy } "
echo " Verifying $name at $addr (explicit ctor, FOUNDRY_PROFILE= $profile )... "
if ! has_contract_bytecode " $addr " ; then
echo " skip: no contract bytecode at $addr "
return 0
fi
if [ [ -z " $ctor_hex " ] ] ; then
echo " skip: could not build constructor args (cast/RPC failed)"
return 0
fi
if FOUNDRY_PROFILE = " $profile " forge verify-contract " $addr " " $path " \
--chain-id 138 \
--constructor-args " $ctor_hex " \
--verifier blockscout \
--verifier-url " $VERIFIER_URL " \
--rpc-url " $RPC " \
--skip-is-verified-check 2>& 1; then
echo " OK"
else
echo " (skip: mismatch or already verified — try VERIFY_CHAIN138_FOUNDRY_PROFILE=default or confirm deploy profile in foundry.toml)"
fi
}
ADDR_DODO_INTEGRATION = " ${ DODO_PMM_INTEGRATION_ADDRESS :- ${ DODO_PMM_INTEGRATION :- ${ CHAIN_138_DODO_PMM_INTEGRATION :- } } } "
ADDR_DODO_PROVIDER = " ${ DODO_PMM_PROVIDER_ADDRESS :- ${ DODO_PMM_PROVIDER :- } } "
if [ [ -n " $ADDR_DODO_INTEGRATION " ] ] && should_verify DODOPMMIntegration; then
admin_i = $( resolve_access_control_admin " $ADDR_DODO_INTEGRATION " " $RPC " || true )
if [ [ -z " $admin_i " ] ] ; then
echo " Verifying DODOPMMIntegration at $ADDR_DODO_INTEGRATION — skip: could not resolve AccessControl admin (set DODO_INTEGRATION_ADMIN or TRANSACTION_MIRROR_ADDRESS for hasRole probe) "
else
enc = $( dodo_integration_constructor_hex " $ADDR_DODO_INTEGRATION " " $RPC " " $admin_i " || true )
verify_one_explicit " $ADDR_DODO_INTEGRATION " "contracts/dex/DODOPMMIntegration.sol:DODOPMMIntegration" "DODOPMMIntegration" " ${ enc :- } "
fi
fi
if [ [ -n " $ADDR_DODO_PROVIDER " ] ] && should_verify DODOPMMProvider; then
admin_p = $( resolve_access_control_admin " $ADDR_DODO_PROVIDER " " $RPC " || true )
if [ [ -z " $admin_p " ] ] ; then
echo " Verifying DODOPMMProvider at $ADDR_DODO_PROVIDER — skip: could not resolve AccessControl admin (set DODO_PMM_PROVIDER_ADMIN or TRANSACTION_MIRROR_ADDRESS) "
else
enc = $( dodo_provider_constructor_hex " $ADDR_DODO_PROVIDER " " $RPC " " $admin_p " || true )
verify_one_explicit " $ADDR_DODO_PROVIDER " "contracts/liquidity/providers/DODOPMMProvider.sol:DODOPMMProvider" "DODOPMMProvider" " ${ enc :- } "
fi
fi
if [ [ -n " $ADDR_TX_MIRROR " ] ] && should_verify TransactionMirror; then
enc = $( transaction_mirror_constructor_hex " $ADDR_TX_MIRROR " " $RPC " || true )
verify_one_explicit " $ADDR_TX_MIRROR " "contracts/mirror/TransactionMirror.sol:TransactionMirror" "TransactionMirror" " ${ enc :- } "
fi
# CompliantFiatToken: one deployment per currency with distinct constructor args — verify per token in the Blockscout UI or add scripted entries when addresses are enumerated in env.
2026-02-12 15:46:57 -08:00
echo ""
echo " Done. Check http:// ${ IP_BLOCKSCOUT } or https://explorer.d-bis.org for verification status. "