gate-evm-tools 1.0.25 → 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.
- package/README.md +33 -11
- package/bin/workflow.cjs +19 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +21 -57
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/checkBatch.d.ts +4 -0
- package/dist/commands/checkBatch.d.ts.map +1 -0
- package/dist/commands/checkBatch.js +57 -0
- package/dist/commands/checkBatch.js.map +1 -0
- package/dist/commands/checkOptions.d.ts +3 -0
- package/dist/commands/checkOptions.d.ts.map +1 -1
- package/dist/commands/checkOptions.js +10 -1
- package/dist/commands/checkOptions.js.map +1 -1
- package/dist/commands/prepare.d.ts +1 -2
- package/dist/commands/prepare.d.ts.map +1 -1
- package/dist/commands/prepare.js +2 -17
- package/dist/commands/prepare.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +2 -17
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +2 -17
- package/dist/commands/validate.js.map +1 -1
- package/dist/core/BytecodeChecker.d.ts.map +1 -1
- package/dist/core/BytecodeChecker.js +16 -1
- package/dist/core/BytecodeChecker.js.map +1 -1
- package/dist/core/legacyProxyArtifact.d.ts +6 -0
- package/dist/core/legacyProxyArtifact.d.ts.map +1 -0
- package/dist/core/legacyProxyArtifact.js +36 -0
- package/dist/core/legacyProxyArtifact.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/hardhat.d.ts +4 -0
- package/dist/utils/hardhat.d.ts.map +1 -0
- package/dist/utils/hardhat.js +9 -0
- package/dist/utils/hardhat.js.map +1 -0
- package/dist-runtime/config/hardhatAdapters.d.ts +5 -0
- package/dist-runtime/config/hardhatAdapters.js +53 -0
- package/dist-runtime/config/hardhatAdapters.js.map +1 -0
- package/dist-runtime/config/networks.d.ts +8 -0
- package/dist-runtime/config/networks.js +269 -0
- package/dist-runtime/config/networks.js.map +1 -0
- package/dist-runtime/config/openzeppelin.d.ts +28 -0
- package/dist-runtime/config/openzeppelin.js +144 -0
- package/dist-runtime/config/openzeppelin.js.map +1 -0
- package/dist-runtime/config/rpc.d.ts +7 -0
- package/dist-runtime/config/rpc.js +19 -0
- package/dist-runtime/config/rpc.js.map +1 -0
- package/dist-runtime/config/zksyncSolcPath.d.ts +1 -0
- package/dist-runtime/config/zksyncSolcPath.js +61 -0
- package/dist-runtime/config/zksyncSolcPath.js.map +1 -0
- package/dist-runtime/migrate.d.ts +13 -0
- package/dist-runtime/migrate.js +208 -0
- package/dist-runtime/migrate.js.map +1 -0
- package/dist-runtime/package.json +1 -0
- package/dist-runtime/projectHardhat.d.ts +4 -0
- package/dist-runtime/projectHardhat.js +16 -0
- package/dist-runtime/projectHardhat.js.map +1 -0
- package/dist-runtime/runtime-identity.json +4 -0
- package/dist-runtime/template-migration.json +293 -0
- package/dist-runtime/workflow/checkCode.d.ts +1 -0
- package/dist-runtime/workflow/checkCode.js +16 -0
- package/dist-runtime/workflow/checkCode.js.map +1 -0
- package/dist-runtime/workflow/checkCodeFinalize.d.ts +1 -0
- package/dist-runtime/workflow/checkCodeFinalize.js +11 -0
- package/dist-runtime/workflow/checkCodeFinalize.js.map +1 -0
- package/dist-runtime/workflow/checkCodePlan.d.ts +1 -0
- package/dist-runtime/workflow/checkCodePlan.js +11 -0
- package/dist-runtime/workflow/checkCodePlan.js.map +1 -0
- package/dist-runtime/workflow/cli.d.ts +1 -0
- package/dist-runtime/workflow/cli.js +302 -0
- package/dist-runtime/workflow/cli.js.map +1 -0
- package/dist-runtime/workflow/commitContractInfo.d.ts +1 -0
- package/dist-runtime/workflow/commitContractInfo.js +64 -0
- package/dist-runtime/workflow/commitContractInfo.js.map +1 -0
- package/dist-runtime/workflow/create2Hashes.d.ts +1 -0
- package/dist-runtime/workflow/create2Hashes.js +76 -0
- package/dist-runtime/workflow/create2Hashes.js.map +1 -0
- package/dist-runtime/workflow/forkSourceProxy.d.ts +1 -0
- package/dist-runtime/workflow/forkSourceProxy.js +151 -0
- package/dist-runtime/workflow/forkSourceProxy.js.map +1 -0
- package/dist-runtime/workflow/importState.d.ts +1 -0
- package/dist-runtime/workflow/importState.js +193 -0
- package/dist-runtime/workflow/importState.js.map +1 -0
- package/dist-runtime/workflow/lib/authority.d.ts +28 -0
- package/dist-runtime/workflow/lib/authority.js +122 -0
- package/dist-runtime/workflow/lib/authority.js.map +1 -0
- package/dist-runtime/workflow/lib/calldata.d.ts +2 -0
- package/dist-runtime/workflow/lib/calldata.js +8 -0
- package/dist-runtime/workflow/lib/calldata.js.map +1 -0
- package/dist-runtime/workflow/lib/calls.d.ts +23 -0
- package/dist-runtime/workflow/lib/calls.js +316 -0
- package/dist-runtime/workflow/lib/calls.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/args.d.ts +2 -0
- package/dist-runtime/workflow/lib/checkCode/args.js +68 -0
- package/dist-runtime/workflow/lib/checkCode/args.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/command.d.ts +5 -0
- package/dist-runtime/workflow/lib/checkCode/command.js +35 -0
- package/dist-runtime/workflow/lib/checkCode/command.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/dependencies.d.ts +6 -0
- package/dist-runtime/workflow/lib/checkCode/dependencies.js +125 -0
- package/dist-runtime/workflow/lib/checkCode/dependencies.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/finalize.d.ts +17 -0
- package/dist-runtime/workflow/lib/checkCode/finalize.js +174 -0
- package/dist-runtime/workflow/lib/checkCode/finalize.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/index.d.ts +3 -0
- package/dist-runtime/workflow/lib/checkCode/index.js +20 -0
- package/dist-runtime/workflow/lib/checkCode/index.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/plan.d.ts +22 -0
- package/dist-runtime/workflow/lib/checkCode/plan.js +145 -0
- package/dist-runtime/workflow/lib/checkCode/plan.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCodeReport.d.ts +25 -0
- package/dist-runtime/workflow/lib/checkCodeReport.js +102 -0
- package/dist-runtime/workflow/lib/checkCodeReport.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCodeRunner.d.ts +87 -0
- package/dist-runtime/workflow/lib/checkCodeRunner.js +964 -0
- package/dist-runtime/workflow/lib/checkCodeRunner.js.map +1 -0
- package/dist-runtime/workflow/lib/cliTool.d.ts +10 -0
- package/dist-runtime/workflow/lib/cliTool.js +25 -0
- package/dist-runtime/workflow/lib/cliTool.js.map +1 -0
- package/dist-runtime/workflow/lib/config.d.ts +33 -0
- package/dist-runtime/workflow/lib/config.js +100 -0
- package/dist-runtime/workflow/lib/config.js.map +1 -0
- package/dist-runtime/workflow/lib/contractInfoCandidates.d.ts +82 -0
- package/dist-runtime/workflow/lib/contractInfoCandidates.js +441 -0
- package/dist-runtime/workflow/lib/contractInfoCandidates.js.map +1 -0
- package/dist-runtime/workflow/lib/create2.d.ts +49 -0
- package/dist-runtime/workflow/lib/create2.js +194 -0
- package/dist-runtime/workflow/lib/create2.js.map +1 -0
- package/dist-runtime/workflow/lib/create2Hashes.d.ts +22 -0
- package/dist-runtime/workflow/lib/create2Hashes.js +57 -0
- package/dist-runtime/workflow/lib/create2Hashes.js.map +1 -0
- package/dist-runtime/workflow/lib/deploymentInfo.d.ts +40 -0
- package/dist-runtime/workflow/lib/deploymentInfo.js +240 -0
- package/dist-runtime/workflow/lib/deploymentInfo.js.map +1 -0
- package/dist-runtime/workflow/lib/deploymentRecords.d.ts +137 -0
- package/dist-runtime/workflow/lib/deploymentRecords.js +374 -0
- package/dist-runtime/workflow/lib/deploymentRecords.js.map +1 -0
- package/dist-runtime/workflow/lib/deployments.d.ts +21 -0
- package/dist-runtime/workflow/lib/deployments.js +240 -0
- package/dist-runtime/workflow/lib/deployments.js.map +1 -0
- package/dist-runtime/workflow/lib/erc1967.d.ts +5 -0
- package/dist-runtime/workflow/lib/erc1967.js +29 -0
- package/dist-runtime/workflow/lib/erc1967.js.map +1 -0
- package/dist-runtime/workflow/lib/errors.d.ts +6 -0
- package/dist-runtime/workflow/lib/errors.js +77 -0
- package/dist-runtime/workflow/lib/errors.js.map +1 -0
- package/dist-runtime/workflow/lib/executionId.d.ts +7 -0
- package/dist-runtime/workflow/lib/executionId.js +26 -0
- package/dist-runtime/workflow/lib/executionId.js.map +1 -0
- package/dist-runtime/workflow/lib/executionPlan.d.ts +85 -0
- package/dist-runtime/workflow/lib/executionPlan.js +450 -0
- package/dist-runtime/workflow/lib/executionPlan.js.map +1 -0
- package/dist-runtime/workflow/lib/executionProgress.d.ts +5 -0
- package/dist-runtime/workflow/lib/executionProgress.js +100 -0
- package/dist-runtime/workflow/lib/executionProgress.js.map +1 -0
- package/dist-runtime/workflow/lib/external.d.ts +62 -0
- package/dist-runtime/workflow/lib/external.js +282 -0
- package/dist-runtime/workflow/lib/external.js.map +1 -0
- package/dist-runtime/workflow/lib/externalExecutionScanner.d.ts +64 -0
- package/dist-runtime/workflow/lib/externalExecutionScanner.js +638 -0
- package/dist-runtime/workflow/lib/externalExecutionScanner.js.map +1 -0
- package/dist-runtime/workflow/lib/forkSnapshot.d.ts +34 -0
- package/dist-runtime/workflow/lib/forkSnapshot.js +167 -0
- package/dist-runtime/workflow/lib/forkSnapshot.js.map +1 -0
- package/dist-runtime/workflow/lib/gateTool.d.ts +16 -0
- package/dist-runtime/workflow/lib/gateTool.js +177 -0
- package/dist-runtime/workflow/lib/gateTool.js.map +1 -0
- package/dist-runtime/workflow/lib/gateToolSync.d.ts +22 -0
- package/dist-runtime/workflow/lib/gateToolSync.js +253 -0
- package/dist-runtime/workflow/lib/gateToolSync.js.map +1 -0
- package/dist-runtime/workflow/lib/lock.d.ts +52 -0
- package/dist-runtime/workflow/lib/lock.js +269 -0
- package/dist-runtime/workflow/lib/lock.js.map +1 -0
- package/dist-runtime/workflow/lib/migrationState.d.ts +103 -0
- package/dist-runtime/workflow/lib/migrationState.js +920 -0
- package/dist-runtime/workflow/lib/migrationState.js.map +1 -0
- package/dist-runtime/workflow/lib/operatorExecution.d.ts +28 -0
- package/dist-runtime/workflow/lib/operatorExecution.js +134 -0
- package/dist-runtime/workflow/lib/operatorExecution.js.map +1 -0
- package/dist-runtime/workflow/lib/parameters.d.ts +58 -0
- package/dist-runtime/workflow/lib/parameters.js +322 -0
- package/dist-runtime/workflow/lib/parameters.js.map +1 -0
- package/dist-runtime/workflow/lib/pendingDeploymentReuse.d.ts +29 -0
- package/dist-runtime/workflow/lib/pendingDeploymentReuse.js +277 -0
- package/dist-runtime/workflow/lib/pendingDeploymentReuse.js.map +1 -0
- package/dist-runtime/workflow/lib/proxy.d.ts +18 -0
- package/dist-runtime/workflow/lib/proxy.js +19 -0
- package/dist-runtime/workflow/lib/proxy.js.map +1 -0
- package/dist-runtime/workflow/lib/registryCommit.d.ts +33 -0
- package/dist-runtime/workflow/lib/registryCommit.js +319 -0
- package/dist-runtime/workflow/lib/registryCommit.js.map +1 -0
- package/dist-runtime/workflow/lib/release.d.ts +57 -0
- package/dist-runtime/workflow/lib/release.js +256 -0
- package/dist-runtime/workflow/lib/release.js.map +1 -0
- package/dist-runtime/workflow/lib/releaseFinalize.d.ts +14 -0
- package/dist-runtime/workflow/lib/releaseFinalize.js +571 -0
- package/dist-runtime/workflow/lib/releaseFinalize.js.map +1 -0
- package/dist-runtime/workflow/lib/releaseParameters.d.ts +43 -0
- package/dist-runtime/workflow/lib/releaseParameters.js +184 -0
- package/dist-runtime/workflow/lib/releaseParameters.js.map +1 -0
- package/dist-runtime/workflow/lib/reporter.d.ts +39 -0
- package/dist-runtime/workflow/lib/reporter.js +452 -0
- package/dist-runtime/workflow/lib/reporter.js.map +1 -0
- package/dist-runtime/workflow/lib/resultPaths.d.ts +17 -0
- package/dist-runtime/workflow/lib/resultPaths.js +29 -0
- package/dist-runtime/workflow/lib/resultPaths.js.map +1 -0
- package/dist-runtime/workflow/lib/runtimeVersion.d.ts +10 -0
- package/dist-runtime/workflow/lib/runtimeVersion.js +46 -0
- package/dist-runtime/workflow/lib/runtimeVersion.js.map +1 -0
- package/dist-runtime/workflow/lib/signer.d.ts +10 -0
- package/dist-runtime/workflow/lib/signer.js +105 -0
- package/dist-runtime/workflow/lib/signer.js.map +1 -0
- package/dist-runtime/workflow/lib/signerIdentity.d.ts +1 -0
- package/dist-runtime/workflow/lib/signerIdentity.js +23 -0
- package/dist-runtime/workflow/lib/signerIdentity.js.map +1 -0
- package/dist-runtime/workflow/lib/simulation.d.ts +35 -0
- package/dist-runtime/workflow/lib/simulation.js +282 -0
- package/dist-runtime/workflow/lib/simulation.js.map +1 -0
- package/dist-runtime/workflow/lib/simulationCallers.d.ts +44 -0
- package/dist-runtime/workflow/lib/simulationCallers.js +260 -0
- package/dist-runtime/workflow/lib/simulationCallers.js.map +1 -0
- package/dist-runtime/workflow/lib/sourceRpcPolicy.d.ts +1 -0
- package/dist-runtime/workflow/lib/sourceRpcPolicy.js +43 -0
- package/dist-runtime/workflow/lib/sourceRpcPolicy.js.map +1 -0
- package/dist-runtime/workflow/lib/state.d.ts +6 -0
- package/dist-runtime/workflow/lib/state.js +25 -0
- package/dist-runtime/workflow/lib/state.js.map +1 -0
- package/dist-runtime/workflow/lib/staticValidation.d.ts +22 -0
- package/dist-runtime/workflow/lib/staticValidation.js +124 -0
- package/dist-runtime/workflow/lib/staticValidation.js.map +1 -0
- package/dist-runtime/workflow/lib/zksyncForkCapability.d.ts +27 -0
- package/dist-runtime/workflow/lib/zksyncForkCapability.js +52 -0
- package/dist-runtime/workflow/lib/zksyncForkCapability.js.map +1 -0
- package/dist-runtime/workflow/lib/zksyncForkRunner.d.ts +57 -0
- package/dist-runtime/workflow/lib/zksyncForkRunner.js +344 -0
- package/dist-runtime/workflow/lib/zksyncForkRunner.js.map +1 -0
- package/dist-runtime/workflow/reconcileExternal.d.ts +1 -0
- package/dist-runtime/workflow/reconcileExternal.js +87 -0
- package/dist-runtime/workflow/reconcileExternal.js.map +1 -0
- package/dist-runtime/workflow/reconcileExternalRun.d.ts +1 -0
- package/dist-runtime/workflow/reconcileExternalRun.js +77 -0
- package/dist-runtime/workflow/reconcileExternalRun.js.map +1 -0
- package/dist-runtime/workflow/releaseFinalize.d.ts +1 -0
- package/dist-runtime/workflow/releaseFinalize.js +11 -0
- package/dist-runtime/workflow/releaseFinalize.js.map +1 -0
- package/dist-runtime/workflow/run.d.ts +40 -0
- package/dist-runtime/workflow/run.js +858 -0
- package/dist-runtime/workflow/run.js.map +1 -0
- package/dist-runtime/workflow/staticValidate.d.ts +1 -0
- package/dist-runtime/workflow/staticValidate.js +26 -0
- package/dist-runtime/workflow/staticValidate.js.map +1 -0
- package/dist-runtime/workflow/steps/call.d.ts +3 -0
- package/dist-runtime/workflow/steps/call.js +9 -0
- package/dist-runtime/workflow/steps/call.js.map +1 -0
- package/dist-runtime/workflow/steps/check.d.ts +3 -0
- package/dist-runtime/workflow/steps/check.js +9 -0
- package/dist-runtime/workflow/steps/check.js.map +1 -0
- package/dist-runtime/workflow/steps/custom.d.ts +3 -0
- package/dist-runtime/workflow/steps/custom.js +33 -0
- package/dist-runtime/workflow/steps/custom.js.map +1 -0
- package/dist-runtime/workflow/steps/deploy.d.ts +3 -0
- package/dist-runtime/workflow/steps/deploy.js +595 -0
- package/dist-runtime/workflow/steps/deploy.js.map +1 -0
- package/dist-runtime/workflow/steps/upgrade.d.ts +3 -0
- package/dist-runtime/workflow/steps/upgrade.js +465 -0
- package/dist-runtime/workflow/steps/upgrade.js.map +1 -0
- package/dist-runtime/workflow/steps/view.d.ts +3 -0
- package/dist-runtime/workflow/steps/view.js +9 -0
- package/dist-runtime/workflow/steps/view.js.map +1 -0
- package/package.json +26 -5
- package/runtime/config/hardhatAdapters.ts +50 -0
- package/runtime/config/networks.ts +296 -0
- package/runtime/config/openzeppelin.ts +173 -0
- package/runtime/config/rpc.ts +27 -0
- package/runtime/config/zksyncSolcPath.ts +59 -0
- package/runtime/hardhat-types.d.ts +2 -0
- package/runtime/migrate.ts +154 -0
- package/runtime/projectHardhat.ts +10 -0
- package/runtime/template-migration.json +293 -0
- package/runtime/workflow/checkCode.ts +13 -0
- package/runtime/workflow/checkCodeFinalize.ts +8 -0
- package/runtime/workflow/checkCodePlan.ts +8 -0
- package/runtime/workflow/cli.ts +356 -0
- package/runtime/workflow/commitContractInfo.ts +63 -0
- package/runtime/workflow/create2Hashes.ts +79 -0
- package/runtime/workflow/forkSourceProxy.ts +193 -0
- package/runtime/workflow/importState.ts +190 -0
- package/runtime/workflow/lib/authority.ts +170 -0
- package/runtime/workflow/lib/calldata.ts +9 -0
- package/runtime/workflow/lib/calls.ts +407 -0
- package/runtime/workflow/lib/checkCode/args.ts +76 -0
- package/runtime/workflow/lib/checkCode/command.ts +36 -0
- package/runtime/workflow/lib/checkCode/dependencies.ts +112 -0
- package/runtime/workflow/lib/checkCode/finalize.ts +198 -0
- package/runtime/workflow/lib/checkCode/index.ts +18 -0
- package/runtime/workflow/lib/checkCode/plan.ts +163 -0
- package/runtime/workflow/lib/checkCodeReport.ts +133 -0
- package/runtime/workflow/lib/checkCodeRunner.ts +1225 -0
- package/runtime/workflow/lib/cliTool.ts +31 -0
- package/runtime/workflow/lib/config.ts +130 -0
- package/runtime/workflow/lib/contractInfoCandidates.ts +690 -0
- package/runtime/workflow/lib/create2.ts +278 -0
- package/runtime/workflow/lib/create2Hashes.ts +83 -0
- package/runtime/workflow/lib/deploymentInfo.ts +286 -0
- package/runtime/workflow/lib/deploymentRecords.ts +576 -0
- package/runtime/workflow/lib/deployments.ts +276 -0
- package/runtime/workflow/lib/erc1967.ts +57 -0
- package/runtime/workflow/lib/errors.ts +95 -0
- package/runtime/workflow/lib/executionId.ts +35 -0
- package/runtime/workflow/lib/executionPlan.ts +550 -0
- package/runtime/workflow/lib/executionProgress.ts +127 -0
- package/runtime/workflow/lib/external.ts +372 -0
- package/runtime/workflow/lib/externalExecutionScanner.ts +892 -0
- package/runtime/workflow/lib/forkSnapshot.ts +231 -0
- package/runtime/workflow/lib/gateTool.ts +261 -0
- package/runtime/workflow/lib/gateToolSync.ts +398 -0
- package/runtime/workflow/lib/lock.ts +388 -0
- package/runtime/workflow/lib/migrationState.ts +1246 -0
- package/runtime/workflow/lib/operatorExecution.ts +174 -0
- package/runtime/workflow/lib/parameters.ts +437 -0
- package/runtime/workflow/lib/pendingDeploymentReuse.ts +428 -0
- package/runtime/workflow/lib/proxy.ts +45 -0
- package/runtime/workflow/lib/registryCommit.ts +455 -0
- package/runtime/workflow/lib/release.ts +329 -0
- package/runtime/workflow/lib/releaseFinalize.ts +711 -0
- package/runtime/workflow/lib/releaseParameters.ts +248 -0
- package/runtime/workflow/lib/reporter.ts +502 -0
- package/runtime/workflow/lib/resultPaths.ts +49 -0
- package/runtime/workflow/lib/runtimeVersion.ts +28 -0
- package/runtime/workflow/lib/signer.ts +124 -0
- package/runtime/workflow/lib/signerIdentity.ts +22 -0
- package/runtime/workflow/lib/simulation.ts +415 -0
- package/runtime/workflow/lib/simulationCallers.ts +335 -0
- package/runtime/workflow/lib/sourceRpcPolicy.ts +41 -0
- package/runtime/workflow/lib/state.ts +25 -0
- package/runtime/workflow/lib/staticValidation.ts +142 -0
- package/runtime/workflow/lib/zksyncForkCapability.ts +80 -0
- package/runtime/workflow/lib/zksyncForkRunner.ts +505 -0
- package/runtime/workflow/reconcileExternal.ts +86 -0
- package/runtime/workflow/reconcileExternalRun.ts +88 -0
- package/runtime/workflow/releaseFinalize.ts +8 -0
- package/runtime/workflow/run.ts +1072 -0
- package/runtime/workflow/staticValidate.ts +23 -0
- package/runtime/workflow/steps/call.ts +10 -0
- package/runtime/workflow/steps/check.ts +10 -0
- package/runtime/workflow/steps/custom.ts +43 -0
- package/runtime/workflow/steps/deploy.ts +771 -0
- package/runtime/workflow/steps/upgrade.ts +598 -0
- package/runtime/workflow/steps/view.ts +10 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import type { WorkflowConfig } from "./config";
|
|
4
|
+
import { readJsonFile, writeJsonFile } from "./config";
|
|
5
|
+
import {
|
|
6
|
+
cloneContractInfo,
|
|
7
|
+
ContractInfo,
|
|
8
|
+
loadContractInfo,
|
|
9
|
+
planDeploymentAddress,
|
|
10
|
+
resolveDeployment,
|
|
11
|
+
saveContractInfo,
|
|
12
|
+
validateContractInfo,
|
|
13
|
+
withContractInfoLock,
|
|
14
|
+
} from "./deployments";
|
|
15
|
+
import { validateExecutionId } from "./executionId";
|
|
16
|
+
import {
|
|
17
|
+
prepareDeploymentInfoFromRecords,
|
|
18
|
+
saveDeploymentInfo,
|
|
19
|
+
type DeploymentRecordPromotionInput,
|
|
20
|
+
} from "./deploymentInfo";
|
|
21
|
+
import { hashValue } from "./release";
|
|
22
|
+
|
|
23
|
+
export interface ContractInfoCandidateReplacement {
|
|
24
|
+
network: string;
|
|
25
|
+
instanceId: string;
|
|
26
|
+
previousKey?: string;
|
|
27
|
+
previousAddress?: string;
|
|
28
|
+
candidateKey: string;
|
|
29
|
+
candidateAddress: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ContractInfoCandidate {
|
|
33
|
+
version: 1;
|
|
34
|
+
status: "pending" | "promoted";
|
|
35
|
+
deploymentId: string;
|
|
36
|
+
executionId: string;
|
|
37
|
+
network: string;
|
|
38
|
+
baseContractInfoHash: string;
|
|
39
|
+
backupPath: string;
|
|
40
|
+
replacements: ContractInfoCandidateReplacement[];
|
|
41
|
+
contractInfo: ContractInfo;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
updatedAt: string;
|
|
44
|
+
promotedAt?: string;
|
|
45
|
+
promotedContractInfoHash?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface StageContractInfoCandidateOptions {
|
|
49
|
+
root: string;
|
|
50
|
+
taskDir: string;
|
|
51
|
+
config: WorkflowConfig;
|
|
52
|
+
deploymentId: string;
|
|
53
|
+
executionId: string;
|
|
54
|
+
network: string;
|
|
55
|
+
supersedeCandidatePath?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface StageContractInfoReplacement {
|
|
59
|
+
instanceId: string;
|
|
60
|
+
contractName: string;
|
|
61
|
+
address: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface StagedContractInfoCandidate {
|
|
65
|
+
candidate: ContractInfoCandidate;
|
|
66
|
+
candidatePath: string;
|
|
67
|
+
backupPath: string;
|
|
68
|
+
replacement: ContractInfoCandidateReplacement;
|
|
69
|
+
archivedCandidatePath?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PromoteContractInfoCandidatesOptions {
|
|
73
|
+
root: string;
|
|
74
|
+
config: WorkflowConfig;
|
|
75
|
+
candidatePaths: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface PromotedContractInfoCandidates {
|
|
79
|
+
contractInfo: ContractInfo;
|
|
80
|
+
beforeHash: string;
|
|
81
|
+
afterHash: string;
|
|
82
|
+
deploymentInfoPath: string;
|
|
83
|
+
deploymentInfoHash: string;
|
|
84
|
+
changedEntries: ContractInfoCandidateReplacement[];
|
|
85
|
+
promoted: Array<{
|
|
86
|
+
candidatePath: string;
|
|
87
|
+
candidate: ContractInfoCandidate;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface PreviewedContractInfoCandidates {
|
|
92
|
+
contractInfo: ContractInfo;
|
|
93
|
+
candidates: Array<{
|
|
94
|
+
candidatePath: string;
|
|
95
|
+
candidate: ContractInfoCandidate;
|
|
96
|
+
}>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface FindExecutionCandidateAddressOptions {
|
|
100
|
+
root: string;
|
|
101
|
+
deploymentId: string;
|
|
102
|
+
executionId: string;
|
|
103
|
+
network: string;
|
|
104
|
+
name: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => {
|
|
108
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const safeNetworkFileId = (value: string): string => {
|
|
112
|
+
const normalized = value.trim();
|
|
113
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(normalized)) {
|
|
114
|
+
throw new Error("network must contain only letters, numbers, underscores, or hyphens");
|
|
115
|
+
}
|
|
116
|
+
return normalized;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const relativeProjectPath = (root: string, absolutePath: string): string => {
|
|
120
|
+
const relative = path.relative(root, absolutePath);
|
|
121
|
+
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
122
|
+
throw new Error("Candidate path must stay inside the project root: " + absolutePath);
|
|
123
|
+
}
|
|
124
|
+
return relative.split(path.sep).join("/");
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const resolveProjectPath = (root: string, filePath: string): string => {
|
|
128
|
+
const absolute = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(root, filePath);
|
|
129
|
+
const relative = path.relative(path.resolve(root), absolute);
|
|
130
|
+
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
131
|
+
throw new Error("Candidate path must stay inside the project root: " + filePath);
|
|
132
|
+
}
|
|
133
|
+
return absolute;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const writeExclusiveJson = (filePath: string, value: unknown): void => {
|
|
137
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
138
|
+
let file: number;
|
|
139
|
+
try {
|
|
140
|
+
file = fs.openSync(filePath, "wx");
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (isRecord(error) && String(error.code) === "EEXIST") {
|
|
143
|
+
throw new Error("Refusing to overwrite existing contractInfo backup: " + filePath);
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
fs.writeFileSync(file, JSON.stringify(value, null, 2) + "\n");
|
|
149
|
+
fs.fsyncSync(file);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
fs.closeSync(file);
|
|
152
|
+
fs.unlinkSync(filePath);
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
fs.closeSync(file);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const validateReplacement = (
|
|
159
|
+
value: unknown,
|
|
160
|
+
label: string,
|
|
161
|
+
): ContractInfoCandidateReplacement => {
|
|
162
|
+
if (!isRecord(value)) throw new Error(label + " must be an object");
|
|
163
|
+
for (const key of ["network", "instanceId", "candidateKey", "candidateAddress"] as const) {
|
|
164
|
+
if (typeof value[key] !== "string" || !String(value[key]).trim()) {
|
|
165
|
+
throw new Error(label + "." + key + " must be a non-empty string");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return value as unknown as ContractInfoCandidateReplacement;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const validateContractInfoCandidate = (value: unknown): ContractInfoCandidate => {
|
|
172
|
+
if (!isRecord(value)) throw new Error("contractInfo candidate must be an object");
|
|
173
|
+
if (value.version !== 1) throw new Error("contractInfo candidate version must be 1");
|
|
174
|
+
if (value.status !== "pending" && value.status !== "promoted") {
|
|
175
|
+
throw new Error("contractInfo candidate status must be pending or promoted");
|
|
176
|
+
}
|
|
177
|
+
for (const key of [
|
|
178
|
+
"deploymentId",
|
|
179
|
+
"network",
|
|
180
|
+
"baseContractInfoHash",
|
|
181
|
+
"backupPath",
|
|
182
|
+
"createdAt",
|
|
183
|
+
"updatedAt",
|
|
184
|
+
] as const) {
|
|
185
|
+
if (typeof value[key] !== "string" || !String(value[key]).trim()) {
|
|
186
|
+
throw new Error("contractInfo candidate " + key + " must be a non-empty string");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
validateExecutionId(value.executionId, "contractInfo candidate executionId");
|
|
190
|
+
if (!Array.isArray(value.replacements)) {
|
|
191
|
+
throw new Error("contractInfo candidate replacements must be an array");
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
...(value as unknown as ContractInfoCandidate),
|
|
195
|
+
replacements: value.replacements.map((item, index) => (
|
|
196
|
+
validateReplacement(item, "contractInfo candidate replacements[" + index + "]")
|
|
197
|
+
)),
|
|
198
|
+
contractInfo: validateContractInfo(value.contractInfo),
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const candidateRelativePath = (executionId: string): string => {
|
|
203
|
+
return "contractInfo.pending." + validateExecutionId(executionId) + ".json";
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const backupAbsolutePath = (
|
|
207
|
+
options: StageContractInfoCandidateOptions,
|
|
208
|
+
): string => {
|
|
209
|
+
const network = safeNetworkFileId(options.network);
|
|
210
|
+
const executionId = validateExecutionId(options.executionId);
|
|
211
|
+
return path.join(
|
|
212
|
+
options.taskDir,
|
|
213
|
+
"results",
|
|
214
|
+
network + "." + executionId + ".contractInfo.before.json",
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const readCandidate = (filePath: string): ContractInfoCandidate => {
|
|
219
|
+
return validateContractInfoCandidate(readJsonFile<unknown>(filePath));
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export const findExecutionCandidateAddress = (
|
|
223
|
+
options: FindExecutionCandidateAddressOptions,
|
|
224
|
+
): string | undefined => {
|
|
225
|
+
const executionId = validateExecutionId(options.executionId);
|
|
226
|
+
const filePath = path.join(options.root, candidateRelativePath(executionId));
|
|
227
|
+
if (!fs.existsSync(filePath)) return undefined;
|
|
228
|
+
|
|
229
|
+
const candidate = readCandidate(filePath);
|
|
230
|
+
if (
|
|
231
|
+
candidate.deploymentId !== options.deploymentId
|
|
232
|
+
|| candidate.executionId !== executionId
|
|
233
|
+
|| candidate.network !== options.network
|
|
234
|
+
) {
|
|
235
|
+
throw new Error("contractInfo candidate does not match this verification execution");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const deployment = resolveDeployment(candidate.contractInfo, options.network, options.name);
|
|
239
|
+
if (!deployment) return undefined;
|
|
240
|
+
const replacements = candidate.replacements.filter((replacement) => (
|
|
241
|
+
replacement.network === options.network
|
|
242
|
+
&& replacement.candidateKey === deployment.key
|
|
243
|
+
&& replacement.candidateAddress.toLowerCase() === deployment.address.toLowerCase()
|
|
244
|
+
));
|
|
245
|
+
if (replacements.length > 1) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
"contractInfo candidate contains duplicate replacements for "
|
|
248
|
+
+ options.network + "." + options.name,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return replacements.length === 1 ? deployment.address : undefined;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const pendingCandidateFiles = (root: string): string[] => {
|
|
255
|
+
if (!fs.existsSync(root)) return [];
|
|
256
|
+
return fs.readdirSync(root)
|
|
257
|
+
.filter((name) => {
|
|
258
|
+
const prefix = "contractInfo.pending.";
|
|
259
|
+
const suffix = ".json";
|
|
260
|
+
if (!name.startsWith(prefix) || !name.endsWith(suffix)) return false;
|
|
261
|
+
try {
|
|
262
|
+
validateExecutionId(name.slice(prefix.length, -suffix.length));
|
|
263
|
+
return true;
|
|
264
|
+
} catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
.map((name) => path.join(root, name));
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const assertNoPendingConflict = (
|
|
272
|
+
root: string,
|
|
273
|
+
candidatePath: string,
|
|
274
|
+
network: string,
|
|
275
|
+
instanceId: string,
|
|
276
|
+
supersedeCandidatePath?: string,
|
|
277
|
+
): void => {
|
|
278
|
+
for (const filePath of pendingCandidateFiles(root)) {
|
|
279
|
+
if (
|
|
280
|
+
path.resolve(filePath) === path.resolve(candidatePath)
|
|
281
|
+
|| (supersedeCandidatePath && path.resolve(filePath) === path.resolve(supersedeCandidatePath))
|
|
282
|
+
) continue;
|
|
283
|
+
const candidate = readCandidate(filePath);
|
|
284
|
+
if (candidate.status !== "pending") continue;
|
|
285
|
+
const conflict = candidate.replacements.some((replacement) => (
|
|
286
|
+
replacement.network === network && replacement.instanceId === instanceId
|
|
287
|
+
));
|
|
288
|
+
if (conflict) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"pending candidate conflict for " + network + "." + instanceId
|
|
291
|
+
+ " in " + path.basename(filePath),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const archiveSupersededCandidate = (
|
|
298
|
+
options: StageContractInfoCandidateOptions,
|
|
299
|
+
replacement: StageContractInfoReplacement,
|
|
300
|
+
): string | undefined => {
|
|
301
|
+
if (!options.supersedeCandidatePath) return undefined;
|
|
302
|
+
const source = resolveProjectPath(options.root, options.supersedeCandidatePath);
|
|
303
|
+
const archive = path.join(
|
|
304
|
+
options.taskDir,
|
|
305
|
+
"results",
|
|
306
|
+
"archived-pending-candidates",
|
|
307
|
+
path.basename(source),
|
|
308
|
+
);
|
|
309
|
+
const validateSuperseded = (filePath: string): void => {
|
|
310
|
+
const candidate = readCandidate(filePath);
|
|
311
|
+
const matches = candidate.status === "pending"
|
|
312
|
+
&& candidate.deploymentId === options.deploymentId
|
|
313
|
+
&& candidate.network === options.network
|
|
314
|
+
&& candidate.replacements.some((entry) => (
|
|
315
|
+
entry.network === options.network
|
|
316
|
+
&& entry.instanceId === replacement.instanceId
|
|
317
|
+
&& entry.candidateAddress.toLowerCase() === replacement.address.toLowerCase()
|
|
318
|
+
));
|
|
319
|
+
if (!matches) throw new Error("Superseded pending candidate identity does not match");
|
|
320
|
+
};
|
|
321
|
+
if (!fs.existsSync(source)) {
|
|
322
|
+
if (!fs.existsSync(archive)) {
|
|
323
|
+
throw new Error("Trusted pending candidate disappeared before it could be superseded");
|
|
324
|
+
}
|
|
325
|
+
validateSuperseded(archive);
|
|
326
|
+
return relativeProjectPath(options.root, archive);
|
|
327
|
+
}
|
|
328
|
+
validateSuperseded(source);
|
|
329
|
+
if (fs.existsSync(archive)) {
|
|
330
|
+
throw new Error("Archived pending candidate already exists: " + archive);
|
|
331
|
+
}
|
|
332
|
+
fs.mkdirSync(path.dirname(archive), { recursive: true });
|
|
333
|
+
fs.renameSync(source, archive);
|
|
334
|
+
return relativeProjectPath(options.root, archive);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const replacementFor = (
|
|
338
|
+
contractInfo: ContractInfo,
|
|
339
|
+
network: string,
|
|
340
|
+
replacement: StageContractInfoReplacement,
|
|
341
|
+
): {
|
|
342
|
+
replacement: ContractInfoCandidateReplacement;
|
|
343
|
+
contractInfo: ContractInfo;
|
|
344
|
+
} => {
|
|
345
|
+
const previous = resolveDeployment(contractInfo, network, replacement.instanceId);
|
|
346
|
+
const planned = planDeploymentAddress(
|
|
347
|
+
contractInfo,
|
|
348
|
+
network,
|
|
349
|
+
replacement.instanceId,
|
|
350
|
+
replacement.contractName,
|
|
351
|
+
replacement.address,
|
|
352
|
+
);
|
|
353
|
+
return {
|
|
354
|
+
replacement: {
|
|
355
|
+
network,
|
|
356
|
+
instanceId: replacement.instanceId,
|
|
357
|
+
previousKey: previous?.key,
|
|
358
|
+
previousAddress: previous?.address,
|
|
359
|
+
candidateKey: planned.key,
|
|
360
|
+
candidateAddress: replacement.address,
|
|
361
|
+
},
|
|
362
|
+
contractInfo: planned.contractInfo,
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
export const stageContractInfoCandidate = (
|
|
367
|
+
options: StageContractInfoCandidateOptions,
|
|
368
|
+
replacementInput: StageContractInfoReplacement,
|
|
369
|
+
): StagedContractInfoCandidate => {
|
|
370
|
+
validateExecutionId(options.executionId);
|
|
371
|
+
return withContractInfoLock(options.root, () => {
|
|
372
|
+
const candidatePath = candidateRelativePath(options.executionId);
|
|
373
|
+
const candidateAbsolute = path.join(options.root, candidatePath);
|
|
374
|
+
const backupAbsolute = backupAbsolutePath(options);
|
|
375
|
+
const backupPath = relativeProjectPath(options.root, backupAbsolute);
|
|
376
|
+
assertNoPendingConflict(
|
|
377
|
+
options.root,
|
|
378
|
+
candidateAbsolute,
|
|
379
|
+
options.network,
|
|
380
|
+
replacementInput.instanceId,
|
|
381
|
+
options.supersedeCandidatePath
|
|
382
|
+
? resolveProjectPath(options.root, options.supersedeCandidatePath)
|
|
383
|
+
: undefined,
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
if (fs.existsSync(candidateAbsolute)) {
|
|
387
|
+
const existing = readCandidate(candidateAbsolute);
|
|
388
|
+
if (
|
|
389
|
+
existing.status !== "pending"
|
|
390
|
+
|| existing.deploymentId !== options.deploymentId
|
|
391
|
+
|| existing.executionId !== options.executionId
|
|
392
|
+
|| existing.network !== options.network
|
|
393
|
+
|| existing.backupPath !== backupPath
|
|
394
|
+
) {
|
|
395
|
+
throw new Error("Existing contractInfo candidate does not match this execution");
|
|
396
|
+
}
|
|
397
|
+
const duplicate = existing.replacements.find((item) => (
|
|
398
|
+
item.network === options.network && item.instanceId === replacementInput.instanceId
|
|
399
|
+
));
|
|
400
|
+
if (duplicate) {
|
|
401
|
+
if (
|
|
402
|
+
duplicate.candidateAddress.toLowerCase() !== replacementInput.address.toLowerCase()
|
|
403
|
+
|| duplicate.candidateKey !== planDeploymentAddress(
|
|
404
|
+
existing.contractInfo,
|
|
405
|
+
options.network,
|
|
406
|
+
replacementInput.instanceId,
|
|
407
|
+
replacementInput.contractName,
|
|
408
|
+
replacementInput.address,
|
|
409
|
+
).key
|
|
410
|
+
) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
"Execution candidate already defines a different replacement for "
|
|
413
|
+
+ options.network + "." + replacementInput.instanceId,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
candidate: existing,
|
|
418
|
+
candidatePath,
|
|
419
|
+
backupPath,
|
|
420
|
+
replacement: duplicate,
|
|
421
|
+
archivedCandidatePath: archiveSupersededCandidate(options, replacementInput),
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const next = replacementFor(existing.contractInfo, options.network, replacementInput);
|
|
426
|
+
const updated: ContractInfoCandidate = {
|
|
427
|
+
...existing,
|
|
428
|
+
replacements: [...existing.replacements, next.replacement],
|
|
429
|
+
contractInfo: next.contractInfo,
|
|
430
|
+
updatedAt: new Date().toISOString(),
|
|
431
|
+
};
|
|
432
|
+
writeJsonFile(candidateAbsolute, updated);
|
|
433
|
+
return {
|
|
434
|
+
candidate: updated,
|
|
435
|
+
candidatePath,
|
|
436
|
+
backupPath,
|
|
437
|
+
replacement: next.replacement,
|
|
438
|
+
archivedCandidatePath: archiveSupersededCandidate(options, replacementInput),
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const archivedCandidatePath = archiveSupersededCandidate(options, replacementInput);
|
|
443
|
+
const current = cloneContractInfo(loadContractInfo(options.root, options.config));
|
|
444
|
+
const next = replacementFor(current, options.network, replacementInput);
|
|
445
|
+
writeExclusiveJson(backupAbsolute, current);
|
|
446
|
+
const now = new Date().toISOString();
|
|
447
|
+
const candidate: ContractInfoCandidate = {
|
|
448
|
+
version: 1,
|
|
449
|
+
status: "pending",
|
|
450
|
+
deploymentId: options.deploymentId,
|
|
451
|
+
executionId: options.executionId,
|
|
452
|
+
network: options.network,
|
|
453
|
+
baseContractInfoHash: hashValue(current),
|
|
454
|
+
backupPath,
|
|
455
|
+
replacements: [next.replacement],
|
|
456
|
+
contractInfo: next.contractInfo,
|
|
457
|
+
createdAt: now,
|
|
458
|
+
updatedAt: now,
|
|
459
|
+
};
|
|
460
|
+
writeJsonFile(candidateAbsolute, candidate);
|
|
461
|
+
return {
|
|
462
|
+
candidate,
|
|
463
|
+
candidatePath,
|
|
464
|
+
backupPath,
|
|
465
|
+
replacement: next.replacement,
|
|
466
|
+
archivedCandidatePath,
|
|
467
|
+
};
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const sameDeployment = (
|
|
472
|
+
left: { key: string; address: string } | undefined,
|
|
473
|
+
rightKey: string | undefined,
|
|
474
|
+
rightAddress: string | undefined,
|
|
475
|
+
): boolean => {
|
|
476
|
+
if (!left || !rightKey || !rightAddress) {
|
|
477
|
+
return left === undefined && rightKey === undefined && rightAddress === undefined;
|
|
478
|
+
}
|
|
479
|
+
return left.key === rightKey && left.address.toLowerCase() === rightAddress.toLowerCase();
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const updateDeploymentRecordAfterPromotion = (
|
|
483
|
+
root: string,
|
|
484
|
+
candidate: ContractInfoCandidate,
|
|
485
|
+
contractInfo: ContractInfo,
|
|
486
|
+
): void => {
|
|
487
|
+
const resultDir = path.dirname(resolveProjectPath(root, candidate.backupPath));
|
|
488
|
+
const recordPath = path.join(
|
|
489
|
+
resultDir,
|
|
490
|
+
candidate.network + "." + candidate.executionId + ".deployment.json",
|
|
491
|
+
);
|
|
492
|
+
if (!fs.existsSync(recordPath)) return;
|
|
493
|
+
const record = readJsonFile<Record<string, unknown>>(recordPath);
|
|
494
|
+
const changes = Array.isArray(record.changes)
|
|
495
|
+
? record.changes.map((change) => {
|
|
496
|
+
if (!isRecord(change)) return change;
|
|
497
|
+
const promoted = candidate.replacements.some((replacement) => (
|
|
498
|
+
replacement.instanceId === change.instanceId
|
|
499
|
+
&& replacement.candidateAddress.toLowerCase() === String(change.candidateAddress || change.address).toLowerCase()
|
|
500
|
+
));
|
|
501
|
+
return promoted ? { ...change, status: "applied" } : change;
|
|
502
|
+
})
|
|
503
|
+
: [];
|
|
504
|
+
const allApplied = changes.every((change) => isRecord(change) && change.status === "applied");
|
|
505
|
+
writeJsonFile(recordPath, {
|
|
506
|
+
...record,
|
|
507
|
+
status: allApplied ? "applied" : record.status,
|
|
508
|
+
candidateStatus: "promoted",
|
|
509
|
+
promotedAt: new Date().toISOString(),
|
|
510
|
+
contractInfo: {
|
|
511
|
+
...(isRecord(record.contractInfo) ? record.contractInfo : {}),
|
|
512
|
+
after: contractInfo,
|
|
513
|
+
},
|
|
514
|
+
changes,
|
|
515
|
+
updatedAt: new Date().toISOString(),
|
|
516
|
+
});
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const promotionRecord = (
|
|
520
|
+
root: string,
|
|
521
|
+
candidate: ContractInfoCandidate,
|
|
522
|
+
): DeploymentRecordPromotionInput => {
|
|
523
|
+
const resultDir = path.dirname(resolveProjectPath(root, candidate.backupPath));
|
|
524
|
+
const absolutePath = path.join(
|
|
525
|
+
resultDir,
|
|
526
|
+
`${candidate.network}.${candidate.executionId}.deployment.json`,
|
|
527
|
+
);
|
|
528
|
+
return { recordPath: relativeProjectPath(root, absolutePath) };
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const prepareCandidatePromotion = (
|
|
532
|
+
options: PromoteContractInfoCandidatesOptions,
|
|
533
|
+
): {
|
|
534
|
+
contractInfo: ContractInfo;
|
|
535
|
+
selected: Array<{
|
|
536
|
+
candidatePath: string;
|
|
537
|
+
absolutePath: string;
|
|
538
|
+
candidate: ContractInfoCandidate;
|
|
539
|
+
backup: ContractInfo;
|
|
540
|
+
}>;
|
|
541
|
+
} => {
|
|
542
|
+
const uniquePaths = [...new Set(options.candidatePaths)];
|
|
543
|
+
if (uniquePaths.length !== options.candidatePaths.length) {
|
|
544
|
+
throw new Error("Duplicate contractInfo candidate path in promotion request");
|
|
545
|
+
}
|
|
546
|
+
const current = cloneContractInfo(loadContractInfo(options.root, options.config));
|
|
547
|
+
const currentHash = hashValue(current);
|
|
548
|
+
const selected = uniquePaths.map((candidatePath) => {
|
|
549
|
+
const absolutePath = resolveProjectPath(options.root, candidatePath);
|
|
550
|
+
const candidate = readCandidate(absolutePath);
|
|
551
|
+
if (
|
|
552
|
+
candidate.status === "promoted"
|
|
553
|
+
&& candidate.promotedContractInfoHash !== currentHash
|
|
554
|
+
) {
|
|
555
|
+
throw new Error("promoted contractInfo candidate hash is invalid: " + candidatePath);
|
|
556
|
+
}
|
|
557
|
+
const backup = validateContractInfo(
|
|
558
|
+
readJsonFile<unknown>(resolveProjectPath(options.root, candidate.backupPath)),
|
|
559
|
+
);
|
|
560
|
+
if (hashValue(backup) !== candidate.baseContractInfoHash) {
|
|
561
|
+
throw new Error("contractInfo candidate backup hash changed: " + candidatePath);
|
|
562
|
+
}
|
|
563
|
+
return { candidatePath, absolutePath, candidate, backup };
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
let merged = cloneContractInfo(current);
|
|
567
|
+
const selectedInstances = new Set<string>();
|
|
568
|
+
for (const item of selected) {
|
|
569
|
+
for (const replacement of item.candidate.replacements) {
|
|
570
|
+
const instanceKey = replacement.network + "." + replacement.instanceId;
|
|
571
|
+
if (selectedInstances.has(instanceKey)) {
|
|
572
|
+
throw new Error("Multiple selected candidates replace " + instanceKey);
|
|
573
|
+
}
|
|
574
|
+
selectedInstances.add(instanceKey);
|
|
575
|
+
|
|
576
|
+
const backupDeployment = resolveDeployment(
|
|
577
|
+
item.backup,
|
|
578
|
+
replacement.network,
|
|
579
|
+
replacement.instanceId,
|
|
580
|
+
);
|
|
581
|
+
if (!sameDeployment(
|
|
582
|
+
backupDeployment,
|
|
583
|
+
replacement.previousKey,
|
|
584
|
+
replacement.previousAddress,
|
|
585
|
+
)) {
|
|
586
|
+
throw new Error("candidate backup does not match replacement metadata for " + instanceKey);
|
|
587
|
+
}
|
|
588
|
+
const candidateDeployment = resolveDeployment(
|
|
589
|
+
item.candidate.contractInfo,
|
|
590
|
+
replacement.network,
|
|
591
|
+
replacement.instanceId,
|
|
592
|
+
);
|
|
593
|
+
if (!sameDeployment(
|
|
594
|
+
candidateDeployment,
|
|
595
|
+
replacement.candidateKey,
|
|
596
|
+
replacement.candidateAddress,
|
|
597
|
+
)) {
|
|
598
|
+
throw new Error("candidate registry does not match replacement metadata for " + instanceKey);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const currentDeployment = resolveDeployment(
|
|
602
|
+
current,
|
|
603
|
+
replacement.network,
|
|
604
|
+
replacement.instanceId,
|
|
605
|
+
);
|
|
606
|
+
const currentMatchesBase = sameDeployment(
|
|
607
|
+
currentDeployment,
|
|
608
|
+
replacement.previousKey,
|
|
609
|
+
replacement.previousAddress,
|
|
610
|
+
);
|
|
611
|
+
const currentMatchesCandidate = sameDeployment(
|
|
612
|
+
currentDeployment,
|
|
613
|
+
replacement.candidateKey,
|
|
614
|
+
replacement.candidateAddress,
|
|
615
|
+
);
|
|
616
|
+
if (!currentMatchesBase && !currentMatchesCandidate) {
|
|
617
|
+
throw new Error("candidate base conflict for " + instanceKey);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
merged = planDeploymentAddress(
|
|
621
|
+
merged,
|
|
622
|
+
replacement.network,
|
|
623
|
+
replacement.instanceId,
|
|
624
|
+
replacement.candidateKey.split(":").at(-1) || replacement.candidateKey,
|
|
625
|
+
replacement.candidateAddress,
|
|
626
|
+
).contractInfo;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return { contractInfo: merged, selected };
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
export const previewContractInfoCandidates = (
|
|
633
|
+
options: PromoteContractInfoCandidatesOptions,
|
|
634
|
+
): PreviewedContractInfoCandidates => {
|
|
635
|
+
return withContractInfoLock(options.root, () => {
|
|
636
|
+
const prepared = prepareCandidatePromotion(options);
|
|
637
|
+
return {
|
|
638
|
+
contractInfo: prepared.contractInfo,
|
|
639
|
+
candidates: prepared.selected.map((item) => ({
|
|
640
|
+
candidatePath: item.candidatePath,
|
|
641
|
+
candidate: item.candidate,
|
|
642
|
+
})),
|
|
643
|
+
};
|
|
644
|
+
});
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
export const promoteContractInfoCandidates = (
|
|
648
|
+
options: PromoteContractInfoCandidatesOptions,
|
|
649
|
+
): PromotedContractInfoCandidates => {
|
|
650
|
+
if (options.candidatePaths.length === 0) {
|
|
651
|
+
throw new Error("At least one contractInfo candidate is required for promotion");
|
|
652
|
+
}
|
|
653
|
+
return withContractInfoLock(options.root, () => {
|
|
654
|
+
const prepared = prepareCandidatePromotion(options);
|
|
655
|
+
const merged = prepared.contractInfo;
|
|
656
|
+
const selected = prepared.selected;
|
|
657
|
+
const beforeHash = hashValue(loadContractInfo(options.root, options.config));
|
|
658
|
+
const records = selected.map((item) => promotionRecord(options.root, item.candidate));
|
|
659
|
+
const deploymentInfo = prepareDeploymentInfoFromRecords({
|
|
660
|
+
root: options.root,
|
|
661
|
+
records,
|
|
662
|
+
expectedContractInfo: merged,
|
|
663
|
+
});
|
|
664
|
+
saveContractInfo(options.root, merged);
|
|
665
|
+
saveDeploymentInfo(options.root, deploymentInfo);
|
|
666
|
+
const promotedAt = new Date().toISOString();
|
|
667
|
+
const promotedHash = hashValue(merged);
|
|
668
|
+
const promoted = selected.map((item) => {
|
|
669
|
+
const candidate: ContractInfoCandidate = {
|
|
670
|
+
...item.candidate,
|
|
671
|
+
status: "promoted",
|
|
672
|
+
promotedAt,
|
|
673
|
+
promotedContractInfoHash: promotedHash,
|
|
674
|
+
updatedAt: promotedAt,
|
|
675
|
+
};
|
|
676
|
+
writeJsonFile(item.absolutePath, candidate);
|
|
677
|
+
updateDeploymentRecordAfterPromotion(options.root, candidate, merged);
|
|
678
|
+
return { candidatePath: item.candidatePath, candidate };
|
|
679
|
+
});
|
|
680
|
+
return {
|
|
681
|
+
contractInfo: merged,
|
|
682
|
+
beforeHash,
|
|
683
|
+
afterHash: promotedHash,
|
|
684
|
+
deploymentInfoPath: "deploymentInfo.json",
|
|
685
|
+
deploymentInfoHash: hashValue(deploymentInfo),
|
|
686
|
+
changedEntries: selected.flatMap((item) => item.candidate.replacements),
|
|
687
|
+
promoted,
|
|
688
|
+
};
|
|
689
|
+
});
|
|
690
|
+
};
|