gate-evm-tools 1.0.26 → 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +14 -0
  2. package/bin/workflow.cjs +19 -0
  3. package/dist/core/BytecodeChecker.d.ts.map +1 -1
  4. package/dist/core/BytecodeChecker.js +16 -1
  5. package/dist/core/BytecodeChecker.js.map +1 -1
  6. package/dist/core/legacyProxyArtifact.d.ts +6 -0
  7. package/dist/core/legacyProxyArtifact.d.ts.map +1 -0
  8. package/dist/core/legacyProxyArtifact.js +36 -0
  9. package/dist/core/legacyProxyArtifact.js.map +1 -0
  10. package/dist-runtime/config/hardhatAdapters.d.ts +5 -0
  11. package/dist-runtime/config/hardhatAdapters.js +53 -0
  12. package/dist-runtime/config/hardhatAdapters.js.map +1 -0
  13. package/dist-runtime/config/networks.d.ts +8 -0
  14. package/dist-runtime/config/networks.js +269 -0
  15. package/dist-runtime/config/networks.js.map +1 -0
  16. package/dist-runtime/config/openzeppelin.d.ts +28 -0
  17. package/dist-runtime/config/openzeppelin.js +144 -0
  18. package/dist-runtime/config/openzeppelin.js.map +1 -0
  19. package/dist-runtime/config/rpc.d.ts +7 -0
  20. package/dist-runtime/config/rpc.js +19 -0
  21. package/dist-runtime/config/rpc.js.map +1 -0
  22. package/dist-runtime/config/zksyncSolcPath.d.ts +1 -0
  23. package/dist-runtime/config/zksyncSolcPath.js +61 -0
  24. package/dist-runtime/config/zksyncSolcPath.js.map +1 -0
  25. package/dist-runtime/migrate.d.ts +13 -0
  26. package/dist-runtime/migrate.js +208 -0
  27. package/dist-runtime/migrate.js.map +1 -0
  28. package/dist-runtime/package.json +1 -0
  29. package/dist-runtime/projectHardhat.d.ts +4 -0
  30. package/dist-runtime/projectHardhat.js +16 -0
  31. package/dist-runtime/projectHardhat.js.map +1 -0
  32. package/dist-runtime/runtime-identity.json +4 -0
  33. package/dist-runtime/template-migration.json +293 -0
  34. package/dist-runtime/workflow/checkCode.d.ts +1 -0
  35. package/dist-runtime/workflow/checkCode.js +16 -0
  36. package/dist-runtime/workflow/checkCode.js.map +1 -0
  37. package/dist-runtime/workflow/checkCodeFinalize.d.ts +1 -0
  38. package/dist-runtime/workflow/checkCodeFinalize.js +11 -0
  39. package/dist-runtime/workflow/checkCodeFinalize.js.map +1 -0
  40. package/dist-runtime/workflow/checkCodePlan.d.ts +1 -0
  41. package/dist-runtime/workflow/checkCodePlan.js +11 -0
  42. package/dist-runtime/workflow/checkCodePlan.js.map +1 -0
  43. package/dist-runtime/workflow/cli.d.ts +1 -0
  44. package/dist-runtime/workflow/cli.js +302 -0
  45. package/dist-runtime/workflow/cli.js.map +1 -0
  46. package/dist-runtime/workflow/commitContractInfo.d.ts +1 -0
  47. package/dist-runtime/workflow/commitContractInfo.js +64 -0
  48. package/dist-runtime/workflow/commitContractInfo.js.map +1 -0
  49. package/dist-runtime/workflow/create2Hashes.d.ts +1 -0
  50. package/dist-runtime/workflow/create2Hashes.js +76 -0
  51. package/dist-runtime/workflow/create2Hashes.js.map +1 -0
  52. package/dist-runtime/workflow/forkSourceProxy.d.ts +1 -0
  53. package/dist-runtime/workflow/forkSourceProxy.js +151 -0
  54. package/dist-runtime/workflow/forkSourceProxy.js.map +1 -0
  55. package/dist-runtime/workflow/importState.d.ts +1 -0
  56. package/dist-runtime/workflow/importState.js +193 -0
  57. package/dist-runtime/workflow/importState.js.map +1 -0
  58. package/dist-runtime/workflow/lib/authority.d.ts +28 -0
  59. package/dist-runtime/workflow/lib/authority.js +122 -0
  60. package/dist-runtime/workflow/lib/authority.js.map +1 -0
  61. package/dist-runtime/workflow/lib/calldata.d.ts +2 -0
  62. package/dist-runtime/workflow/lib/calldata.js +8 -0
  63. package/dist-runtime/workflow/lib/calldata.js.map +1 -0
  64. package/dist-runtime/workflow/lib/calls.d.ts +23 -0
  65. package/dist-runtime/workflow/lib/calls.js +316 -0
  66. package/dist-runtime/workflow/lib/calls.js.map +1 -0
  67. package/dist-runtime/workflow/lib/checkCode/args.d.ts +2 -0
  68. package/dist-runtime/workflow/lib/checkCode/args.js +68 -0
  69. package/dist-runtime/workflow/lib/checkCode/args.js.map +1 -0
  70. package/dist-runtime/workflow/lib/checkCode/command.d.ts +5 -0
  71. package/dist-runtime/workflow/lib/checkCode/command.js +35 -0
  72. package/dist-runtime/workflow/lib/checkCode/command.js.map +1 -0
  73. package/dist-runtime/workflow/lib/checkCode/dependencies.d.ts +6 -0
  74. package/dist-runtime/workflow/lib/checkCode/dependencies.js +125 -0
  75. package/dist-runtime/workflow/lib/checkCode/dependencies.js.map +1 -0
  76. package/dist-runtime/workflow/lib/checkCode/finalize.d.ts +17 -0
  77. package/dist-runtime/workflow/lib/checkCode/finalize.js +174 -0
  78. package/dist-runtime/workflow/lib/checkCode/finalize.js.map +1 -0
  79. package/dist-runtime/workflow/lib/checkCode/index.d.ts +3 -0
  80. package/dist-runtime/workflow/lib/checkCode/index.js +20 -0
  81. package/dist-runtime/workflow/lib/checkCode/index.js.map +1 -0
  82. package/dist-runtime/workflow/lib/checkCode/plan.d.ts +22 -0
  83. package/dist-runtime/workflow/lib/checkCode/plan.js +145 -0
  84. package/dist-runtime/workflow/lib/checkCode/plan.js.map +1 -0
  85. package/dist-runtime/workflow/lib/checkCodeReport.d.ts +25 -0
  86. package/dist-runtime/workflow/lib/checkCodeReport.js +102 -0
  87. package/dist-runtime/workflow/lib/checkCodeReport.js.map +1 -0
  88. package/dist-runtime/workflow/lib/checkCodeRunner.d.ts +87 -0
  89. package/dist-runtime/workflow/lib/checkCodeRunner.js +964 -0
  90. package/dist-runtime/workflow/lib/checkCodeRunner.js.map +1 -0
  91. package/dist-runtime/workflow/lib/cliTool.d.ts +10 -0
  92. package/dist-runtime/workflow/lib/cliTool.js +25 -0
  93. package/dist-runtime/workflow/lib/cliTool.js.map +1 -0
  94. package/dist-runtime/workflow/lib/config.d.ts +33 -0
  95. package/dist-runtime/workflow/lib/config.js +100 -0
  96. package/dist-runtime/workflow/lib/config.js.map +1 -0
  97. package/dist-runtime/workflow/lib/contractInfoCandidates.d.ts +82 -0
  98. package/dist-runtime/workflow/lib/contractInfoCandidates.js +441 -0
  99. package/dist-runtime/workflow/lib/contractInfoCandidates.js.map +1 -0
  100. package/dist-runtime/workflow/lib/create2.d.ts +49 -0
  101. package/dist-runtime/workflow/lib/create2.js +194 -0
  102. package/dist-runtime/workflow/lib/create2.js.map +1 -0
  103. package/dist-runtime/workflow/lib/create2Hashes.d.ts +22 -0
  104. package/dist-runtime/workflow/lib/create2Hashes.js +57 -0
  105. package/dist-runtime/workflow/lib/create2Hashes.js.map +1 -0
  106. package/dist-runtime/workflow/lib/deploymentInfo.d.ts +40 -0
  107. package/dist-runtime/workflow/lib/deploymentInfo.js +240 -0
  108. package/dist-runtime/workflow/lib/deploymentInfo.js.map +1 -0
  109. package/dist-runtime/workflow/lib/deploymentRecords.d.ts +137 -0
  110. package/dist-runtime/workflow/lib/deploymentRecords.js +374 -0
  111. package/dist-runtime/workflow/lib/deploymentRecords.js.map +1 -0
  112. package/dist-runtime/workflow/lib/deployments.d.ts +21 -0
  113. package/dist-runtime/workflow/lib/deployments.js +240 -0
  114. package/dist-runtime/workflow/lib/deployments.js.map +1 -0
  115. package/dist-runtime/workflow/lib/erc1967.d.ts +5 -0
  116. package/dist-runtime/workflow/lib/erc1967.js +29 -0
  117. package/dist-runtime/workflow/lib/erc1967.js.map +1 -0
  118. package/dist-runtime/workflow/lib/errors.d.ts +6 -0
  119. package/dist-runtime/workflow/lib/errors.js +77 -0
  120. package/dist-runtime/workflow/lib/errors.js.map +1 -0
  121. package/dist-runtime/workflow/lib/executionId.d.ts +7 -0
  122. package/dist-runtime/workflow/lib/executionId.js +26 -0
  123. package/dist-runtime/workflow/lib/executionId.js.map +1 -0
  124. package/dist-runtime/workflow/lib/executionPlan.d.ts +85 -0
  125. package/dist-runtime/workflow/lib/executionPlan.js +450 -0
  126. package/dist-runtime/workflow/lib/executionPlan.js.map +1 -0
  127. package/dist-runtime/workflow/lib/executionProgress.d.ts +5 -0
  128. package/dist-runtime/workflow/lib/executionProgress.js +100 -0
  129. package/dist-runtime/workflow/lib/executionProgress.js.map +1 -0
  130. package/dist-runtime/workflow/lib/external.d.ts +62 -0
  131. package/dist-runtime/workflow/lib/external.js +282 -0
  132. package/dist-runtime/workflow/lib/external.js.map +1 -0
  133. package/dist-runtime/workflow/lib/externalExecutionScanner.d.ts +64 -0
  134. package/dist-runtime/workflow/lib/externalExecutionScanner.js +638 -0
  135. package/dist-runtime/workflow/lib/externalExecutionScanner.js.map +1 -0
  136. package/dist-runtime/workflow/lib/forkSnapshot.d.ts +34 -0
  137. package/dist-runtime/workflow/lib/forkSnapshot.js +167 -0
  138. package/dist-runtime/workflow/lib/forkSnapshot.js.map +1 -0
  139. package/dist-runtime/workflow/lib/gateTool.d.ts +16 -0
  140. package/dist-runtime/workflow/lib/gateTool.js +177 -0
  141. package/dist-runtime/workflow/lib/gateTool.js.map +1 -0
  142. package/dist-runtime/workflow/lib/gateToolSync.d.ts +22 -0
  143. package/dist-runtime/workflow/lib/gateToolSync.js +253 -0
  144. package/dist-runtime/workflow/lib/gateToolSync.js.map +1 -0
  145. package/dist-runtime/workflow/lib/lock.d.ts +52 -0
  146. package/dist-runtime/workflow/lib/lock.js +269 -0
  147. package/dist-runtime/workflow/lib/lock.js.map +1 -0
  148. package/dist-runtime/workflow/lib/migrationState.d.ts +103 -0
  149. package/dist-runtime/workflow/lib/migrationState.js +920 -0
  150. package/dist-runtime/workflow/lib/migrationState.js.map +1 -0
  151. package/dist-runtime/workflow/lib/operatorExecution.d.ts +28 -0
  152. package/dist-runtime/workflow/lib/operatorExecution.js +134 -0
  153. package/dist-runtime/workflow/lib/operatorExecution.js.map +1 -0
  154. package/dist-runtime/workflow/lib/parameters.d.ts +58 -0
  155. package/dist-runtime/workflow/lib/parameters.js +322 -0
  156. package/dist-runtime/workflow/lib/parameters.js.map +1 -0
  157. package/dist-runtime/workflow/lib/pendingDeploymentReuse.d.ts +29 -0
  158. package/dist-runtime/workflow/lib/pendingDeploymentReuse.js +277 -0
  159. package/dist-runtime/workflow/lib/pendingDeploymentReuse.js.map +1 -0
  160. package/dist-runtime/workflow/lib/proxy.d.ts +18 -0
  161. package/dist-runtime/workflow/lib/proxy.js +19 -0
  162. package/dist-runtime/workflow/lib/proxy.js.map +1 -0
  163. package/dist-runtime/workflow/lib/registryCommit.d.ts +33 -0
  164. package/dist-runtime/workflow/lib/registryCommit.js +319 -0
  165. package/dist-runtime/workflow/lib/registryCommit.js.map +1 -0
  166. package/dist-runtime/workflow/lib/release.d.ts +57 -0
  167. package/dist-runtime/workflow/lib/release.js +256 -0
  168. package/dist-runtime/workflow/lib/release.js.map +1 -0
  169. package/dist-runtime/workflow/lib/releaseFinalize.d.ts +14 -0
  170. package/dist-runtime/workflow/lib/releaseFinalize.js +571 -0
  171. package/dist-runtime/workflow/lib/releaseFinalize.js.map +1 -0
  172. package/dist-runtime/workflow/lib/releaseParameters.d.ts +43 -0
  173. package/dist-runtime/workflow/lib/releaseParameters.js +184 -0
  174. package/dist-runtime/workflow/lib/releaseParameters.js.map +1 -0
  175. package/dist-runtime/workflow/lib/reporter.d.ts +39 -0
  176. package/dist-runtime/workflow/lib/reporter.js +452 -0
  177. package/dist-runtime/workflow/lib/reporter.js.map +1 -0
  178. package/dist-runtime/workflow/lib/resultPaths.d.ts +17 -0
  179. package/dist-runtime/workflow/lib/resultPaths.js +29 -0
  180. package/dist-runtime/workflow/lib/resultPaths.js.map +1 -0
  181. package/dist-runtime/workflow/lib/runtimeVersion.d.ts +10 -0
  182. package/dist-runtime/workflow/lib/runtimeVersion.js +46 -0
  183. package/dist-runtime/workflow/lib/runtimeVersion.js.map +1 -0
  184. package/dist-runtime/workflow/lib/signer.d.ts +10 -0
  185. package/dist-runtime/workflow/lib/signer.js +105 -0
  186. package/dist-runtime/workflow/lib/signer.js.map +1 -0
  187. package/dist-runtime/workflow/lib/signerIdentity.d.ts +1 -0
  188. package/dist-runtime/workflow/lib/signerIdentity.js +23 -0
  189. package/dist-runtime/workflow/lib/signerIdentity.js.map +1 -0
  190. package/dist-runtime/workflow/lib/simulation.d.ts +35 -0
  191. package/dist-runtime/workflow/lib/simulation.js +282 -0
  192. package/dist-runtime/workflow/lib/simulation.js.map +1 -0
  193. package/dist-runtime/workflow/lib/simulationCallers.d.ts +44 -0
  194. package/dist-runtime/workflow/lib/simulationCallers.js +260 -0
  195. package/dist-runtime/workflow/lib/simulationCallers.js.map +1 -0
  196. package/dist-runtime/workflow/lib/sourceRpcPolicy.d.ts +1 -0
  197. package/dist-runtime/workflow/lib/sourceRpcPolicy.js +43 -0
  198. package/dist-runtime/workflow/lib/sourceRpcPolicy.js.map +1 -0
  199. package/dist-runtime/workflow/lib/state.d.ts +6 -0
  200. package/dist-runtime/workflow/lib/state.js +25 -0
  201. package/dist-runtime/workflow/lib/state.js.map +1 -0
  202. package/dist-runtime/workflow/lib/staticValidation.d.ts +22 -0
  203. package/dist-runtime/workflow/lib/staticValidation.js +124 -0
  204. package/dist-runtime/workflow/lib/staticValidation.js.map +1 -0
  205. package/dist-runtime/workflow/lib/zksyncForkCapability.d.ts +27 -0
  206. package/dist-runtime/workflow/lib/zksyncForkCapability.js +52 -0
  207. package/dist-runtime/workflow/lib/zksyncForkCapability.js.map +1 -0
  208. package/dist-runtime/workflow/lib/zksyncForkRunner.d.ts +57 -0
  209. package/dist-runtime/workflow/lib/zksyncForkRunner.js +344 -0
  210. package/dist-runtime/workflow/lib/zksyncForkRunner.js.map +1 -0
  211. package/dist-runtime/workflow/reconcileExternal.d.ts +1 -0
  212. package/dist-runtime/workflow/reconcileExternal.js +87 -0
  213. package/dist-runtime/workflow/reconcileExternal.js.map +1 -0
  214. package/dist-runtime/workflow/reconcileExternalRun.d.ts +1 -0
  215. package/dist-runtime/workflow/reconcileExternalRun.js +77 -0
  216. package/dist-runtime/workflow/reconcileExternalRun.js.map +1 -0
  217. package/dist-runtime/workflow/releaseFinalize.d.ts +1 -0
  218. package/dist-runtime/workflow/releaseFinalize.js +11 -0
  219. package/dist-runtime/workflow/releaseFinalize.js.map +1 -0
  220. package/dist-runtime/workflow/run.d.ts +40 -0
  221. package/dist-runtime/workflow/run.js +858 -0
  222. package/dist-runtime/workflow/run.js.map +1 -0
  223. package/dist-runtime/workflow/staticValidate.d.ts +1 -0
  224. package/dist-runtime/workflow/staticValidate.js +26 -0
  225. package/dist-runtime/workflow/staticValidate.js.map +1 -0
  226. package/dist-runtime/workflow/steps/call.d.ts +3 -0
  227. package/dist-runtime/workflow/steps/call.js +9 -0
  228. package/dist-runtime/workflow/steps/call.js.map +1 -0
  229. package/dist-runtime/workflow/steps/check.d.ts +3 -0
  230. package/dist-runtime/workflow/steps/check.js +9 -0
  231. package/dist-runtime/workflow/steps/check.js.map +1 -0
  232. package/dist-runtime/workflow/steps/custom.d.ts +3 -0
  233. package/dist-runtime/workflow/steps/custom.js +33 -0
  234. package/dist-runtime/workflow/steps/custom.js.map +1 -0
  235. package/dist-runtime/workflow/steps/deploy.d.ts +3 -0
  236. package/dist-runtime/workflow/steps/deploy.js +595 -0
  237. package/dist-runtime/workflow/steps/deploy.js.map +1 -0
  238. package/dist-runtime/workflow/steps/upgrade.d.ts +3 -0
  239. package/dist-runtime/workflow/steps/upgrade.js +465 -0
  240. package/dist-runtime/workflow/steps/upgrade.js.map +1 -0
  241. package/dist-runtime/workflow/steps/view.d.ts +3 -0
  242. package/dist-runtime/workflow/steps/view.js +9 -0
  243. package/dist-runtime/workflow/steps/view.js.map +1 -0
  244. package/package.json +25 -5
  245. package/runtime/config/hardhatAdapters.ts +50 -0
  246. package/runtime/config/networks.ts +296 -0
  247. package/runtime/config/openzeppelin.ts +173 -0
  248. package/runtime/config/rpc.ts +27 -0
  249. package/runtime/config/zksyncSolcPath.ts +59 -0
  250. package/runtime/hardhat-types.d.ts +2 -0
  251. package/runtime/migrate.ts +154 -0
  252. package/runtime/projectHardhat.ts +10 -0
  253. package/runtime/template-migration.json +293 -0
  254. package/runtime/workflow/checkCode.ts +13 -0
  255. package/runtime/workflow/checkCodeFinalize.ts +8 -0
  256. package/runtime/workflow/checkCodePlan.ts +8 -0
  257. package/runtime/workflow/cli.ts +356 -0
  258. package/runtime/workflow/commitContractInfo.ts +63 -0
  259. package/runtime/workflow/create2Hashes.ts +79 -0
  260. package/runtime/workflow/forkSourceProxy.ts +193 -0
  261. package/runtime/workflow/importState.ts +190 -0
  262. package/runtime/workflow/lib/authority.ts +170 -0
  263. package/runtime/workflow/lib/calldata.ts +9 -0
  264. package/runtime/workflow/lib/calls.ts +407 -0
  265. package/runtime/workflow/lib/checkCode/args.ts +76 -0
  266. package/runtime/workflow/lib/checkCode/command.ts +36 -0
  267. package/runtime/workflow/lib/checkCode/dependencies.ts +112 -0
  268. package/runtime/workflow/lib/checkCode/finalize.ts +198 -0
  269. package/runtime/workflow/lib/checkCode/index.ts +18 -0
  270. package/runtime/workflow/lib/checkCode/plan.ts +163 -0
  271. package/runtime/workflow/lib/checkCodeReport.ts +133 -0
  272. package/runtime/workflow/lib/checkCodeRunner.ts +1225 -0
  273. package/runtime/workflow/lib/cliTool.ts +31 -0
  274. package/runtime/workflow/lib/config.ts +130 -0
  275. package/runtime/workflow/lib/contractInfoCandidates.ts +690 -0
  276. package/runtime/workflow/lib/create2.ts +278 -0
  277. package/runtime/workflow/lib/create2Hashes.ts +83 -0
  278. package/runtime/workflow/lib/deploymentInfo.ts +286 -0
  279. package/runtime/workflow/lib/deploymentRecords.ts +576 -0
  280. package/runtime/workflow/lib/deployments.ts +276 -0
  281. package/runtime/workflow/lib/erc1967.ts +57 -0
  282. package/runtime/workflow/lib/errors.ts +95 -0
  283. package/runtime/workflow/lib/executionId.ts +35 -0
  284. package/runtime/workflow/lib/executionPlan.ts +550 -0
  285. package/runtime/workflow/lib/executionProgress.ts +127 -0
  286. package/runtime/workflow/lib/external.ts +372 -0
  287. package/runtime/workflow/lib/externalExecutionScanner.ts +892 -0
  288. package/runtime/workflow/lib/forkSnapshot.ts +231 -0
  289. package/runtime/workflow/lib/gateTool.ts +261 -0
  290. package/runtime/workflow/lib/gateToolSync.ts +398 -0
  291. package/runtime/workflow/lib/lock.ts +388 -0
  292. package/runtime/workflow/lib/migrationState.ts +1246 -0
  293. package/runtime/workflow/lib/operatorExecution.ts +174 -0
  294. package/runtime/workflow/lib/parameters.ts +437 -0
  295. package/runtime/workflow/lib/pendingDeploymentReuse.ts +428 -0
  296. package/runtime/workflow/lib/proxy.ts +45 -0
  297. package/runtime/workflow/lib/registryCommit.ts +455 -0
  298. package/runtime/workflow/lib/release.ts +329 -0
  299. package/runtime/workflow/lib/releaseFinalize.ts +711 -0
  300. package/runtime/workflow/lib/releaseParameters.ts +248 -0
  301. package/runtime/workflow/lib/reporter.ts +502 -0
  302. package/runtime/workflow/lib/resultPaths.ts +49 -0
  303. package/runtime/workflow/lib/runtimeVersion.ts +28 -0
  304. package/runtime/workflow/lib/signer.ts +124 -0
  305. package/runtime/workflow/lib/signerIdentity.ts +22 -0
  306. package/runtime/workflow/lib/simulation.ts +415 -0
  307. package/runtime/workflow/lib/simulationCallers.ts +335 -0
  308. package/runtime/workflow/lib/sourceRpcPolicy.ts +41 -0
  309. package/runtime/workflow/lib/state.ts +25 -0
  310. package/runtime/workflow/lib/staticValidation.ts +142 -0
  311. package/runtime/workflow/lib/zksyncForkCapability.ts +80 -0
  312. package/runtime/workflow/lib/zksyncForkRunner.ts +505 -0
  313. package/runtime/workflow/reconcileExternal.ts +86 -0
  314. package/runtime/workflow/reconcileExternalRun.ts +88 -0
  315. package/runtime/workflow/releaseFinalize.ts +8 -0
  316. package/runtime/workflow/run.ts +1072 -0
  317. package/runtime/workflow/staticValidate.ts +23 -0
  318. package/runtime/workflow/steps/call.ts +10 -0
  319. package/runtime/workflow/steps/check.ts +10 -0
  320. package/runtime/workflow/steps/custom.ts +43 -0
  321. package/runtime/workflow/steps/deploy.ts +771 -0
  322. package/runtime/workflow/steps/upgrade.ts +598 -0
  323. package/runtime/workflow/steps/view.ts +10 -0
@@ -0,0 +1,892 @@
1
+ import crypto from "crypto";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { Interface, JsonRpcProvider } from "ethers";
5
+ import {
6
+ loadReleaseExecutionPlan,
7
+ ReleaseExecutionPlan,
8
+ ReleaseExecutionTransaction,
9
+ } from "./executionPlan";
10
+ import {
11
+ loadExternalExecutionPackage,
12
+ type ExternalExecutionPackage,
13
+ } from "./external";
14
+ import { loadOperatorExecutionResult } from "./operatorExecution";
15
+ import {
16
+ loadDeploymentExecutionRecord,
17
+ markDeploymentRecordReconciled,
18
+ type GateToolEvidence,
19
+ } from "./deploymentRecords";
20
+ import { loadConfig, resolveFinalityConfirmations, resolveTarget } from "./config";
21
+ import {
22
+ syncGateToolUups,
23
+ type GateToolSyncHre,
24
+ } from "./gateToolSync";
25
+
26
+ export type ExternalFinalityPolicy =
27
+ | { mode: "rpc_finalized" }
28
+ | { mode: "confirmation_depth"; confirmations: number };
29
+
30
+ export interface ExternalReconciliationMatch {
31
+ transactionId: string;
32
+ transactionIndex: number;
33
+ route: "operator" | "external";
34
+ executor: string;
35
+ submittedBy: string;
36
+ executionKind: "eoa" | "safe_call" | "safe_multisend";
37
+ transactionHash: string;
38
+ blockNumber: number;
39
+ blockHash: string;
40
+ blockTransactionIndex: number;
41
+ innerCallIndex?: number;
42
+ receiptStatus: 0 | 1;
43
+ finalized: boolean;
44
+ }
45
+
46
+ export interface ExternalReconciliationReport {
47
+ version: 1;
48
+ status: "not_required" | "waiting_operator" | "waiting_finality" | "waiting_external" | "reconciled" | "failed";
49
+ taskId: string;
50
+ target: string;
51
+ executionId: string;
52
+ executionPlanHash: string;
53
+ packagePath?: string;
54
+ packageHash?: string;
55
+ segments?: Array<{
56
+ segmentId: string;
57
+ transactionIds: string[];
58
+ handoffBlock: number;
59
+ nextBlock: number;
60
+ }>;
61
+ finalizedBlock: number;
62
+ finalityPolicy: ExternalFinalityPolicy;
63
+ matchedTransactionIds: string[];
64
+ matches: ExternalReconciliationMatch[];
65
+ failure?: { code: string; message: string; transactionId?: string };
66
+ reportPath: string;
67
+ createdAt: string;
68
+ updatedAt: string;
69
+ completedAt?: string;
70
+ }
71
+
72
+ interface RpcTransaction {
73
+ hash: string;
74
+ from: string;
75
+ to: string | null;
76
+ value: unknown;
77
+ data?: string;
78
+ input?: string;
79
+ blockNumber?: unknown;
80
+ blockHash?: string;
81
+ index?: unknown;
82
+ transactionIndex?: unknown;
83
+ }
84
+
85
+ interface RpcReceipt {
86
+ status: unknown;
87
+ blockNumber: unknown;
88
+ blockHash: string;
89
+ index?: unknown;
90
+ transactionIndex?: unknown;
91
+ from?: string;
92
+ to?: string | null;
93
+ logs?: Array<{
94
+ address?: string;
95
+ topics?: string[];
96
+ data?: string;
97
+ }>;
98
+ }
99
+
100
+ interface ReconciliationProvider {
101
+ getBlockNumber(): Promise<number>;
102
+ getCode(address: string): Promise<string>;
103
+ getTransaction(hash: string): Promise<RpcTransaction | null>;
104
+ getTransactionReceipt(hash: string): Promise<RpcReceipt | null>;
105
+ send(method: string, params: unknown[]): Promise<unknown>;
106
+ call?(transaction: { to: string; data: string }): Promise<string>;
107
+ }
108
+
109
+ const SAFE_INTERFACE = new Interface([
110
+ "function execTransaction(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address payable refundReceiver,bytes signatures) returns (bool success)",
111
+ "function VERSION() view returns (string)",
112
+ "event ExecutionSuccess(bytes32 txHash,uint256 payment)",
113
+ "event ExecutionFailure(bytes32 txHash,uint256 payment)",
114
+ ]);
115
+ const MULTISEND_INTERFACE = new Interface([
116
+ "function multiSend(bytes transactions)",
117
+ ]);
118
+
119
+ const sha256 = (value: Buffer | string): string => (
120
+ crypto.createHash("sha256").update(value).digest("hex")
121
+ );
122
+
123
+ const numberValue = (value: unknown, label: string): number => {
124
+ let parsed: bigint;
125
+ try {
126
+ parsed = typeof value === "bigint" ? value : BigInt(value as string | number);
127
+ } catch {
128
+ throw new Error(`${label} is not an integer`);
129
+ }
130
+ if (parsed < 0n || parsed > BigInt(Number.MAX_SAFE_INTEGER)) {
131
+ throw new Error(`${label} is outside the safe integer range`);
132
+ }
133
+ return Number(parsed);
134
+ };
135
+
136
+ const address = (value: unknown, label: string): string => {
137
+ if (typeof value !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(value)) {
138
+ throw new Error(`${label} is not an EVM address`);
139
+ }
140
+ return value.toLowerCase();
141
+ };
142
+
143
+ const calldata = (transaction: RpcTransaction): string => {
144
+ const value = transaction.data ?? transaction.input;
145
+ if (typeof value !== "string" || !/^0x(?:[0-9a-fA-F]{2})*$/.test(value)) {
146
+ throw new Error(`Transaction ${transaction.hash} has invalid calldata`);
147
+ }
148
+ return value.toLowerCase();
149
+ };
150
+
151
+ const receiptStatus = (receipt: RpcReceipt): 0 | 1 => {
152
+ const status = numberValue(receipt.status, "receipt status");
153
+ if (status !== 0 && status !== 1) throw new Error("receipt status must be 0 or 1");
154
+ return status;
155
+ };
156
+
157
+ const relative = (root: string, filePath: string): string => {
158
+ const value = path.relative(path.resolve(root), path.resolve(filePath));
159
+ if (value.startsWith("..") || path.isAbsolute(value)) {
160
+ throw new Error("Reconciliation evidence path escapes the project root");
161
+ }
162
+ return value.split(path.sep).join("/");
163
+ };
164
+
165
+ const readJson = (filePath: string, label: string): unknown => {
166
+ try {
167
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
168
+ } catch (error) {
169
+ throw new Error(`${label} is invalid: ${error instanceof Error ? error.message : String(error)}`);
170
+ }
171
+ };
172
+
173
+ const writeJsonAtomic = (filePath: string, value: unknown): void => {
174
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
175
+ const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
176
+ try {
177
+ fs.writeFileSync(temporary, JSON.stringify(value, null, 2) + "\n");
178
+ fs.renameSync(temporary, filePath);
179
+ } catch (error) {
180
+ if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
181
+ throw error;
182
+ }
183
+ };
184
+
185
+ const loadExternalPackage = (input: {
186
+ root: string;
187
+ taskDir: string;
188
+ plan: ReleaseExecutionPlan;
189
+ }): { package: ExternalExecutionPackage; filePath: string; hash: string } | undefined => {
190
+ const externalTransactions = input.plan.transactions.filter((transaction) => (
191
+ transaction.route === "external"
192
+ ));
193
+ const filePath = path.join(
194
+ input.taskDir,
195
+ "calldata",
196
+ input.plan.target,
197
+ input.plan.executionId,
198
+ "external-execution.json",
199
+ );
200
+ if (externalTransactions.length === 0) {
201
+ if (fs.existsSync(filePath)) {
202
+ throw new Error("All-operator execution must not contain an external package");
203
+ }
204
+ return undefined;
205
+ }
206
+ const loaded = loadExternalExecutionPackage({ taskDir: input.taskDir, plan: input.plan });
207
+ if (!loaded) return undefined;
208
+ return {
209
+ package: loaded.package,
210
+ filePath: loaded.filePath,
211
+ hash: sha256(fs.readFileSync(loaded.filePath)),
212
+ };
213
+ };
214
+
215
+ const transactionMatches = (
216
+ transaction: RpcTransaction,
217
+ expected: ReleaseExecutionTransaction,
218
+ from: string,
219
+ ): boolean => (
220
+ address(transaction.from, "transaction sender") === address(from, "expected sender")
221
+ && transaction.to !== null
222
+ && address(transaction.to, "transaction target") === expected.to
223
+ && numberValue(transaction.value, "transaction value").toString() === expected.value
224
+ && calldata(transaction) === expected.data
225
+ && expected.operation === 0
226
+ );
227
+
228
+ const parseMultisend = (encoded: string): Array<{
229
+ operation: 0 | 1;
230
+ to: string;
231
+ value: string;
232
+ data: string;
233
+ }> => {
234
+ const payload = encoded.startsWith("0x") ? encoded.slice(2) : encoded;
235
+ const calls: Array<{ operation: 0 | 1; to: string; value: string; data: string }> = [];
236
+ let offset = 0;
237
+ while (offset < payload.length) {
238
+ if (payload.length - offset < 2 + 40 + 64 + 64) {
239
+ throw new Error("Safe MultiSend payload is truncated");
240
+ }
241
+ const operation = Number.parseInt(payload.slice(offset, offset + 2), 16);
242
+ offset += 2;
243
+ if (operation !== 0 && operation !== 1) throw new Error("Safe MultiSend operation is invalid");
244
+ const to = `0x${payload.slice(offset, offset + 40)}`.toLowerCase();
245
+ offset += 40;
246
+ const value = BigInt(`0x${payload.slice(offset, offset + 64)}`).toString(10);
247
+ offset += 64;
248
+ const length = numberValue(`0x${payload.slice(offset, offset + 64)}`, "MultiSend data length");
249
+ offset += 64;
250
+ const end = offset + length * 2;
251
+ if (end > payload.length) throw new Error("Safe MultiSend call data is truncated");
252
+ calls.push({ operation, to, value, data: `0x${payload.slice(offset, end)}`.toLowerCase() });
253
+ offset = end;
254
+ }
255
+ return calls;
256
+ };
257
+
258
+ const safeCalls = async (
259
+ provider: ReconciliationProvider,
260
+ transaction: RpcTransaction,
261
+ safeAddress: string,
262
+ ): Promise<Array<{
263
+ operation: 0 | 1;
264
+ to: string;
265
+ value: string;
266
+ data: string;
267
+ kind: "safe_call" | "safe_multisend";
268
+ innerCallIndex?: number;
269
+ }>> => {
270
+ if (address(transaction.to, "Safe transaction target") !== safeAddress) return [];
271
+ if (!provider.call) throw new Error(`Unknown contract-wallet executor ${safeAddress}`);
272
+ try {
273
+ const versionResult = await provider.call({
274
+ to: safeAddress,
275
+ data: SAFE_INTERFACE.encodeFunctionData("VERSION", []),
276
+ });
277
+ SAFE_INTERFACE.decodeFunctionResult("VERSION", versionResult);
278
+ } catch {
279
+ throw new Error(`Unknown contract-wallet executor ${safeAddress}`);
280
+ }
281
+ let decoded;
282
+ try {
283
+ decoded = SAFE_INTERFACE.decodeFunctionData("execTransaction", calldata(transaction));
284
+ } catch {
285
+ return [];
286
+ }
287
+ const outer = {
288
+ to: address(decoded[0], "Safe inner target"),
289
+ value: BigInt(decoded[1]).toString(10),
290
+ data: String(decoded[2]).toLowerCase(),
291
+ operation: Number(decoded[3]) as 0 | 1,
292
+ };
293
+ try {
294
+ const multisend = MULTISEND_INTERFACE.decodeFunctionData("multiSend", outer.data);
295
+ return parseMultisend(String(multisend[0])).map((call, innerCallIndex) => ({
296
+ ...call,
297
+ kind: "safe_multisend" as const,
298
+ innerCallIndex,
299
+ }));
300
+ } catch {
301
+ return [{ ...outer, kind: "safe_call" }];
302
+ }
303
+ };
304
+
305
+ const assertSafeExecutionSucceeded = (receipt: RpcReceipt, safeAddress: string): void => {
306
+ let success = false;
307
+ let failure = false;
308
+ for (const log of receipt.logs || []) {
309
+ if (
310
+ typeof log.address !== "string"
311
+ || log.address.toLowerCase() !== safeAddress.toLowerCase()
312
+ || !Array.isArray(log.topics)
313
+ || typeof log.data !== "string"
314
+ ) {
315
+ continue;
316
+ }
317
+ try {
318
+ const parsed = SAFE_INTERFACE.parseLog({ topics: log.topics, data: log.data });
319
+ if (parsed?.name === "ExecutionFailure") failure = true;
320
+ if (parsed?.name === "ExecutionSuccess") success = true;
321
+ } catch {
322
+ // Ignore unrelated logs emitted by the Safe transaction.
323
+ }
324
+ }
325
+ if (failure) throw new Error(`Safe ${safeAddress} emitted ExecutionFailure`);
326
+ if (!success) throw new Error(`Safe ${safeAddress} receipt is missing ExecutionSuccess`);
327
+ };
328
+
329
+ const finalizedHeight = async (
330
+ provider: ReconciliationProvider,
331
+ confirmations: number,
332
+ ): Promise<{ finalizedBlock: number; policy: ExternalFinalityPolicy }> => {
333
+ try {
334
+ const block = await provider.send("eth_getBlockByNumber", ["finalized", false]) as {
335
+ number?: unknown;
336
+ } | null;
337
+ if (block?.number !== undefined) {
338
+ return {
339
+ finalizedBlock: numberValue(block.number, "finalized block number"),
340
+ policy: { mode: "rpc_finalized" },
341
+ };
342
+ }
343
+ } catch {
344
+ // Some EVM RPC providers do not implement the finalized block tag.
345
+ }
346
+ const latest = await provider.getBlockNumber();
347
+ if (!Number.isSafeInteger(latest) || latest < 0) {
348
+ throw new Error("RPC latest block number is invalid");
349
+ }
350
+ return {
351
+ finalizedBlock: Math.max(0, latest - confirmations),
352
+ policy: { mode: "confirmation_depth", confirmations },
353
+ };
354
+ };
355
+
356
+ const receiptMatch = (
357
+ expected: ReleaseExecutionTransaction,
358
+ transaction: RpcTransaction,
359
+ receipt: RpcReceipt,
360
+ finalizedBlock: number,
361
+ details: {
362
+ executionKind: "eoa" | "safe_call" | "safe_multisend";
363
+ submittedBy: string;
364
+ innerCallIndex?: number;
365
+ },
366
+ ): ExternalReconciliationMatch => {
367
+ const blockNumber = numberValue(receipt.blockNumber, "receipt blockNumber");
368
+ const blockTransactionIndex = numberValue(
369
+ receipt.index ?? receipt.transactionIndex ?? transaction.index ?? transaction.transactionIndex ?? 0,
370
+ "receipt transaction index",
371
+ );
372
+ return {
373
+ transactionId: expected.transactionId,
374
+ transactionIndex: expected.transactionIndex,
375
+ route: expected.route,
376
+ executor: expected.executor,
377
+ submittedBy: address(details.submittedBy, "submittedBy"),
378
+ executionKind: details.executionKind,
379
+ transactionHash: transaction.hash.toLowerCase(),
380
+ blockNumber,
381
+ blockHash: receipt.blockHash.toLowerCase(),
382
+ blockTransactionIndex,
383
+ innerCallIndex: details.innerCallIndex,
384
+ receiptStatus: receiptStatus(receipt),
385
+ finalized: blockNumber <= finalizedBlock,
386
+ };
387
+ };
388
+
389
+ const validateCanonicalReceipt = async (
390
+ provider: ReconciliationProvider,
391
+ receipt: RpcReceipt,
392
+ ): Promise<void> => {
393
+ const blockNumber = numberValue(receipt.blockNumber, "receipt blockNumber");
394
+ const block = await provider.send("eth_getBlockByNumber", [`0x${blockNumber.toString(16)}`, false]) as {
395
+ hash?: string;
396
+ } | null;
397
+ if (!block?.hash || block.hash.toLowerCase() !== receipt.blockHash.toLowerCase()) {
398
+ throw new Error("Transaction receipt is no longer in the canonical chain");
399
+ }
400
+ };
401
+
402
+ export const reconcileExternalExecution = async (input: {
403
+ root: string;
404
+ taskId: string;
405
+ network: string;
406
+ executionId: string;
407
+ provider: JsonRpcProvider;
408
+ batchSize?: number;
409
+ gateToolSyncHre?: GateToolSyncHre;
410
+ dependencies?: {
411
+ syncGateToolUups?: typeof syncGateToolUups;
412
+ };
413
+ }): Promise<ExternalReconciliationReport> => {
414
+ const provider = input.provider as unknown as ReconciliationProvider;
415
+ const taskDir = path.join(input.root, "scripts", "tasks", input.taskId);
416
+ const plan = loadReleaseExecutionPlan({
417
+ root: input.root,
418
+ taskDir,
419
+ target: input.network,
420
+ executionId: input.executionId,
421
+ });
422
+ if (plan.taskId !== input.taskId) throw new Error("Execution plan taskId mismatch");
423
+ const packageEvidence = loadExternalPackage({ root: input.root, taskDir, plan });
424
+ const operatorResultPath = path.join(
425
+ taskDir,
426
+ "results",
427
+ plan.target,
428
+ plan.executionId,
429
+ "operator-execution.json",
430
+ );
431
+ const operatorResult = fs.existsSync(operatorResultPath)
432
+ ? loadOperatorExecutionResult({ taskDir, plan })
433
+ : undefined;
434
+ const reportFile = path.join(
435
+ taskDir,
436
+ "results",
437
+ input.network,
438
+ input.executionId,
439
+ "external-reconciliation.json",
440
+ );
441
+ const reportPath = relative(input.root, reportFile);
442
+ const previous = fs.existsSync(reportFile)
443
+ ? readJson(reportFile, "external reconciliation report") as ExternalReconciliationReport
444
+ : undefined;
445
+ if (
446
+ previous
447
+ && (
448
+ previous.version !== 1
449
+ || previous.taskId !== input.taskId
450
+ || previous.target !== input.network
451
+ || previous.executionId !== input.executionId
452
+ || previous.executionPlanHash !== plan.planHash
453
+ )
454
+ ) {
455
+ throw new Error("Persisted reconciliation report does not match this execution");
456
+ }
457
+ const target = resolveTarget(loadConfig(input.root), input.network);
458
+ const confirmations = resolveFinalityConfirmations(target, input.network);
459
+ const finality = await finalizedHeight(provider, confirmations);
460
+ const { finalizedBlock } = finality;
461
+ const matches = new Map<string, ExternalReconciliationMatch>();
462
+ let failure: ExternalReconciliationReport["failure"];
463
+
464
+ try {
465
+ for (const evidence of operatorResult?.receipts ?? []) {
466
+ const expected = plan.transactions.find((transaction) => (
467
+ transaction.transactionId === evidence.transactionId
468
+ ));
469
+ if (!expected || expected.route !== "operator") {
470
+ throw new Error(`Operator receipt ${evidence.transactionId} does not match the plan`);
471
+ }
472
+ const transaction = await provider.getTransaction(evidence.transactionHash);
473
+ const receipt = await provider.getTransactionReceipt(evidence.transactionHash);
474
+ if (!transaction || !receipt) continue;
475
+ await validateCanonicalReceipt(provider, receipt);
476
+ if (!transactionMatches(transaction, expected, expected.executor)) {
477
+ throw new Error(`Operator transaction ${expected.transactionId} does not match the plan`);
478
+ }
479
+ matches.set(expected.transactionId, receiptMatch(
480
+ expected,
481
+ transaction,
482
+ receipt,
483
+ finalizedBlock,
484
+ { executionKind: "eoa", submittedBy: transaction.from },
485
+ ));
486
+ }
487
+
488
+ for (const previousMatch of previous?.matches || []) {
489
+ if (previousMatch.route !== "external") continue;
490
+ const expected = plan.transactions[previousMatch.transactionIndex];
491
+ if (!expected || expected.transactionId !== previousMatch.transactionId) {
492
+ throw new Error("Persisted external match does not belong to the execution plan");
493
+ }
494
+ const transaction = await provider.getTransaction(previousMatch.transactionHash);
495
+ const receipt = await provider.getTransactionReceipt(previousMatch.transactionHash);
496
+ if (!transaction || !receipt) {
497
+ throw new Error(`Previously matched transaction disappeared: ${previousMatch.transactionHash}`);
498
+ }
499
+ await validateCanonicalReceipt(provider, receipt);
500
+ if (previousMatch.executionKind === "eoa") {
501
+ if (!transactionMatches(transaction, expected, expected.executor)) {
502
+ throw new Error(`Previously matched transaction changed: ${expected.transactionId}`);
503
+ }
504
+ } else {
505
+ const decoded = await safeCalls(provider, transaction, expected.executor);
506
+ const call = decoded[previousMatch.innerCallIndex || 0];
507
+ if (
508
+ !call
509
+ || call.to !== expected.to
510
+ || call.value !== expected.value
511
+ || call.data !== expected.data
512
+ || call.operation !== expected.operation
513
+ ) {
514
+ throw new Error(`Previously matched Safe transaction changed: ${expected.transactionId}`);
515
+ }
516
+ assertSafeExecutionSucceeded(receipt, expected.executor);
517
+ }
518
+ matches.set(expected.transactionId, receiptMatch(
519
+ expected,
520
+ transaction,
521
+ receipt,
522
+ finalizedBlock,
523
+ {
524
+ executionKind: previousMatch.executionKind,
525
+ submittedBy: transaction.from,
526
+ innerCallIndex: previousMatch.innerCallIndex,
527
+ },
528
+ ));
529
+ }
530
+ } catch (error) {
531
+ failure = {
532
+ code: "EVIDENCE_INVALID",
533
+ message: error instanceof Error ? error.message : String(error),
534
+ };
535
+ }
536
+
537
+ const external = plan.transactions.filter((transaction) => transaction.route === "external");
538
+ const previousSegments = new Map((previous?.segments ?? []).map((segment) => [
539
+ segment.segmentId,
540
+ segment,
541
+ ]));
542
+ const segmentProgress = packageEvidence?.package.segments.map((segment) => {
543
+ const persisted = previousSegments.get(segment.segmentId);
544
+ if (persisted && (
545
+ persisted.handoffBlock !== segment.handoffBlock
546
+ || JSON.stringify(persisted.transactionIds) !== JSON.stringify(segment.transactionIds)
547
+ )) {
548
+ throw new Error(`Persisted reconciliation segment ${segment.segmentId} changed`);
549
+ }
550
+ return {
551
+ segmentId: segment.segmentId,
552
+ transactionIds: segment.transactionIds,
553
+ handoffBlock: segment.handoffBlock,
554
+ nextBlock: persisted?.nextBlock ?? segment.handoffBlock,
555
+ };
556
+ });
557
+ if (previousSegments.size > (segmentProgress?.length ?? 0)) {
558
+ throw new Error("External execution package removed a reconciled segment");
559
+ }
560
+
561
+ const firstIncompleteBeforeScan = plan.transactions.find((transaction) => {
562
+ const match = matches.get(transaction.transactionId);
563
+ return !match || match.receiptStatus !== 1 || !match.finalized;
564
+ });
565
+ const activeSegment = firstIncompleteBeforeScan?.route === "external"
566
+ ? packageEvidence?.package.segments.find((segment) => (
567
+ segment.transactionIds.includes(firstIncompleteBeforeScan.transactionId)
568
+ ))
569
+ : undefined;
570
+ const activeProgress = activeSegment
571
+ ? segmentProgress?.find((segment) => segment.segmentId === activeSegment.segmentId)
572
+ : undefined;
573
+ const writeScanCheckpoint = (): void => {
574
+ const now = new Date().toISOString();
575
+ const checkpointMatches = [...matches.values()].sort((left, right) => (
576
+ left.transactionIndex - right.transactionIndex
577
+ ));
578
+ const checkpoint: ExternalReconciliationReport = {
579
+ version: 1,
580
+ status: "waiting_external",
581
+ taskId: input.taskId,
582
+ target: input.network,
583
+ executionId: input.executionId,
584
+ executionPlanHash: plan.planHash,
585
+ packagePath: packageEvidence ? relative(input.root, packageEvidence.filePath) : undefined,
586
+ packageHash: packageEvidence?.hash,
587
+ segments: segmentProgress,
588
+ finalizedBlock,
589
+ finalityPolicy: finality.policy,
590
+ matchedTransactionIds: checkpointMatches.map((match) => match.transactionId),
591
+ matches: checkpointMatches,
592
+ reportPath,
593
+ createdAt: previous?.createdAt || now,
594
+ updatedAt: now,
595
+ };
596
+ writeJsonAtomic(reportFile, checkpoint);
597
+ };
598
+ if (
599
+ !failure
600
+ && activeSegment
601
+ && activeProgress
602
+ && !matches.has(firstIncompleteBeforeScan!.transactionId)
603
+ ) {
604
+ const latest = await provider.getBlockNumber();
605
+ const batchSize = input.batchSize ?? 1000;
606
+ if (!Number.isInteger(batchSize) || batchSize < 1 || batchSize > 1000) {
607
+ throw new Error("Reconciliation batchSize must be between 1 and 1000");
608
+ }
609
+ const start = activeProgress.nextBlock;
610
+ const end = Math.min(latest, start + batchSize - 1);
611
+ const segmentTransactions = activeSegment.transactionIds.map((transactionId) => {
612
+ const transaction = plan.transactions.find((candidate) => (
613
+ candidate.transactionId === transactionId
614
+ ));
615
+ if (!transaction || transaction.route !== "external") {
616
+ throw new Error(`External segment transaction ${transactionId} is invalid`);
617
+ }
618
+ return transaction;
619
+ });
620
+ for (let blockNumber = start; blockNumber <= end; blockNumber += 1) {
621
+ let block: { transactions?: RpcTransaction[] } | null;
622
+ try {
623
+ block = await provider.send(
624
+ "eth_getBlockByNumber",
625
+ [`0x${blockNumber.toString(16)}`, true],
626
+ ) as { transactions?: RpcTransaction[] } | null;
627
+ } catch (error) {
628
+ writeScanCheckpoint();
629
+ throw error;
630
+ }
631
+ try {
632
+ for (const transaction of block?.transactions || []) {
633
+ const remaining = segmentTransactions.filter((candidate) => (
634
+ !matches.has(candidate.transactionId)
635
+ ));
636
+ const expected = remaining[0];
637
+ if (!expected) break;
638
+ const code = await provider.getCode(expected.executor);
639
+ if (!code || code === "0x") {
640
+ if (!transactionMatches(transaction, expected, expected.executor)) continue;
641
+ const receipt = await provider.getTransactionReceipt(transaction.hash);
642
+ if (!receipt) continue;
643
+ await validateCanonicalReceipt(provider, receipt);
644
+ matches.set(expected.transactionId, receiptMatch(
645
+ expected,
646
+ transaction,
647
+ receipt,
648
+ finalizedBlock,
649
+ { executionKind: "eoa", submittedBy: transaction.from },
650
+ ));
651
+ continue;
652
+ }
653
+ const decoded = await safeCalls(provider, transaction, expected.executor);
654
+ if (decoded.length === 0) continue;
655
+ const receipt = await provider.getTransactionReceipt(transaction.hash);
656
+ if (!receipt) continue;
657
+ await validateCanonicalReceipt(provider, receipt);
658
+ const matchedCalls: Array<{
659
+ transaction: ReleaseExecutionTransaction;
660
+ decodedCall: (typeof decoded)[number];
661
+ }> = [];
662
+ const remainingCalls = segmentTransactions.filter((item) => (
663
+ !matches.has(item.transactionId)
664
+ ));
665
+ for (const decodedCall of decoded) {
666
+ const candidate = remainingCalls[matchedCalls.length];
667
+ if (!candidate) break;
668
+ if (
669
+ decodedCall.to !== candidate.to
670
+ || decodedCall.value !== candidate.value
671
+ || decodedCall.data !== candidate.data
672
+ || decodedCall.operation !== candidate.operation
673
+ || candidate.executor !== expected.executor
674
+ ) {
675
+ break;
676
+ }
677
+ matchedCalls.push({ transaction: candidate, decodedCall });
678
+ }
679
+ if (matchedCalls.length === 0) continue;
680
+ assertSafeExecutionSucceeded(receipt, expected.executor);
681
+ for (const { transaction: candidate, decodedCall } of matchedCalls) {
682
+ matches.set(candidate.transactionId, receiptMatch(
683
+ candidate,
684
+ transaction,
685
+ receipt,
686
+ finalizedBlock,
687
+ {
688
+ executionKind: decodedCall.kind,
689
+ submittedBy: transaction.from,
690
+ innerCallIndex: decodedCall.innerCallIndex,
691
+ },
692
+ ));
693
+ }
694
+ }
695
+ activeProgress.nextBlock = blockNumber + 1;
696
+ writeScanCheckpoint();
697
+ } catch (error) {
698
+ failure = {
699
+ code: "SCAN_FAILED",
700
+ message: error instanceof Error ? error.message : String(error),
701
+ };
702
+ break;
703
+ }
704
+ }
705
+ }
706
+
707
+ const orderedMatches = [...matches.values()].sort((left, right) => (
708
+ left.transactionIndex - right.transactionIndex
709
+ ));
710
+ const reverted = orderedMatches.find((match) => match.receiptStatus === 0);
711
+ if (reverted) {
712
+ failure = {
713
+ code: "TRANSACTION_REVERTED",
714
+ message: `Transaction ${reverted.transactionHash} reverted`,
715
+ transactionId: reverted.transactionId,
716
+ };
717
+ }
718
+ const chainOrdered = [...orderedMatches].sort((left, right) => (
719
+ left.blockNumber - right.blockNumber
720
+ || left.blockTransactionIndex - right.blockTransactionIndex
721
+ || (left.innerCallIndex ?? -1) - (right.innerCallIndex ?? -1)
722
+ ));
723
+ if (chainOrdered.some((match, index) => (
724
+ index > 0 && chainOrdered[index - 1].transactionIndex >= match.transactionIndex
725
+ ))) {
726
+ failure = {
727
+ code: "PLAN_ORDER_VIOLATION",
728
+ message: "Canonical chain execution does not preserve immutable plan order",
729
+ };
730
+ }
731
+ const firstIncomplete = plan.transactions.find((transaction) => {
732
+ const match = matches.get(transaction.transactionId);
733
+ return !match || match.receiptStatus !== 1 || !match.finalized;
734
+ });
735
+ const incompleteMatch = firstIncomplete
736
+ ? matches.get(firstIncomplete.transactionId)
737
+ : undefined;
738
+ const submittedOperatorIds = new Set(
739
+ (operatorResult?.receipts ?? []).map((receipt) => receipt.transactionId),
740
+ );
741
+ const packageContainsIncomplete = Boolean(
742
+ firstIncomplete?.route === "external"
743
+ && packageEvidence?.package.segments.some((segment) => (
744
+ segment.transactionIds.includes(firstIncomplete.transactionId)
745
+ )),
746
+ );
747
+ const status: ExternalReconciliationReport["status"] = failure
748
+ ? "failed"
749
+ : !firstIncomplete
750
+ ? external.length > 0 ? "reconciled" : "not_required"
751
+ : incompleteMatch
752
+ ? "waiting_finality"
753
+ : firstIncomplete.route === "operator"
754
+ ? submittedOperatorIds.has(firstIncomplete.transactionId)
755
+ ? "waiting_finality"
756
+ : "waiting_operator"
757
+ : packageContainsIncomplete
758
+ ? "waiting_external"
759
+ : "waiting_operator";
760
+ const now = new Date().toISOString();
761
+ const report: ExternalReconciliationReport = {
762
+ version: 1,
763
+ status,
764
+ taskId: input.taskId,
765
+ target: input.network,
766
+ executionId: input.executionId,
767
+ executionPlanHash: plan.planHash,
768
+ packagePath: packageEvidence ? relative(input.root, packageEvidence.filePath) : undefined,
769
+ packageHash: packageEvidence?.hash,
770
+ segments: segmentProgress,
771
+ finalizedBlock,
772
+ finalityPolicy: finality.policy,
773
+ matchedTransactionIds: orderedMatches.map((match) => match.transactionId),
774
+ matches: orderedMatches,
775
+ failure,
776
+ reportPath,
777
+ createdAt: previous?.createdAt || now,
778
+ updatedAt: now,
779
+ completedAt: status === "reconciled" || status === "not_required" ? now : undefined,
780
+ };
781
+ const gateToolSyncByStep: Record<string, GateToolEvidence> = {};
782
+ if (status === "reconciled") {
783
+ const deployment = loadDeploymentExecutionRecord({
784
+ taskDir,
785
+ taskId: input.taskId,
786
+ network: input.network,
787
+ executionId: input.executionId,
788
+ });
789
+ const matchByTransactionId = new Map(
790
+ orderedMatches.map((match) => [match.transactionId, match]),
791
+ );
792
+ for (const change of deployment.changes) {
793
+ if (change.proxyKind !== "uups") continue;
794
+ if (
795
+ !change.proxyAddress
796
+ || !change.implementationAddress
797
+ || !Array.isArray(change.constructorArgs)
798
+ || !change.proxyTransactionId
799
+ ) {
800
+ throw new Error(`UUPS change ${change.stepId} is missing GateTool sync inputs`);
801
+ }
802
+ const candidates = plan.transactions.filter((transaction) => (
803
+ transaction.transactionId === change.proxyTransactionId
804
+ ));
805
+ if (candidates.length !== 1) {
806
+ throw new Error(`UUPS change ${change.stepId} has no unique finalized proxy receipt`);
807
+ }
808
+ const transaction = candidates[0];
809
+ if (transaction.stepId !== change.stepId) {
810
+ throw new Error(`UUPS change ${change.stepId} proxy receipt does not match its plan step`);
811
+ }
812
+ if (
813
+ change.kind === "upgrade"
814
+ && transaction.to.toLowerCase() !== change.proxyAddress.toLowerCase()
815
+ ) {
816
+ throw new Error(`UUPS change ${change.stepId} receipt does not target its proxy`);
817
+ }
818
+ if (
819
+ change.kind === "deploy"
820
+ && (
821
+ !change.multicall
822
+ || transaction.to.toLowerCase() !== change.multicall.toLowerCase()
823
+ )
824
+ ) {
825
+ throw new Error(`UUPS change ${change.stepId} receipt does not target its controlled multicall`);
826
+ }
827
+ const match = matchByTransactionId.get(transaction.transactionId);
828
+ if (!match || match.receiptStatus !== 1 || !match.finalized) {
829
+ throw new Error(`UUPS change ${change.stepId} receipt is not finalized`);
830
+ }
831
+ const gateToolSync = await (
832
+ input.dependencies?.syncGateToolUups ?? syncGateToolUups
833
+ )({
834
+ root: input.root,
835
+ evidenceDir: path.join(
836
+ taskDir,
837
+ "results",
838
+ input.network,
839
+ input.executionId,
840
+ "gate-tool",
841
+ "execute",
842
+ ),
843
+ evidenceName: change.stepId,
844
+ scheme: transaction.scheme || "evm",
845
+ network: input.network,
846
+ target: input.network,
847
+ manifestDir: target.openzeppelin.manifestDir,
848
+ proxy: change.proxyAddress,
849
+ implementation: change.implementationAddress,
850
+ contract: change.actualContract,
851
+ constructorArgs: change.constructorArgs,
852
+ transactionHash: match.transactionHash,
853
+ hre: input.gateToolSyncHre,
854
+ });
855
+ if (
856
+ change.gateToolSync
857
+ && JSON.stringify(change.gateToolSync) !== JSON.stringify(gateToolSync)
858
+ ) {
859
+ throw new Error(
860
+ `UUPS change ${change.stepId} stored GateTool sync evidence does not match the finalized receipt`,
861
+ );
862
+ }
863
+ gateToolSyncByStep[change.stepId] = gateToolSync;
864
+ }
865
+ }
866
+ const normalizedReport = JSON.parse(
867
+ JSON.stringify(report),
868
+ ) as ExternalReconciliationReport;
869
+ const persistedReport = previous?.status === "reconciled"
870
+ && normalizedReport.status === "reconciled"
871
+ && previous.packagePath === normalizedReport.packagePath
872
+ && previous.packageHash === normalizedReport.packageHash
873
+ && JSON.stringify(previous.segments) === JSON.stringify(normalizedReport.segments)
874
+ && JSON.stringify(previous.matchedTransactionIds)
875
+ === JSON.stringify(normalizedReport.matchedTransactionIds)
876
+ && JSON.stringify(previous.matches) === JSON.stringify(normalizedReport.matches)
877
+ ? previous
878
+ : normalizedReport;
879
+ if (persistedReport !== previous) writeJsonAtomic(reportFile, persistedReport);
880
+ if (status === "reconciled") {
881
+ markDeploymentRecordReconciled({
882
+ taskDir,
883
+ taskId: input.taskId,
884
+ network: input.network,
885
+ executionId: input.executionId,
886
+ reportPath,
887
+ reportHash: sha256(fs.readFileSync(reportFile)),
888
+ gateToolSyncByStep,
889
+ });
890
+ }
891
+ return persistedReport;
892
+ };