Compare commits

..

1 Commits

Author SHA1 Message Date
defiQUG
87d1645461 Refresh MEV ops runbooks 2026-04-16 12:58:27 -07:00
12 changed files with 8 additions and 1606 deletions

5
.gitignore vendored
View File

@@ -68,7 +68,6 @@ out/
# Temporary files
*.tmp
*.temp
.tmp-*.cjs
# Environment backup files (Security: Prevent committing backup files with secrets)
*.env.backup
@@ -98,10 +97,6 @@ reports/status/live_inventory_*.json
reports/status/hardware_poll_*.txt
reports/status/lxc_cluster_health_*.json
reports/status/lxc_cluster_health_*.txt
reports/status/*runtime-env*.env
reports/status/*operator-rpcs*.env
reports/status/*_runtime.env
reports/status/*.tar.gz
# Wormhole AI docs mirror (sync with scripts/doc/sync-wormhole-ai-resources.sh; keep manifest.json committable)
third-party/wormhole-ai-docs/**

View File

@@ -1,149 +0,0 @@
## Chain 138 Blockscout Route and Flash Lineage Report
Date: 2026-04-16
### Summary
- The Chain `138` Blockscout publication set is now fully closed.
- `dodo_v3_core`, `flash_infra`, `native_v2`, and `route_execution_stack` are all `verified` on the public explorer.
- The historical route and flash deployments were not produced by the current local default build outputs.
- The exact recovered historical source/build profile for the deployed route and flash families is:
- source family rooted at commit `6817f53`
- compiler `solc 0.8.20`
- `evm_version = london`
- `optimizer_runs = 200`
- `via_ir = false`
### Important provenance note
The Foundry broadcast files for the route and flash deployments record:
- `commit: 7678218`
That field is not the true source commit for the deployed route and flash contract families.
The exact source lineage was recovered by:
1. tracing the relevant contract families through `git log`
2. testing historical worktrees directly
3. comparing candidate creation bytecode plus ABI-encoded constructor args against the original broadcast `transaction.input`
Recovered deployment family:
- `6817f53`
### Route execution lineage
Broadcast files:
- `smom-dbis-138/broadcast/DeployEnhancedSwapRouterV2.s.sol/138/run-latest.json`
- `smom-dbis-138/broadcast/DeployEnhancedSwapRouterV2.s.sol/138/run-1775195187069.json`
Recovered historical build/profile:
- source root: `6817f53`
- compiler: `0.8.20`
- `evm_version = london`
- `optimizer_runs = 200`
- `via_ir = false`
Recovered live lineage and publication outcome:
- `EnhancedSwapRouterV2`
- address: `0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce`
- create tx: `0x30e68f519243377006e93dd82823305729a1ede5f03e744e27e5d57c7b6766a7`
- exact historical creation bytecode/input match: yes
- explorer verification: `verified`
- `IntentBridgeCoordinatorV2`
- address: `0x7D0022B7e8360172fd9C0bB6778113b7Ea3674E7`
- create tx: `0x73fc5f883eda73370e3a4f0d800453e095cee07ef5f37793ed4576f47b4fa5fb`
- exact historical creation bytecode/input match: yes
- explorer verification: `verified`
- `DodoRouteExecutorAdapter`
- address: `0x88495B3dccEA93b0633390fDE71992683121Fa62`
- create tx: `0xc574dde65e90421ed1ff5600c1f9dd71a6b8afb5a1b8416b1dde38bd2961120c`
- exact historical runtime lineage match used as canary: yes
- explorer verification: `verified`
- `DodoV3RouteExecutorAdapter`
- address: `0x9Cb97adD29c52e3B81989BcA2E33D46074B530eF`
- explorer verification: `verified`
- `UniswapV3RouteExecutorAdapter`
- address: `0x960D6db4E78705f82995690548556fb2266308EA`
- explorer verification: `verified`
- `BalancerRouteExecutorAdapter`
- address: `0x4E1B71B69188Ab45021c797039b4887a4924157A`
- explorer verification: `verified`
- `CurveRouteExecutorAdapter`
- address: `0x5f0E07071c41ACcD2A1b1032D3bd49b323b9ADE6`
- explorer verification: `verified`
- `OneInchRouteExecutorAdapter`
- address: `0x8168083d29b3293F215392A49D16e7FeF4a02600`
- explorer verification: `verified`
- `Chain138PilotUniswapV3Router`
- address: `0xD164D9cCfAcf5D9F91698f296aE0cd245D964384`
- explorer verification: `verified`
- `Chain138PilotBalancerVault`
- address: `0x96423d7C1727698D8a25EbFB88131e9422d1a3C3`
- explorer verification: `verified`
- `Chain138PilotCurve3Pool`
- address: `0xE440Ec15805BE4C7BabCD17A63B8C8A08a492e0f`
- explorer verification: `verified`
- `Chain138PilotOneInchAggregationRouter`
- address: `0x500B84b1Bc6F59C1898a5Fe538eA20A758757A4F`
- explorer verification: `verified`
### Flash-infrastructure lineage
Broadcast file:
- `smom-dbis-138/broadcast/DeployCrossChainFlashInfrastructure.s.sol/138/run-latest.json`
Recovered historical build/profile:
- source root: `6817f53`
- compiler: `0.8.20`
- `evm_version = london`
- `optimizer_runs = 200`
- `via_ir = false`
Recovered live lineage and publication outcome:
- `UniversalCCIPFlashBridgeAdapter`
- address: `0xBe9e0B2d4cF6A3b2994d6f2f0904D2B165eB8ffC`
- create tx: `0x8cc4ba611a3d0a6e880f9e21f6390f67aadd6a234df1dc2828788ad775849844`
- exact historical creation bytecode/input match: yes
- explorer verification: `verified`
- `CrossChainFlashRepayReceiver`
- address: `0xD084b68cB4B1ef2cBA09CF99FB1B6552fd9b4859`
- create tx: `0xf1fc28c10956cf368ebfc3d1cdd3150caf8aceacae86964f547a91e66a801e33`
- exact historical creation bytecode/input match: yes
- explorer verification: `verified`
- `CrossChainFlashVaultCreditReceiver`
- address: `0x89F7a1fcbBe104BeE96Da4b4b6b7d3AF85f7E661`
- create tx: `0x085e1cad6e8fbe6642db420563b7b75194a88ec6649fae99cd940c2a894ec1ad`
- exact historical creation bytecode/input match: yes
- explorer verification: `verified`
### DODO publication status
The earlier Blockscout insert/materialization blocker is closed.
Published now:
- `D3Oracle`
- `D3Vault`
- `DODOApprove`
- `DODOApproveProxy`
- `D3MMFactory`
- `D3Proxy`
All six `dodo_v3_core` contracts are fully verified on the public explorer.
### Current truthful end state
- `dodo_v3_core`: `6/6 verified`
- `flash_infra`: `3/3 verified`
- `native_v2`: `4/4 verified`
- `route_execution_stack`: `12/12 verified`
The historical build/profile recovery work is complete for the deployed route contracts and flash trio, and the dedicated verifiers have now been rerun successfully against those recovered artifacts.

View File

@@ -1,48 +0,0 @@
# Chain 138 Deployed Smart Contract Verification Status
This report is generated from the canonical Chain `138` inventory in `config/smart-contracts-master.json`, on-chain bytecode checks against the Core RPC, and Blockscout smart-contract metadata from the internal explorer API.
## Summary
| Group | Total | Deployed | Verified | Bytecode only | Pending |
| --- | ---: | ---: | ---: | ---: | ---: |
| `dodo_v3_core` | 6 | 6 | 6 | 0 | 0 |
| `flash_infra` | 3 | 3 | 3 | 0 | 0 |
| `native_v2` | 4 | 4 | 4 | 0 | 0 |
| `route_execution_stack` | 12 | 12 | 12 | 0 | 0 |
## Inventory
| Group | Label | Address | Deployed | Verification | Blockscout name | Compiler |
| --- | --- | --- | --- | --- | --- | --- |
| `dodo_v3_core` | `D3Oracle` | `0xD7459aEa8bB53C83a1e90262777D730539A326F0` | yes | `verified` | `D3Oracle` | `v0.8.16+commit.07a7930e` |
| `dodo_v3_core` | `D3Vault` | `0x42b6867260Fb9eE6d09B7E0233A1fAD65D0133D1` | yes | `verified` | `D3Vault` | `v0.8.16+commit.07a7930e` |
| `dodo_v3_core` | `DODOApprove` | `0xbF8D5CB7E8F333CA686a27374Ae06F5dfd772E9E` | yes | `verified` | `DODOApprove` | `v0.8.16+commit.07a7930e` |
| `dodo_v3_core` | `DODOApproveProxy` | `0x08d764c03C42635d8ef9046752b5694243E21Fe9` | yes | `verified` | `DODOApproveProxy` | `v0.8.16+commit.07a7930e` |
| `dodo_v3_core` | `D3MMFactory` | `0x78470C7d2925B6738544E2DD4FE7c07CcA21AC31` | yes | `verified` | `D3MMFactory` | `v0.8.16+commit.07a7930e` |
| `dodo_v3_core` | `D3Proxy` | `0xc9a11abB7C63d88546Be24D58a6d95e3762cB843` | yes | `verified` | `D3Proxy` | `v0.8.16+commit.07a7930e` |
| `flash_infra` | `UniversalCCIPFlashBridgeAdapter` | `0xBe9e0B2d4cF6A3b2994d6f2f0904D2B165eB8ffC` | yes | `verified` | `UniversalCCIPFlashBridgeAdapter` | `v0.8.20+commit.a1b79de6` |
| `flash_infra` | `CrossChainFlashRepayReceiver` | `0xD084b68cB4B1ef2cBA09CF99FB1B6552fd9b4859` | yes | `verified` | `CrossChainFlashRepayReceiver` | `v0.8.20+commit.a1b79de6` |
| `flash_infra` | `CrossChainFlashVaultCreditReceiver` | `0x89F7a1fcbBe104BeE96Da4b4b6b7d3AF85f7E661` | yes | `verified` | `CrossChainFlashVaultCreditReceiver` | `v0.8.20+commit.a1b79de6` |
| `native_v2` | `UniswapV2Factory` | `0x0C30F6e67Ab3667fCc2f5CEA8e274ef1FB920279` | yes | `verified` | `UniswapV2Factory` | `v0.5.16+commit.9c3226ce` |
| `native_v2` | `UniswapV2Router` | `0x3019A7fDc76ba7F64F18d78e66842760037ee638` | yes | `verified` | `UniswapV2Router02` | `v0.6.6+commit.6c089d02` |
| `native_v2` | `SushiSwapFactory` | `0x2871207ff0d56089D70c0134d33f1291B6Fce0BE` | yes | `verified` | `UniswapV2Factory` | `v0.6.12+commit.27d51765` |
| `native_v2` | `SushiSwapRouter` | `0xB37b93D38559f53b62ab020A14919f2630a1aE34` | yes | `verified` | `UniswapV2Router02` | `v0.6.12+commit.27d51765` |
| `route_execution_stack` | `EnhancedSwapRouterV2` | `0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce` | yes | `verified` | `EnhancedSwapRouterV2` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `IntentBridgeCoordinatorV2` | `0x7D0022B7e8360172fd9C0bB6778113b7Ea3674E7` | yes | `verified` | `IntentBridgeCoordinatorV2` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `DodoRouteExecutorAdapter` | `0x88495B3dccEA93b0633390fDE71992683121Fa62` | yes | `verified` | `DodoRouteExecutorAdapter` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `DodoV3RouteExecutorAdapter` | `0x9Cb97adD29c52e3B81989BcA2E33D46074B530eF` | yes | `verified` | `DodoV3RouteExecutorAdapter` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `UniswapV3RouteExecutorAdapter` | `0x960D6db4E78705f82995690548556fb2266308EA` | yes | `verified` | `UniswapV3RouteExecutorAdapter` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `BalancerRouteExecutorAdapter` | `0x4E1B71B69188Ab45021c797039b4887a4924157A` | yes | `verified` | `BalancerRouteExecutorAdapter` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `CurveRouteExecutorAdapter` | `0x5f0E07071c41ACcD2A1b1032D3bd49b323b9ADE6` | yes | `verified` | `CurveRouteExecutorAdapter` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `OneInchRouteExecutorAdapter` | `0x8168083d29b3293F215392A49D16e7FeF4a02600` | yes | `verified` | `OneInchRouteExecutorAdapter` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `PilotUniswapV3Router` | `0xD164D9cCfAcf5D9F91698f296aE0cd245D964384` | yes | `verified` | `Chain138PilotUniswapV3Router` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `PilotBalancerVault` | `0x96423d7C1727698D8a25EbFB88131e9422d1a3C3` | yes | `verified` | `Chain138PilotBalancerVault` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `PilotCurve3Pool` | `0xE440Ec15805BE4C7BabCD17A63B8C8A08a492e0f` | yes | `verified` | `Chain138PilotCurve3Pool` | `v0.8.20+commit.a1b79de6` |
| `route_execution_stack` | `PilotOneInchRouter` | `0x500B84b1Bc6F59C1898a5Fe538eA20A758757A4F` | yes | `verified` | `Chain138PilotOneInchAggregationRouter` | `v0.8.20+commit.a1b79de6` |
## Notes
- `verified` means Blockscout currently exposes both a contract name and compiler version.
- `bytecode-only` means the address is known to the explorer, but source metadata has not materialized yet.
- `pending` means the contract is deployed in the canonical inventory, but the current Blockscout API response does not yet expose verification metadata.

View File

@@ -62,12 +62,12 @@ Default CT identity:
The provisioner creates an unprivileged Debian 12 CT with:
- `16` vCPU
- `32 GiB` RAM
- `8 GiB` swap
- `72 GiB` RAM
- `0 GiB` swap by default
- `200 GiB` rootfs
- `nesting=1,keyctl=1`
These defaults are intentionally generous for the first contained deployment and can be tuned later.
These defaults reflect the current stable allocation for CT `2421` after live pressure remediation. Keep the backend on `72 GiB` unless host capacity becomes constrained enough to justify a deliberate retune.
## 3. Paths inside the backend CT

View File

@@ -1,259 +0,0 @@
{
"rows": [
{
"group": "dodo_v3_core",
"label": "D3Oracle",
"address": "0xD7459aEa8bB53C83a1e90262777D730539A326F0",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "D3Oracle",
"compilerVersion": "v0.8.16+commit.07a7930e"
},
{
"group": "dodo_v3_core",
"label": "D3Vault",
"address": "0x42b6867260Fb9eE6d09B7E0233A1fAD65D0133D1",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "D3Vault",
"compilerVersion": "v0.8.16+commit.07a7930e"
},
{
"group": "dodo_v3_core",
"label": "DODOApprove",
"address": "0xbF8D5CB7E8F333CA686a27374Ae06F5dfd772E9E",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "DODOApprove",
"compilerVersion": "v0.8.16+commit.07a7930e"
},
{
"group": "dodo_v3_core",
"label": "DODOApproveProxy",
"address": "0x08d764c03C42635d8ef9046752b5694243E21Fe9",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "DODOApproveProxy",
"compilerVersion": "v0.8.16+commit.07a7930e"
},
{
"group": "dodo_v3_core",
"label": "D3MMFactory",
"address": "0x78470C7d2925B6738544E2DD4FE7c07CcA21AC31",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "D3MMFactory",
"compilerVersion": "v0.8.16+commit.07a7930e"
},
{
"group": "dodo_v3_core",
"label": "D3Proxy",
"address": "0xc9a11abB7C63d88546Be24D58a6d95e3762cB843",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "D3Proxy",
"compilerVersion": "v0.8.16+commit.07a7930e"
},
{
"group": "flash_infra",
"label": "UniversalCCIPFlashBridgeAdapter",
"address": "0xBe9e0B2d4cF6A3b2994d6f2f0904D2B165eB8ffC",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "UniversalCCIPFlashBridgeAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "flash_infra",
"label": "CrossChainFlashRepayReceiver",
"address": "0xD084b68cB4B1ef2cBA09CF99FB1B6552fd9b4859",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "CrossChainFlashRepayReceiver",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "flash_infra",
"label": "CrossChainFlashVaultCreditReceiver",
"address": "0x89F7a1fcbBe104BeE96Da4b4b6b7d3AF85f7E661",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "CrossChainFlashVaultCreditReceiver",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "native_v2",
"label": "UniswapV2Factory",
"address": "0x0C30F6e67Ab3667fCc2f5CEA8e274ef1FB920279",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "UniswapV2Factory",
"compilerVersion": "v0.5.16+commit.9c3226ce"
},
{
"group": "native_v2",
"label": "UniswapV2Router",
"address": "0x3019A7fDc76ba7F64F18d78e66842760037ee638",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "UniswapV2Router02",
"compilerVersion": "v0.6.6+commit.6c089d02"
},
{
"group": "native_v2",
"label": "SushiSwapFactory",
"address": "0x2871207ff0d56089D70c0134d33f1291B6Fce0BE",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "UniswapV2Factory",
"compilerVersion": "v0.6.12+commit.27d51765"
},
{
"group": "native_v2",
"label": "SushiSwapRouter",
"address": "0xB37b93D38559f53b62ab020A14919f2630a1aE34",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "UniswapV2Router02",
"compilerVersion": "v0.6.12+commit.27d51765"
},
{
"group": "route_execution_stack",
"label": "EnhancedSwapRouterV2",
"address": "0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "EnhancedSwapRouterV2",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "IntentBridgeCoordinatorV2",
"address": "0x7D0022B7e8360172fd9C0bB6778113b7Ea3674E7",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "IntentBridgeCoordinatorV2",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "DodoRouteExecutorAdapter",
"address": "0x88495B3dccEA93b0633390fDE71992683121Fa62",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "DodoRouteExecutorAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "DodoV3RouteExecutorAdapter",
"address": "0x9Cb97adD29c52e3B81989BcA2E33D46074B530eF",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "DodoV3RouteExecutorAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "UniswapV3RouteExecutorAdapter",
"address": "0x960D6db4E78705f82995690548556fb2266308EA",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "UniswapV3RouteExecutorAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "BalancerRouteExecutorAdapter",
"address": "0x4E1B71B69188Ab45021c797039b4887a4924157A",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "BalancerRouteExecutorAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "CurveRouteExecutorAdapter",
"address": "0x5f0E07071c41ACcD2A1b1032D3bd49b323b9ADE6",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "CurveRouteExecutorAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "OneInchRouteExecutorAdapter",
"address": "0x8168083d29b3293F215392A49D16e7FeF4a02600",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "OneInchRouteExecutorAdapter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "PilotUniswapV3Router",
"address": "0xD164D9cCfAcf5D9F91698f296aE0cd245D964384",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "Chain138PilotUniswapV3Router",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "PilotBalancerVault",
"address": "0x96423d7C1727698D8a25EbFB88131e9422d1a3C3",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "Chain138PilotBalancerVault",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "PilotCurve3Pool",
"address": "0xE440Ec15805BE4C7BabCD17A63B8C8A08a492e0f",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "Chain138PilotCurve3Pool",
"compilerVersion": "v0.8.20+commit.a1b79de6"
},
{
"group": "route_execution_stack",
"label": "PilotOneInchRouter",
"address": "0x500B84b1Bc6F59C1898a5Fe538eA20A758757A4F",
"deployed": true,
"verificationState": "verified",
"blockscoutName": "Chain138PilotOneInchAggregationRouter",
"compilerVersion": "v0.8.20+commit.a1b79de6"
}
],
"summary": {
"dodo_v3_core": {
"total": 6,
"deployed": 6,
"verified": 6,
"bytecode_only": 0,
"pending": 0
},
"flash_infra": {
"total": 3,
"deployed": 3,
"verified": 3,
"bytecode_only": 0,
"pending": 0
},
"native_v2": {
"total": 4,
"deployed": 4,
"verified": 4,
"bytecode_only": 0,
"pending": 0
},
"route_execution_stack": {
"total": 12,
"deployed": 12,
"verified": 12,
"bytecode_only": 0,
"pending": 0
}
}
}

View File

@@ -1,130 +0,0 @@
#!/usr/bin/env bash
# Deploy Phoenix Deploy API to the dev VM (canonical: VMID 5700, IP_DEV_VM).
# Installs to /opt/phoenix-deploy-api and enables systemd (see phoenix-deploy-api/scripts/install-systemd.sh).
#
# Layout on the workstation: repo root must contain phoenix-deploy-api/ and
# config/public-sector-program-manifest.json (copied into /opt by install-systemd).
# Include phoenix-deploy-api/.env in your tree before deploy (not committed); it is packed if present.
#
# Requires: LAN SSH to the Proxmox node that hosts VMID 5700 (see get_host_for_vmid in
# scripts/lib/load-project-env.sh). Default PVE: r630-02 for 5700.
#
# Usage:
# ./scripts/deployment/deploy-phoenix-deploy-api-to-dev-vm.sh --dry-run
# ./scripts/deployment/deploy-phoenix-deploy-api-to-dev-vm.sh --apply
# ./scripts/deployment/deploy-phoenix-deploy-api-to-dev-vm.sh --apply --start-ct # pct start 5700 on PVE if stopped
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# shellcheck source=/dev/null
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || {
echo "ERROR: load-project-env.sh not found at ${PROJECT_ROOT}/scripts/lib/load-project-env.sh" >&2
exit 1
}
VMID="${PHOENIX_DEPLOY_DEV_VM_VMID:-5700}"
PVE_HOST="${PHOENIX_DEPLOY_PVE_HOST:-$(get_host_for_vmid "$VMID")}"
PVE_USER="${PHOENIX_DEPLOY_PVE_USER:-root}"
SSH_OPTS="${PHOENIX_DEPLOY_SSH_OPTS:--o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new}"
IP_DEV_VM="${IP_DEV_VM:-192.168.11.59}"
DRY_RUN=1
START_CT=0
for a in "$@"; do
if [[ "$a" == "--apply" ]]; then DRY_RUN=0; fi
if [[ "$a" == "--dry-run" ]]; then DRY_RUN=1; fi
if [[ "$a" == "--start-ct" ]]; then START_CT=1; fi
done
MANIFEST="${PROJECT_ROOT}/config/public-sector-program-manifest.json"
if [[ ! -f "$MANIFEST" ]]; then
echo "WARN: missing ${MANIFEST} — install on CT will warn; add file or fix path." >&2
fi
if [[ ! -d "${PROJECT_ROOT}/phoenix-deploy-api" ]]; then
echo "ERROR: ${PROJECT_ROOT}/phoenix-deploy-api not found." >&2
exit 1
fi
echo "=============================================="
echo "Phoenix Deploy API → dev VM"
echo " VMID: $VMID (expected IP: $IP_DEV_VM)"
echo " PVE host: ${PVE_USER}@${PVE_HOST}"
echo " Dry-run: $DRY_RUN"
echo "=============================================="
REMOTE_TAR="/tmp/pda-deploy-bundle.tar.gz"
STAGE="/tmp/proxmox-pda-stage"
remote_block() {
# shellcheck disable=SC2029
ssh $SSH_OPTS "${PVE_USER}@${PVE_HOST}" "$@"
}
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "Dry-run only. Would:"
echo " 1. tar czf (phoenix-deploy-api + config/public-sector-program-manifest.json)"
echo " 2. scp bundle → ${PVE_USER}@${PVE_HOST}:${REMOTE_TAR}"
echo " 3. pct push ${VMID} … /root/pda-deploy.tar.gz && pct exec ${VMID} -- install-systemd.sh"
echo " 4. curl http://${IP_DEV_VM}:4001/health"
echo "Optional: --start-ct starts VMID ${VMID} on ${PVE_HOST} if it is stopped (pct must target a running CT)."
echo "Re-run with --apply to execute."
exit 0
fi
TMP_TAR="$(mktemp /tmp/pda-deploy-XXXXXX.tar.gz)"
cleanup() { rm -f "$TMP_TAR"; }
trap cleanup EXIT
cd "$PROJECT_ROOT"
tar czf "$TMP_TAR" phoenix-deploy-api config/public-sector-program-manifest.json
ensure_ct_running() {
if remote_block "pct exec ${VMID} -- true 2>/dev/null"; then
return 0
fi
echo "CT ${VMID} is not running or not reachable (pct exec failed)." >&2
if [[ "$START_CT" -eq 1 ]]; then
echo "Starting CT ${VMID} on ${PVE_HOST} (--start-ct)..."
if ! remote_block "pct start ${VMID}"; then
echo "pct start failed — CT may not exist on this node. Find VMID: ssh ${PVE_USER}@${PVE_HOST} \"pct list\"" >&2
echo "Override: PHOENIX_DEPLOY_PVE_HOST=<node-ip> PHOENIX_DEPLOY_DEV_VM_VMID=<id> $0 --apply" >&2
exit 1
fi
sleep 3
if ! remote_block "pct exec ${VMID} -- true 2>/dev/null"; then
echo "CT ${VMID} still not reachable after start." >&2
exit 1
fi
return 0
fi
echo "Start the dev VM first, e.g. on ${PVE_HOST}: pct start ${VMID}" >&2
echo "Or re-run with --apply --start-ct (scoped to this script only)." >&2
exit 1
}
run_deploy() {
ensure_ct_running
echo "[1/3] Upload bundle to PVE..."
scp $SSH_OPTS "$TMP_TAR" "${PVE_USER}@${PVE_HOST}:${REMOTE_TAR}"
echo "[2/3] pct push → CT ${VMID}, extract, install-systemd..."
remote_block bash -s <<REMOTE_EOF
set -euo pipefail
pct push ${VMID} ${REMOTE_TAR} /root/pda-deploy.tar.gz
pct exec ${VMID} -- bash -c "set -euo pipefail; rm -rf ${STAGE}; mkdir -p ${STAGE}; tar xzf /root/pda-deploy.tar.gz -C ${STAGE}; cd ${STAGE} && bash phoenix-deploy-api/scripts/install-systemd.sh; rm -f /root/pda-deploy.tar.gz"
rm -f ${REMOTE_TAR}
REMOTE_EOF
echo "[3/3] Health check on dev VM (LAN)..."
if command -v curl >/dev/null 2>&1; then
curl -sS --max-time 10 -o /dev/null -w " http://${IP_DEV_VM}:4001/health → HTTP %{http_code}\n" "http://${IP_DEV_VM}:4001/health" || echo " (curl failed — check firewall or service)"
else
echo " (curl not installed locally; skip health check)"
fi
}
run_deploy
echo "Done."

View File

@@ -44,8 +44,8 @@ else
set -euo pipefail
pct create ${VMID} ${TEMPLATE_CT} \\
--hostname ${HOSTNAME_CT} \\
--memory 32768 \\
--swap 8192 \\
--memory 73728 \\
--swap 0 \\
--cores 16 \\
--rootfs ${STORAGE}:200 \\
--net0 name=eth0,bridge=${NETWORK},ip=${IP_CT}/24,gw=${GATEWAY} \\

View File

@@ -157,7 +157,7 @@ PATCH_CMD+=(--apply)
CT_VERIFY_CMD=$(cat <<EOF
set -euo pipefail
printf '== env ==\n'
grep -E '^(MEV_CONFIG|MEV_ADMIN_PORT|MEV_SUPERVISOR_PORT|MEV_SUPERVISOR_URL|MEV_SUBMIT_DISABLED|MEV_ADMIN_API_KEY|MEV_EXECUTOR_PRIVATE_KEY)=' /etc/mev-platform/backend.env || true
grep -E '^(MEV_CONFIG|MEV_ADMIN_PORT|MEV_SUPERVISOR_PORT|MEV_SUPERVISOR_URL|MEV_SUPERVISOR_BIN_DIR|MEV_SUBMIT_DISABLED|MEV_ADMIN_API_KEY|MEV_EXECUTOR_PRIVATE_KEY)=' /etc/mev-platform/backend.env || true
printf '\n== services ==\n'
systemctl restart mev-supervisor.service
systemctl restart mev-admin-api.service

View File

@@ -131,7 +131,6 @@ export DBIS_CORE_DIR="${DBIS_CORE_DIR:-${PROJECT_ROOT}/dbis_core}"
# Covers: DBIS (101xx), RPC (2101-2103, 2201, 2301, etc.), Blockscout (5000), CCIP (5400-5476), NPMplus (10233, 10234), Sankofa stack (78007806)
# Live placement (2026-04-09): validators 1003/1004, sentries 1503-1510, and RPCs 2102, 2301, 2304, 2400, 2402, 2403 on r630-03;
# RPCs 2201, 2303, 2305-2308, 2401 on r630-02; 2101 + 2103 remain on r630-01 — see ALL_VMIDS_ENDPOINTS.md
# Dev VM (GitOps / Gitea sidecar target): VMID 5700 on r630-04 (verified cluster API 2026-04-17)
get_host_for_vmid() {
local vmid="$1"
case "$vmid" in
@@ -139,8 +138,7 @@ get_host_for_vmid() {
10130|10150|10151|106|107|108|10000|10001|10020|10100|10101|10120|10203|10233|10235) echo "${PROXMOX_HOST_R630_01}";;
1000|1001|1002|1500|1501|1502|2101|2103) echo "${PROXMOX_HOST_R630_01}";;
1003|1004|1503|1504|1505|1506|1507|1508|1509|1510|2102|2301|2304|2400|2402|2403) echo "${PROXMOX_HOST_R630_03}";;
5700) echo "${PROXMOX_HOST_R630_04}";;
5000|7810|2201|2303|2305|2306|2307|2308|2401|6200|6201|6202|6203|6204|6205|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
5000|5700|7810|2201|2303|2305|2306|2307|2308|2401|6200|6201|6202|6203|6204|6205|10234|10237|5800|5801) echo "${PROXMOX_HOST_R630_02}";;
2420|2430|2440|2460|2470|2480) echo "${PROXMOX_HOST_R630_01}";;
5400|5401|5402|5403|5410|5411|5412|5413|5414|5415|5416|5417|5418|5419|5420|5421|5422|5423|5424|5425|5440|5441|5442|5443|5444|5445|5446|5447|5448|5449|5450|5451|5452|5453|5454|5455|5470|5471|5472|5473|5474|5475|5476) echo "${PROXMOX_HOST_R630_02}";;
*) echo "${PROXMOX_HOST_R630_01:-${PROXMOX_R630_02}}";;

View File

@@ -52,7 +52,7 @@ const DOMAINS = [
{ domain: 'd-bis.org', target: 'http://192.168.11.54:3001', websocket: false }, // Gov Portals DBIS VMID 7804
{ domain: 'www.d-bis.org', target: 'http://192.168.11.54:3001', websocket: false },
{ domain: 'admin.d-bis.org', target: 'http://192.168.11.130:80', websocket: false },
{ domain: 'core.d-bis.org', target: 'http://192.168.11.155:3000', websocket: false },
{ domain: 'core.d-bis.org', target: 'http://192.168.11.130:80', websocket: false }, // VMID 10130: dbis-frontend
{ domain: 'dbis-admin.d-bis.org', target: 'http://192.168.11.130:80', websocket: false }, // VMID 10130: dbis-frontend
{ domain: 'dbis-api.d-bis.org', target: 'http://192.168.11.155:3000', websocket: false }, // VMID 10150: dbis-api-primary
{ domain: 'dbis-api-2.d-bis.org', target: 'http://192.168.11.156:3000', websocket: false }, // VMID 10151: dbis-api-secondary

View File

@@ -1,417 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Verify the deployed Chain 138 cross-chain flash infrastructure on Blockscout
# using the exact Foundry deployment lineage from
# DeployCrossChainFlashInfrastructure.s.sol.
#
# Usage:
# bash scripts/verify/verify-chain138-flash-infra-blockscout.sh
# bash scripts/verify/verify-chain138-flash-infra-blockscout.sh --status-only
# bash scripts/verify/verify-chain138-flash-infra-blockscout.sh --only UniversalCCIPFlashBridgeAdapter
# bash scripts/verify/verify-chain138-flash-infra-blockscout.sh --force-submit
#
# Notes:
# - By default this script refuses to submit when the current local Foundry
# runtime artifact does not match the deployed Chain 138 runtime bytecode.
# - Use --force-submit only after you intentionally decide to test a candidate
# historical source/build lineage anyway.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
SMOM_SOURCE_ROOT="${CHAIN138_VERIFY_SOURCE_ROOT:-${PROJECT_ROOT}/smom-dbis-138}"
SMOM_BROADCAST_ROOT="${CHAIN138_VERIFY_BROADCAST_ROOT:-${PROJECT_ROOT}/smom-dbis-138}"
FLASH_BROADCAST="${SMOM_BROADCAST_ROOT}/broadcast/DeployCrossChainFlashInfrastructure.s.sol/138/run-latest.json"
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
# shellcheck source=/dev/null
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
fi
command -v forge >/dev/null 2>&1 || { echo "ERROR: forge not found"; exit 1; }
command -v node >/dev/null 2>&1 || { echo "ERROR: node not found"; exit 1; }
command -v cast >/dev/null 2>&1 || { echo "ERROR: cast not found"; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "ERROR: jq not found"; exit 1; }
command -v curl >/dev/null 2>&1 || { echo "ERROR: curl not found"; exit 1; }
[[ -f "${FLASH_BROADCAST}" ]] || { echo "ERROR: missing broadcast ${FLASH_BROADCAST}"; exit 1; }
RPC_URL="${RPC_URL_138:-${CHAIN138_RPC_URL:-http://192.168.11.211:8545}}"
BLOCKSCOUT_URL="${CHAIN138_BLOCKSCOUT_INTERNAL_URL:-http://${IP_BLOCKSCOUT:-192.168.11.140}:4000}"
BLOCKSCOUT_API_BASE="${CHAIN138_BLOCKSCOUT_API_BASE:-${BLOCKSCOUT_URL}/api/v2}"
BLOCKSCOUT_PUBLIC_API_BASE="${CHAIN138_BLOCKSCOUT_PUBLIC_API_BASE:-https://explorer.d-bis.org/api/v2}"
VERIFIER_PORT="${FORGE_VERIFIER_PROXY_PORT:-3080}"
FORGE_VERIFIER_URL="${FORGE_VERIFIER_URL:-http://127.0.0.1:${VERIFIER_PORT}/api}"
WAIT_ATTEMPTS="${CHAIN138_FLASH_VERIFY_WAIT_ATTEMPTS:-18}"
WAIT_SECONDS="${CHAIN138_FLASH_VERIFY_WAIT_SECONDS:-5}"
ONLY_LIST=""
STATUS_ONLY=0
NO_WAIT=0
FORCE_SUBMIT=0
PROXY_PID=""
while [[ $# -gt 0 ]]; do
case "$1" in
--only) ONLY_LIST="${2:-}"; shift 2 ;;
--status-only) STATUS_ONLY=1; shift ;;
--no-wait) NO_WAIT=1; shift ;;
--force-submit) FORCE_SUBMIT=1; shift ;;
*)
echo "Unknown argument: $1" >&2
exit 1
;;
esac
done
cleanup_proxy() {
[[ -n "${PROXY_PID:-}" ]] && kill "${PROXY_PID}" 2>/dev/null || true
}
trap cleanup_proxy EXIT
log() { printf '%s\n' "$*"; }
ok() { printf '[ok] %s\n' "$*"; }
warn() { printf '[warn] %s\n' "$*" >&2; }
fail() { printf '[fail] %s\n' "$*" >&2; exit 1; }
should_handle() {
local name="$1"
[[ -n "${ONLY_LIST}" ]] && [[ ",${ONLY_LIST}," != *",${name},"* ]] && return 1
return 0
}
proxy_listening() {
if command -v nc >/dev/null 2>&1; then
nc -z -w 2 127.0.0.1 "${VERIFIER_PORT}" 2>/dev/null
else
timeout 2 bash -c "echo >/dev/tcp/127.0.0.1/${VERIFIER_PORT}" 2>/dev/null
fi
}
start_proxy_if_needed() {
if proxy_listening; then
ok "Forge verification proxy already listening on ${VERIFIER_PORT}."
return 0
fi
log "Starting forge verification proxy on ${VERIFIER_PORT} -> ${BLOCKSCOUT_URL}"
PORT="${VERIFIER_PORT}" BLOCKSCOUT_URL="${BLOCKSCOUT_URL}" node "${PROJECT_ROOT}/forge-verification-proxy/server.js" >/tmp/chain138-flash-infra-blockscout-proxy.log 2>&1 &
PROXY_PID=$!
sleep 2
proxy_listening || fail "Forge verification proxy failed to start. See /tmp/chain138-flash-infra-blockscout-proxy.log"
}
verification_status_json() {
local addr="$1"
local raw
local base
for base in "${BLOCKSCOUT_API_BASE}" "${BLOCKSCOUT_PUBLIC_API_BASE}"; do
raw="$(curl --max-time 20 -fsS "${base}/smart-contracts/${addr}" 2>/dev/null || true)"
if [[ -n "${raw}" ]] && jq -e 'type == "object"' >/dev/null 2>&1 <<<"${raw}"; then
printf '%s' "${raw}"
return 0
fi
done
return 1
}
is_verified() {
local addr="$1"
local expected_name="$2"
local json name compiler
json="$(verification_status_json "${addr}")" || return 1
name="$(jq -r '.name // empty' <<<"${json}")"
compiler="$(jq -r '.compiler_version // empty' <<<"${json}")"
[[ -n "${name}" && -n "${compiler}" && "${name}" == "${expected_name}" ]]
}
wait_for_verification() {
local label="$1"
local addr="$2"
local expected_name="$3"
local attempt json name compiler
for (( attempt=1; attempt<=WAIT_ATTEMPTS; attempt++ )); do
json="$(verification_status_json "${addr}")" || json=""
name="$(jq -r '.name // empty' <<<"${json}" 2>/dev/null || true)"
compiler="$(jq -r '.compiler_version // empty' <<<"${json}" 2>/dev/null || true)"
if [[ -n "${name}" && -n "${compiler}" && "${name}" == "${expected_name}" ]]; then
ok "${label} verified on Blockscout as ${name} (${compiler})."
return 0
fi
sleep "${WAIT_SECONDS}"
done
return 1
}
broadcast_commit() {
jq -r '.commit' "${FLASH_BROADCAST}"
}
broadcast_timestamp() {
jq -r '.timestamp' "${FLASH_BROADCAST}"
}
broadcast_address() {
local name="$1"
jq -r --arg name "${name}" '.transactions[] | select(.transactionType=="CREATE" and .contractName==$name) | .contractAddress' "${FLASH_BROADCAST}" | head -n1
}
broadcast_tx_hash() {
local name="$1"
jq -r --arg name "${name}" '.transactions[] | select(.transactionType=="CREATE" and .contractName==$name) | .hash' "${FLASH_BROADCAST}" | head -n1
}
broadcast_arg() {
local name="$1"
local index="$2"
jq -r --arg name "${name}" --argjson index "${index}" '.transactions[] | select(.transactionType=="CREATE" and .contractName==$name) | .arguments[$index]' "${FLASH_BROADCAST}" | head -n1
}
contract_path() {
case "$1" in
UniversalCCIPFlashBridgeAdapter) printf '%s' 'contracts/flash/UniversalCCIPFlashBridgeAdapter.sol:UniversalCCIPFlashBridgeAdapter' ;;
CrossChainFlashRepayReceiver) printf '%s' 'contracts/flash/CrossChainFlashRepayReceiver.sol:CrossChainFlashRepayReceiver' ;;
CrossChainFlashVaultCreditReceiver) printf '%s' 'contracts/flash/CrossChainFlashVaultCreditReceiver.sol:CrossChainFlashVaultCreditReceiver' ;;
*) return 1 ;;
esac
}
artifact_json_path() {
case "$1" in
UniversalCCIPFlashBridgeAdapter)
if [[ -f "${SMOM_SOURCE_ROOT}/out/scopes/flash/UniversalCCIPFlashBridgeAdapter.sol/UniversalCCIPFlashBridgeAdapter.json" ]]; then
printf '%s' "${SMOM_SOURCE_ROOT}/out/scopes/flash/UniversalCCIPFlashBridgeAdapter.sol/UniversalCCIPFlashBridgeAdapter.json"
else
printf '%s' "${SMOM_SOURCE_ROOT}/out/UniversalCCIPFlashBridgeAdapter.sol/UniversalCCIPFlashBridgeAdapter.json"
fi
;;
CrossChainFlashRepayReceiver)
if [[ -f "${SMOM_SOURCE_ROOT}/out/scopes/flash/CrossChainFlashRepayReceiver.sol/CrossChainFlashRepayReceiver.json" ]]; then
printf '%s' "${SMOM_SOURCE_ROOT}/out/scopes/flash/CrossChainFlashRepayReceiver.sol/CrossChainFlashRepayReceiver.json"
else
printf '%s' "${SMOM_SOURCE_ROOT}/out/CrossChainFlashRepayReceiver.sol/CrossChainFlashRepayReceiver.json"
fi
;;
CrossChainFlashVaultCreditReceiver)
if [[ -f "${SMOM_SOURCE_ROOT}/out/scopes/flash/CrossChainFlashVaultCreditReceiver.sol/CrossChainFlashVaultCreditReceiver.json" ]]; then
printf '%s' "${SMOM_SOURCE_ROOT}/out/scopes/flash/CrossChainFlashVaultCreditReceiver.sol/CrossChainFlashVaultCreditReceiver.json"
else
printf '%s' "${SMOM_SOURCE_ROOT}/out/CrossChainFlashVaultCreditReceiver.sol/CrossChainFlashVaultCreditReceiver.json"
fi
;;
*) return 1 ;;
esac
}
constructor_signature() {
printf '%s' 'constructor(address)'
}
has_contract_bytecode() {
local addr="$1"
local code
code="$(cast code "${addr}" --rpc-url "${RPC_URL}" 2>/dev/null | tr -d '\n\r \t' | tr '[:upper:]' '[:lower:]')" || true
[[ -n "${code}" && "${code}" != "0x" && "${code}" != "0x0" ]]
}
creation_input_report() {
local label="$1"
local constructor_sig="$2"
local constructor_arg="$3"
local artifact_json artifact_bytecode tx_input encoded candidate candidate_keccak tx_keccak
artifact_json="$(artifact_json_path "${label}")" || return 2
[[ -f "${artifact_json}" ]] || return 2
artifact_bytecode="$(jq -r '.bytecode.object // empty' "${artifact_json}" | tr '[:upper:]' '[:lower:]')"
tx_input="$(jq -r --arg name "${label}" '.transactions[] | select(.transactionType=="CREATE" and .contractName==$name) | .transaction.input' "${FLASH_BROADCAST}" | head -n1 | tr '[:upper:]' '[:lower:]')"
[[ -n "${artifact_bytecode}" && -n "${tx_input}" ]] || return 2
encoded="$(cast abi-encode "${constructor_sig}" "${constructor_arg}")"
candidate="${artifact_bytecode}${encoded#0x}"
candidate_keccak="$(cast keccak "${candidate}")"
tx_keccak="$(cast keccak "${tx_input}")"
if [[ "${candidate}" == "${tx_input}" ]]; then
ok "${label}: recovered exact historical creation bytecode/input (${candidate_keccak})."
return 0
fi
warn "${label}: creation bytecode/input does not match recorded deployment transaction."
warn "${label}: candidate_keccak=${candidate_keccak} tx_input_keccak=${tx_keccak}"
return 1
}
runtime_hash_report() {
local label="$1"
local addr="$2"
local constructor_sig="$3"
local constructor_arg="$4"
local artifact_json artifact_runtime chain_runtime artifact_keccak chain_keccak
local immutable_count
artifact_json="$(artifact_json_path "${label}")" || return 2
[[ -f "${artifact_json}" ]] || return 2
immutable_count="$(jq -r '(.deployedBytecode.immutableReferences // {}) | length' "${artifact_json}")"
if [[ "${immutable_count}" != "0" ]]; then
creation_input_report "${label}" "${constructor_sig}" "${constructor_arg}"
return $?
fi
artifact_runtime="$(jq -r '.deployedBytecode.object // empty' "${artifact_json}" | tr '[:upper:]' '[:lower:]')"
chain_runtime="$(cast code "${addr}" --rpc-url "${RPC_URL}" 2>/dev/null | tr -d '\n\r \t' | tr '[:upper:]' '[:lower:]')" || true
[[ -n "${artifact_runtime}" && -n "${chain_runtime}" && "${chain_runtime}" != "0x" ]] || return 2
artifact_keccak="$(cast keccak "${artifact_runtime}")"
chain_keccak="$(cast keccak "${chain_runtime}")"
if [[ "${artifact_runtime}" != "${chain_runtime}" ]]; then
warn "${label}: Foundry artifact runtime bytecode does not match deployed bytecode."
warn "${label}: artifact_keccak=${artifact_keccak} chain_keccak=${chain_keccak}"
return 1
fi
ok "${label}: current Foundry runtime bytecode matches deployed bytecode (${artifact_keccak})."
return 0
}
submit_standard_input_from_forge() {
local label="$1"
local addr="$2"
local path="$3"
local constructor_args="$4"
local input_file response message
local compiler_version evm_version optimization_runs via_ir_flag artifact_json mismatch_rc
artifact_json="$(artifact_json_path "${label}")" || fail "${label}: missing artifact mapping"
[[ -f "${artifact_json}" ]] || fail "${label}: missing artifact ${artifact_json}"
if runtime_hash_report "${label}" "${addr}" "$(constructor_signature)" "$(broadcast_arg "${label}" 0)"; then
mismatch_rc=0
else
mismatch_rc=$?
fi
if (( mismatch_rc == 1 && FORCE_SUBMIT == 0 )); then
warn "${label}: skipping submission because the current local artifact does not match deployed runtime bytecode."
warn "${label}: recover the exact historical source/build lineage first, then rerun with --force-submit if you intentionally want to test a candidate."
return 0
fi
compiler_version="v$(jq -r '.metadata.compiler.version // empty' "${artifact_json}")"
evm_version="$(jq -r '.metadata.settings.evmVersion // "default"' "${artifact_json}")"
optimization_runs="$(jq -r '.metadata.settings.optimizer.runs // 200' "${artifact_json}")"
via_ir_flag=(--via-ir)
if [[ "$(jq -r '.metadata.settings.viaIR // false' "${artifact_json}")" != "true" ]]; then
via_ir_flag=()
fi
input_file="$(mktemp)"
(
cd "${SMOM_SOURCE_ROOT}"
forge verify-contract "${addr}" "${path}" \
--chain-id 138 \
--root . \
--compiler-version "${compiler_version}" \
--num-of-optimizations "${optimization_runs}" \
"${via_ir_flag[@]}" \
--evm-version "${evm_version}" \
--show-standard-json-input >"${input_file}"
) || {
rm -f "${input_file}"
fail "${label}: failed to render Foundry standard-input from deployment sources."
}
response="$(
curl --max-time 180 -fsS -X POST \
-F "compiler_version=${compiler_version}" \
-F "contract_name=${path}" \
-F "autodetect_constructor_args=false" \
-F "constructor_args=${constructor_args}" \
-F "optimization_runs=${optimization_runs}" \
-F "is_optimization_enabled=true" \
-F "evm_version=${evm_version}" \
-F "license_type=mit" \
-F "files[0]=@${input_file};type=application/json" \
"${BLOCKSCOUT_URL}/api/v2/smart-contracts/${addr}/verification/via/standard-input"
)" || {
rm -f "${input_file}"
fail "${label}: Blockscout Foundry standard-input submission failed."
}
rm -f "${input_file}"
message="$(jq -r '.message // empty' <<<"${response}")"
if [[ "${message}" == "Smart-contract verification started" ]]; then
ok "${label} Foundry standard-input verification submission accepted."
return 0
fi
warn "${label} Foundry standard-input verification returned: ${response}"
return 1
}
submit_best_verification() {
local label="$1"
local addr="$2"
local path="$3"
local expected_name="$4"
local encoded
has_contract_bytecode "${addr}" || fail "${label} has no bytecode at ${addr}"
if is_verified "${addr}" "${expected_name}"; then
ok "${label} already verified on Blockscout."
return 0
fi
encoded="$(cast abi-encode "$(constructor_signature)" "$(broadcast_arg "${label}" 0)")"
submit_standard_input_from_forge "${label}" "${addr}" "${path}" "${encoded}"
}
log "Chain 138 flash-infra Blockscout verification"
log "RPC: ${RPC_URL}"
log "Explorer API: ${BLOCKSCOUT_API_BASE}"
log "Flash broadcast commit: $(broadcast_commit)"
log "Flash broadcast timestamp: $(broadcast_timestamp)"
log
if (( STATUS_ONLY )); then
for label in UniversalCCIPFlashBridgeAdapter CrossChainFlashRepayReceiver CrossChainFlashVaultCreditReceiver; do
should_handle "${label}" || continue
addr="$(broadcast_address "${label}")"
if is_verified "${addr}" "${label}"; then
ok "${label} already verified on Blockscout."
else
warn "${label} not yet verified on Blockscout."
fi
runtime_hash_report "${label}" "${addr}" "$(constructor_signature)" "$(broadcast_arg "${label}" 0)" || true
done
exit 0
fi
for label in UniversalCCIPFlashBridgeAdapter CrossChainFlashRepayReceiver CrossChainFlashVaultCreditReceiver; do
should_handle "${label}" || continue
addr="$(broadcast_address "${label}")"
tx_hash="$(broadcast_tx_hash "${label}")"
log "Processing ${label} at ${addr} (create tx ${tx_hash})"
submit_best_verification "${label}" "${addr}" "$(contract_path "${label}")" "${label}"
done
if (( NO_WAIT )); then
ok "Submission pass complete; skipping wait/poll because --no-wait was passed."
exit 0
fi
for label in UniversalCCIPFlashBridgeAdapter CrossChainFlashRepayReceiver CrossChainFlashVaultCreditReceiver; do
should_handle "${label}" || continue
addr="$(broadcast_address "${label}")"
if is_verified "${addr}" "${label}"; then
ok "${label} already verified on Blockscout."
continue
fi
if ! wait_for_verification "${label}" "${addr}" "${label}"; then
warn "${label} did not materialize as fully verified within the wait window."
fi
done

View File

@@ -1,588 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Verify the deployed Chain 138 route execution stack and pilot venue contracts on Blockscout.
#
# Usage:
# bash scripts/verify/verify-chain138-route-execution-stack-blockscout.sh
# bash scripts/verify/verify-chain138-route-execution-stack-blockscout.sh --status-only
# bash scripts/verify/verify-chain138-route-execution-stack-blockscout.sh --no-wait
# bash scripts/verify/verify-chain138-route-execution-stack-blockscout.sh --only EnhancedSwapRouterV2,Chain138PilotCurve3Pool
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
SMOM_SOURCE_ROOT="${CHAIN138_VERIFY_SOURCE_ROOT:-${PROJECT_ROOT}/smom-dbis-138}"
SMOM_BROADCAST_ROOT="${CHAIN138_VERIFY_BROADCAST_ROOT:-${PROJECT_ROOT}/smom-dbis-138}"
if [[ -f "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" ]]; then
# shellcheck source=/dev/null
source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh"
fi
command -v forge >/dev/null 2>&1 || { echo "ERROR: forge not found"; exit 1; }
command -v node >/dev/null 2>&1 || { echo "ERROR: node not found"; exit 1; }
command -v cast >/dev/null 2>&1 || { echo "ERROR: cast not found"; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "ERROR: jq not found"; exit 1; }
command -v curl >/dev/null 2>&1 || { echo "ERROR: curl not found"; exit 1; }
RPC_URL="${RPC_URL_138:-${CHAIN138_RPC_URL:-http://192.168.11.211:8545}}"
BLOCKSCOUT_URL="${CHAIN138_BLOCKSCOUT_INTERNAL_URL:-http://${IP_BLOCKSCOUT:-192.168.11.140}:4000}"
BLOCKSCOUT_API_BASE="${CHAIN138_BLOCKSCOUT_API_BASE:-${BLOCKSCOUT_URL}/api/v2}"
BLOCKSCOUT_PUBLIC_API_BASE="${CHAIN138_BLOCKSCOUT_PUBLIC_API_BASE:-https://explorer.d-bis.org/api/v2}"
ROUTE_BROADCAST="${SMOM_BROADCAST_ROOT}/broadcast/DeployEnhancedSwapRouterV2.s.sol/138/run-latest.json"
VERIFIER_PORT="${FORGE_VERIFIER_PROXY_PORT:-3080}"
FORGE_VERIFIER_URL="${FORGE_VERIFIER_URL:-http://127.0.0.1:${VERIFIER_PORT}/api}"
ROUTE_STACK_SOLC_VERSION="${ROUTE_STACK_SOLC_VERSION:-v0.8.20+commit.a1b79de6}"
ROUTE_STACK_EVM_VERSION="${ROUTE_STACK_EVM_VERSION:-shanghai}"
ROUTE_STACK_OPT_RUNS="${ROUTE_STACK_OPT_RUNS:-200}"
ONLY_LIST=""
STATUS_ONLY=0
NO_WAIT=0
PROXY_PID=""
while [[ $# -gt 0 ]]; do
case "$1" in
--only) ONLY_LIST="${2:-}"; shift 2 ;;
--status-only) STATUS_ONLY=1; shift ;;
--no-wait) NO_WAIT=1; shift ;;
*)
echo "Unknown argument: $1" >&2
exit 1
;;
esac
done
cleanup_proxy() {
[[ -n "${PROXY_PID:-}" ]] && kill "${PROXY_PID}" 2>/dev/null || true
}
trap cleanup_proxy EXIT
should_handle() {
local name="$1"
[[ -n "${ONLY_LIST}" ]] && [[ ",${ONLY_LIST}," != *",${name},"* ]] && return 1
return 0
}
log() { printf '%s\n' "$*"; }
ok() { printf '[ok] %s\n' "$*"; }
warn() { printf '[warn] %s\n' "$*" >&2; }
fail() { printf '[fail] %s\n' "$*" >&2; exit 1; }
proxy_listening() {
if command -v nc >/dev/null 2>&1; then
nc -z -w 2 127.0.0.1 "${VERIFIER_PORT}" 2>/dev/null
else
timeout 2 bash -c "echo >/dev/tcp/127.0.0.1/${VERIFIER_PORT}" 2>/dev/null
fi
}
start_proxy_if_needed() {
if proxy_listening; then
ok "Forge verification proxy already listening on ${VERIFIER_PORT}."
return 0
fi
log "Starting forge verification proxy on ${VERIFIER_PORT} -> ${BLOCKSCOUT_URL}"
PORT="${VERIFIER_PORT}" BLOCKSCOUT_URL="${BLOCKSCOUT_URL}" node "${PROJECT_ROOT}/forge-verification-proxy/server.js" >/tmp/chain138-route-execution-blockscout-proxy.log 2>&1 &
PROXY_PID=$!
sleep 2
proxy_listening || fail "Forge verification proxy failed to start. See /tmp/chain138-route-execution-blockscout-proxy.log"
}
has_contract_bytecode() {
local addr="$1"
local code
code="$(cast code "${addr}" --rpc-url "${RPC_URL}" 2>/dev/null | tr -d '\n\r \t' | tr '[:upper:]' '[:lower:]')" || true
[[ -n "${code}" && "${code}" != "0x" && "${code}" != "0x0" ]]
}
verification_status_json() {
local addr="$1"
local raw
local base
for base in "${BLOCKSCOUT_API_BASE}" "${BLOCKSCOUT_PUBLIC_API_BASE}"; do
raw="$(curl --max-time 20 -fsS "${base}/smart-contracts/${addr}" 2>/dev/null || true)"
if [[ -n "${raw}" ]] && jq -e 'type == "object"' >/dev/null 2>&1 <<<"${raw}"; then
printf '%s' "${raw}"
return 0
fi
done
return 1
}
is_verified() {
local addr="$1"
local expected_name="$2"
local json name compiler
json="$(verification_status_json "${addr}")" || return 1
name="$(jq -r '.name // empty' <<<"${json}")"
compiler="$(jq -r '.compiler_version // empty' <<<"${json}")"
[[ -n "${name}" && -n "${compiler}" && "${name}" == "${expected_name}" ]]
}
submit_verification() {
local label="$1"
local addr="$2"
local path="$3"
local expected_name="$4"
local constructor_sig="$5"
shift 5
local constructor_args=("$@")
start_proxy_if_needed
has_contract_bytecode "${addr}" || fail "${label} has no bytecode at ${addr}"
if is_verified "${addr}" "${expected_name}"; then
ok "${label} already verified on Blockscout."
return 0
fi
local cmd=(forge verify-contract "${addr}" "${path}" --chain-id 138 --verifier blockscout --verifier-url "${FORGE_VERIFIER_URL}" --rpc-url "${RPC_URL}" --flatten)
if [[ -n "${constructor_sig}" ]]; then
local encoded
encoded="$(cast abi-encode "${constructor_sig}" "${constructor_args[@]}")"
cmd+=(--constructor-args "${encoded}")
fi
log "Submitting Blockscout verification for ${label} (${addr})"
if (cd "${SMOM_SOURCE_ROOT}" && "${cmd[@]}" 2>&1); then
ok "${label} verification submission accepted."
else
warn "${label} verification submission did not complete cleanly. Check Blockscout manually."
fi
}
artifact_dbg_path() {
case "$1" in
EnhancedSwapRouterV2) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/EnhancedSwapRouterV2.sol/EnhancedSwapRouterV2.dbg.json" ;;
IntentBridgeCoordinatorV2) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/IntentBridgeCoordinatorV2.sol/IntentBridgeCoordinatorV2.dbg.json" ;;
DodoRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/adapters/DodoRouteExecutorAdapter.sol/DodoRouteExecutorAdapter.dbg.json" ;;
DodoV3RouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/adapters/DodoV3RouteExecutorAdapter.sol/DodoV3RouteExecutorAdapter.dbg.json" ;;
UniswapV3RouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/adapters/UniswapV3RouteExecutorAdapter.sol/UniswapV3RouteExecutorAdapter.dbg.json" ;;
BalancerRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/adapters/BalancerRouteExecutorAdapter.sol/BalancerRouteExecutorAdapter.dbg.json" ;;
CurveRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/adapters/CurveRouteExecutorAdapter.sol/CurveRouteExecutorAdapter.dbg.json" ;;
OneInchRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/adapters/OneInchRouteExecutorAdapter.sol/OneInchRouteExecutorAdapter.dbg.json" ;;
Chain138PilotUniswapV3Router) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol/Chain138PilotUniswapV3Router.dbg.json" ;;
Chain138PilotBalancerVault) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol/Chain138PilotBalancerVault.dbg.json" ;;
Chain138PilotCurve3Pool) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol/Chain138PilotCurve3Pool.dbg.json" ;;
Chain138PilotOneInchAggregationRouter) printf '%s' "${SMOM_SOURCE_ROOT}/artifacts/contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol/Chain138PilotOneInchAggregationRouter.dbg.json" ;;
*) return 1 ;;
esac
}
foundry_artifact_json_path() {
case "$1" in
EnhancedSwapRouterV2) printf '%s' "${SMOM_SOURCE_ROOT}/out/EnhancedSwapRouterV2.sol/EnhancedSwapRouterV2.json" ;;
IntentBridgeCoordinatorV2) printf '%s' "${SMOM_SOURCE_ROOT}/out/IntentBridgeCoordinatorV2.sol/IntentBridgeCoordinatorV2.json" ;;
DodoRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/out/DodoRouteExecutorAdapter.sol/DodoRouteExecutorAdapter.json" ;;
DodoV3RouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/out/DodoV3RouteExecutorAdapter.sol/DodoV3RouteExecutorAdapter.json" ;;
UniswapV3RouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/out/UniswapV3RouteExecutorAdapter.sol/UniswapV3RouteExecutorAdapter.json" ;;
BalancerRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/out/BalancerRouteExecutorAdapter.sol/BalancerRouteExecutorAdapter.json" ;;
CurveRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/out/CurveRouteExecutorAdapter.sol/CurveRouteExecutorAdapter.json" ;;
OneInchRouteExecutorAdapter) printf '%s' "${SMOM_SOURCE_ROOT}/out/OneInchRouteExecutorAdapter.sol/OneInchRouteExecutorAdapter.json" ;;
Chain138PilotUniswapV3Router|Chain138PilotBalancerVault|Chain138PilotCurve3Pool|Chain138PilotOneInchAggregationRouter)
printf '%s' "${SMOM_SOURCE_ROOT}/out/Chain138PilotDexVenues.sol/${1}.json"
;;
*) return 1 ;;
esac
}
route_broadcast_input() {
local label="$1"
[[ -f "${ROUTE_BROADCAST}" ]] || return 1
jq -r --arg name "${label}" '.transactions[] | select(.transactionType=="CREATE" and .contractName==$name) | .transaction.input' "${ROUTE_BROADCAST}" | head -n1
}
runtime_hash_report() {
local label="$1"
local addr="$2"
local constructor_sig="$3"
shift 3
local constructor_args=("$@")
local artifact_json artifact_runtime chain_runtime
artifact_json="$(foundry_artifact_json_path "${label}")" || return 0
[[ -f "${artifact_json}" ]] || return 0
local immutable_count
immutable_count="$(jq -r '(.deployedBytecode.immutableReferences // {}) | length' "${artifact_json}")"
if [[ "${immutable_count}" != "0" && -n "${constructor_sig}" ]]; then
local bytecode tx_input encoded candidate candidate_keccak tx_keccak
bytecode="$(jq -r '.bytecode.object // empty' "${artifact_json}" | tr '[:upper:]' '[:lower:]')"
tx_input="$(route_broadcast_input "${label}" | tr '[:upper:]' '[:lower:]')" || tx_input=""
if [[ -n "${bytecode}" && -n "${tx_input}" ]]; then
encoded="$(cast abi-encode "${constructor_sig}" "${constructor_args[@]}")"
candidate="${bytecode}${encoded#0x}"
candidate_keccak="$(cast keccak "${candidate}")"
tx_keccak="$(cast keccak "${tx_input}")"
if [[ "${candidate}" == "${tx_input}" ]]; then
ok "${label}: recovered exact historical creation bytecode/input (${candidate_keccak})."
else
warn "${label}: creation bytecode/input does not match recorded deployment transaction."
warn "${label}: candidate_keccak=${candidate_keccak} tx_input_keccak=${tx_keccak}"
fi
fi
return 0
fi
artifact_runtime="$(jq -r '.deployedBytecode.object // empty' "${artifact_json}" | tr '[:upper:]' '[:lower:]')"
chain_runtime="$(cast code "${addr}" --rpc-url "${RPC_URL}" 2>/dev/null | tr -d '\n\r \t' | tr '[:upper:]' '[:lower:]')" || true
[[ -n "${artifact_runtime}" && -n "${chain_runtime}" && "${chain_runtime}" != "0x" ]] || return 0
if [[ "${artifact_runtime}" != "${chain_runtime}" ]]; then
warn "${label}: Foundry artifact runtime bytecode does not match deployed bytecode."
warn "${label}: artifact_keccak=$(cast keccak "${artifact_runtime}") chain_keccak=$(cast keccak "${chain_runtime}")"
fi
}
submit_standard_input_from_artifact() {
local label="$1"
local addr="$2"
local contract_path="$3"
local constructor_args="$4"
local dbg build input_file compiler_version evm_version optimization_runs optimization_enabled license_type response message
dbg="$(artifact_dbg_path "${label}")" || fail "${label}: missing dbg path mapping"
[[ -f "${dbg}" ]] || fail "${label}: missing dbg artifact ${dbg}"
build="$(jq -r '.buildInfo // .build_info // empty' "${dbg}")"
[[ -n "${build}" && "${build}" != "null" ]] || fail "${label}: missing build-info reference in ${dbg}"
build="$(cd "$(dirname "${dbg}")" && realpath "${build}")"
[[ -f "${build}" ]] || fail "${label}: missing build-info file ${build}"
input_file="$(mktemp)"
python3 - "${dbg}" "${build}" "${input_file}" "${contract_path%%:*}" <<'PY'
import json
import os
import posixpath
import re
import sys
dbg_path, build_path, out_path, fallback_source = sys.argv[1:5]
with open(dbg_path, "r", encoding="utf-8") as fh:
dbg = json.load(fh)
with open(build_path, "r", encoding="utf-8") as fh:
build = json.load(fh)
source_name = dbg.get("sourceName") or dbg.get("source_name") or fallback_source
if not source_name:
raise SystemExit(f"missing sourceName in {dbg_path}")
input_data = build["input"]
sources = input_data.get("sources", {})
if source_name not in sources:
raise SystemExit(f"source {source_name} missing from build-info input")
import_re = re.compile(r'import\s+(?:[^;]*?\s+from\s+)?["\']([^"\']+)["\']\s*;')
closure = set()
stack = [source_name]
while stack:
current = stack.pop()
if current in closure or current not in sources:
continue
closure.add(current)
content = sources[current].get("content", "")
for entry in import_re.findall(content):
if entry.startswith("."):
target = posixpath.normpath(posixpath.join(posixpath.dirname(current), entry))
else:
target = entry
if target in sources and target not in closure:
stack.append(target)
reduced = json.loads(json.dumps(input_data))
reduced["sources"] = {name: sources[name] for name in sorted(closure)}
with open(out_path, "w", encoding="utf-8") as fh:
json.dump(reduced, fh, separators=(",", ":"))
PY
compiler_version="$(jq -r '.solcLongVersion | "v" + .' "${build}")"
evm_version="$(jq -r '.input.settings.evmVersion // "default"' "${build}")"
optimization_runs="$(jq -r '.input.settings.optimizer.runs // 200' "${build}")"
optimization_enabled="$(jq -r '.input.settings.optimizer.enabled // true' "${build}")"
license_type="mit"
response="$(
curl --max-time 180 -fsS -X POST \
-F "compiler_version=${compiler_version}" \
-F "contract_name=${contract_path}" \
-F "autodetect_constructor_args=false" \
-F "constructor_args=${constructor_args}" \
-F "optimization_runs=${optimization_runs}" \
-F "is_optimization_enabled=${optimization_enabled}" \
-F "evm_version=${evm_version}" \
-F "license_type=${license_type}" \
-F "files[0]=@${input_file};type=application/json" \
"${BLOCKSCOUT_URL}/api/v2/smart-contracts/${addr}/verification/via/standard-input"
)" || {
rm -f "${input_file}"
fail "${label}: Blockscout standard-input submission failed."
}
rm -f "${input_file}"
message="$(jq -r '.message // empty' <<<"${response}")"
if [[ "${message}" == "Smart-contract verification started" || "${message}" == "Already verified" ]]; then
ok "${label} standard-input verification submission accepted."
return 0
fi
warn "${label} standard-input verification returned: ${response}"
return 1
}
submit_standard_input_from_forge() {
local label="$1"
local addr="$2"
local contract_path="$3"
local constructor_sig="$4"
shift 4
local constructor_args_raw=("$@")
local constructor_args=""
local input_file response message
local compiler_version evm_version optimization_runs via_ir_flag artifact_json
compiler_version="${ROUTE_STACK_SOLC_VERSION}"
evm_version="${ROUTE_STACK_EVM_VERSION}"
optimization_runs="${ROUTE_STACK_OPT_RUNS}"
via_ir_flag=(--via-ir)
artifact_json="$(foundry_artifact_json_path "${label}" || true)"
if [[ -n "${artifact_json}" && -f "${artifact_json}" ]]; then
compiler_version="v$(jq -r '.metadata.compiler.version // empty' "${artifact_json}")"
evm_version="$(jq -r '.metadata.settings.evmVersion // "default"' "${artifact_json}")"
optimization_runs="$(jq -r '.metadata.settings.optimizer.runs // 200' "${artifact_json}")"
if [[ "$(jq -r '.metadata.settings.viaIR // false' "${artifact_json}")" != "true" ]]; then
via_ir_flag=()
fi
fi
if [[ -n "${constructor_sig}" ]]; then
constructor_args="$(cast abi-encode "${constructor_sig}" "${constructor_args_raw[@]}")"
fi
runtime_hash_report "${label}" "${addr}" "${constructor_sig}" "${constructor_args_raw[@]}"
input_file="$(mktemp)"
(
cd "${SMOM_SOURCE_ROOT}"
forge verify-contract "${addr}" "${contract_path}" \
--chain-id 138 \
--root . \
--compiler-version "${compiler_version}" \
--num-of-optimizations "${optimization_runs}" \
"${via_ir_flag[@]}" \
--evm-version "${evm_version}" \
--show-standard-json-input >"${input_file}"
) || {
rm -f "${input_file}"
fail "${label}: failed to render Foundry standard-input from deployment sources."
}
response="$(
curl --max-time 180 -fsS -X POST \
-F "compiler_version=${compiler_version}" \
-F "contract_name=${contract_path}" \
-F "autodetect_constructor_args=false" \
-F "constructor_args=${constructor_args}" \
-F "optimization_runs=${optimization_runs}" \
-F "is_optimization_enabled=true" \
-F "evm_version=${evm_version}" \
-F "license_type=mit" \
-F "files[0]=@${input_file};type=application/json" \
"${BLOCKSCOUT_URL}/api/v2/smart-contracts/${addr}/verification/via/standard-input"
)" || {
rm -f "${input_file}"
fail "${label}: Blockscout Foundry standard-input submission failed."
}
rm -f "${input_file}"
message="$(jq -r '.message // empty' <<<"${response}")"
if [[ "${message}" == "Smart-contract verification started" || "${message}" == "Already verified" ]]; then
ok "${label} Foundry standard-input verification submission accepted."
return 0
fi
warn "${label} Foundry standard-input verification returned: ${response}"
return 1
}
submit_best_verification() {
local label="$1"
local addr="$2"
local path="$3"
local expected_name="$4"
local constructor_sig="$5"
shift 5
local constructor_args=("$@")
local encoded=""
if [[ -n "${constructor_sig}" ]]; then
encoded="$(cast abi-encode "${constructor_sig}" "${constructor_args[@]}")"
fi
# Prefer the Foundry deployment lineage for the route stack. The earlier
# Hardhat dbg/build-info path drifted away from the actual deployed compiler/EVM
# settings and is kept only as a compatibility fallback.
if submit_standard_input_from_forge "${label}" "${addr}" "${path}" "${constructor_sig}" "${constructor_args[@]}"; then
return 0
fi
if artifact_dbg_path "${label}" >/dev/null 2>&1; then
warn "${label}: falling back to artifact-derived standard-input after Foundry mismatch."
submit_standard_input_from_artifact "${label}" "${addr}" "${path}" "${encoded}" || return 1
return 0
fi
warn "${label}: falling back to legacy Forge flattened verification path."
submit_verification "${label}" "${addr}" "${path}" "${expected_name}" "${constructor_sig}" "${constructor_args[@]}"
}
WETH="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
USDT="0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1"
USDC="0x71D6687F38b93CCad569Fa6352c876eea967201b"
DAI_PLACEHOLDER="0x6B175474E89094C44Da98b954EedeAC495271d0F"
ROUTER_V2="$(jq -r '.chains["138"].contracts.EnhancedSwapRouterV2' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
COORDINATOR_V2="$(jq -r '.chains["138"].contracts.IntentBridgeCoordinatorV2' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
DODO_ADAPTER="$(jq -r '.chains["138"].contracts.DodoRouteExecutorAdapter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
DODO_V3_ADAPTER="$(jq -r '.chains["138"].contracts.DodoV3RouteExecutorAdapter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
UNISWAP_V3_ADAPTER="$(jq -r '.chains["138"].contracts.UniswapV3RouteExecutorAdapter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
BALANCER_ADAPTER="$(jq -r '.chains["138"].contracts.BalancerRouteExecutorAdapter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
CURVE_ADAPTER="$(jq -r '.chains["138"].contracts.CurveRouteExecutorAdapter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
ONEINCH_ADAPTER="$(jq -r '.chains["138"].contracts.OneInchRouteExecutorAdapter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
PILOT_UNISWAP="$(jq -r '.chains["138"].contracts.PilotUniswapV3Router' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
PILOT_BALANCER="$(jq -r '.chains["138"].contracts.PilotBalancerVault' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
PILOT_CURVE="$(jq -r '.chains["138"].contracts.PilotCurve3Pool' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
PILOT_ONEINCH="$(jq -r '.chains["138"].contracts.PilotOneInchRouter' "${PROJECT_ROOT}/config/smart-contracts-master.json")"
log "Chain 138 route execution stack Blockscout verification"
log "RPC: ${RPC_URL}"
log "Explorer API: ${BLOCKSCOUT_API_BASE}"
if [[ -f "${ROUTE_BROADCAST}" ]]; then
log "Route broadcast commit: $(jq -r '.commit' "${ROUTE_BROADCAST}")"
log "Route broadcast timestamp: $(jq -r '.timestamp' "${ROUTE_BROADCAST}")"
fi
log
if (( STATUS_ONLY )); then
for pair in \
"EnhancedSwapRouterV2:${ROUTER_V2}:EnhancedSwapRouterV2" \
"IntentBridgeCoordinatorV2:${COORDINATOR_V2}:IntentBridgeCoordinatorV2" \
"DodoRouteExecutorAdapter:${DODO_ADAPTER}:DodoRouteExecutorAdapter" \
"DodoV3RouteExecutorAdapter:${DODO_V3_ADAPTER}:DodoV3RouteExecutorAdapter" \
"UniswapV3RouteExecutorAdapter:${UNISWAP_V3_ADAPTER}:UniswapV3RouteExecutorAdapter" \
"BalancerRouteExecutorAdapter:${BALANCER_ADAPTER}:BalancerRouteExecutorAdapter" \
"CurveRouteExecutorAdapter:${CURVE_ADAPTER}:CurveRouteExecutorAdapter" \
"OneInchRouteExecutorAdapter:${ONEINCH_ADAPTER}:OneInchRouteExecutorAdapter" \
"Chain138PilotUniswapV3Router:${PILOT_UNISWAP}:Chain138PilotUniswapV3Router" \
"Chain138PilotBalancerVault:${PILOT_BALANCER}:Chain138PilotBalancerVault" \
"Chain138PilotCurve3Pool:${PILOT_CURVE}:Chain138PilotCurve3Pool" \
"Chain138PilotOneInchAggregationRouter:${PILOT_ONEINCH}:Chain138PilotOneInchAggregationRouter"
do
IFS=":" read -r label addr expected <<<"${pair}"
should_handle "${label}" || continue
if is_verified "${addr}" "${expected}"; then
ok "${label} already verified on Blockscout."
else
warn "${label} not yet verified on Blockscout."
fi
done
exit 0
fi
should_handle "EnhancedSwapRouterV2" && submit_best_verification \
"EnhancedSwapRouterV2" \
"${ROUTER_V2}" \
"contracts/bridge/trustless/EnhancedSwapRouterV2.sol:EnhancedSwapRouterV2" \
"EnhancedSwapRouterV2" \
"constructor(address,address,address,address)" \
"${WETH}" "${USDT}" "${USDC}" "${DAI_PLACEHOLDER}"
should_handle "IntentBridgeCoordinatorV2" && submit_best_verification \
"IntentBridgeCoordinatorV2" \
"${COORDINATOR_V2}" \
"contracts/bridge/trustless/IntentBridgeCoordinatorV2.sol:IntentBridgeCoordinatorV2" \
"IntentBridgeCoordinatorV2" \
"constructor(address)" \
"${ROUTER_V2}"
should_handle "DodoRouteExecutorAdapter" && submit_best_verification \
"DodoRouteExecutorAdapter" \
"${DODO_ADAPTER}" \
"contracts/bridge/trustless/adapters/DodoRouteExecutorAdapter.sol:DodoRouteExecutorAdapter" \
"DodoRouteExecutorAdapter" \
""
should_handle "DodoV3RouteExecutorAdapter" && submit_best_verification \
"DodoV3RouteExecutorAdapter" \
"${DODO_V3_ADAPTER}" \
"contracts/bridge/trustless/adapters/DodoV3RouteExecutorAdapter.sol:DodoV3RouteExecutorAdapter" \
"DodoV3RouteExecutorAdapter" \
""
should_handle "UniswapV3RouteExecutorAdapter" && submit_best_verification \
"UniswapV3RouteExecutorAdapter" \
"${UNISWAP_V3_ADAPTER}" \
"contracts/bridge/trustless/adapters/UniswapV3RouteExecutorAdapter.sol:UniswapV3RouteExecutorAdapter" \
"UniswapV3RouteExecutorAdapter" \
""
should_handle "BalancerRouteExecutorAdapter" && submit_best_verification \
"BalancerRouteExecutorAdapter" \
"${BALANCER_ADAPTER}" \
"contracts/bridge/trustless/adapters/BalancerRouteExecutorAdapter.sol:BalancerRouteExecutorAdapter" \
"BalancerRouteExecutorAdapter" \
""
should_handle "CurveRouteExecutorAdapter" && submit_best_verification \
"CurveRouteExecutorAdapter" \
"${CURVE_ADAPTER}" \
"contracts/bridge/trustless/adapters/CurveRouteExecutorAdapter.sol:CurveRouteExecutorAdapter" \
"CurveRouteExecutorAdapter" \
""
should_handle "OneInchRouteExecutorAdapter" && submit_best_verification \
"OneInchRouteExecutorAdapter" \
"${ONEINCH_ADAPTER}" \
"contracts/bridge/trustless/adapters/OneInchRouteExecutorAdapter.sol:OneInchRouteExecutorAdapter" \
"OneInchRouteExecutorAdapter" \
""
should_handle "Chain138PilotUniswapV3Router" && submit_best_verification \
"Chain138PilotUniswapV3Router" \
"${PILOT_UNISWAP}" \
"contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol:Chain138PilotUniswapV3Router" \
"Chain138PilotUniswapV3Router" \
""
should_handle "Chain138PilotBalancerVault" && submit_best_verification \
"Chain138PilotBalancerVault" \
"${PILOT_BALANCER}" \
"contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol:Chain138PilotBalancerVault" \
"Chain138PilotBalancerVault" \
""
should_handle "Chain138PilotCurve3Pool" && submit_best_verification \
"Chain138PilotCurve3Pool" \
"${PILOT_CURVE}" \
"contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol:Chain138PilotCurve3Pool" \
"Chain138PilotCurve3Pool" \
"constructor(address,address,address,uint256)" \
"${USDT}" "${USDC}" "0x0000000000000000000000000000000000000000" "4"
should_handle "Chain138PilotOneInchAggregationRouter" && submit_best_verification \
"Chain138PilotOneInchAggregationRouter" \
"${PILOT_ONEINCH}" \
"contracts/bridge/trustless/pilot/Chain138PilotDexVenues.sol:Chain138PilotOneInchAggregationRouter" \
"Chain138PilotOneInchAggregationRouter" \
""
if (( NO_WAIT )); then
log
ok "Chain 138 route execution stack verification submissions complete."
exit 0
fi
log
ok "Chain 138 route execution stack verification flow complete."