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.
- package/README.md +14 -0
- package/bin/workflow.cjs +19 -0
- 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-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 +25 -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,278 @@
|
|
|
1
|
+
import type { Signer } from "ethers";
|
|
2
|
+
import { getAddress, Interface, isAddress } from "ethers";
|
|
3
|
+
import { resolveValue, WorkflowStep } from "./parameters";
|
|
4
|
+
import type { WorkflowContext } from "../run";
|
|
5
|
+
|
|
6
|
+
const CONTROLLED_MULTICALL_INTERFACE = new Interface([
|
|
7
|
+
"function aggregate3(tuple(address target,bool allowFailure,bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)",
|
|
8
|
+
"function aggregate3Value(tuple(address target,bool allowFailure,uint256 value,bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)",
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
export type Create2Scheme = "evm" | "zksync-eravm";
|
|
12
|
+
|
|
13
|
+
export interface Create2Options {
|
|
14
|
+
scheme: Create2Scheme;
|
|
15
|
+
factory: string;
|
|
16
|
+
salt: string;
|
|
17
|
+
controlledMulticall?: string;
|
|
18
|
+
create2ForwardValue?: string;
|
|
19
|
+
callForwardValue?: string;
|
|
20
|
+
expectedAddress?: string;
|
|
21
|
+
leadingZeroBytes?: number;
|
|
22
|
+
uupsPlaceholderImplementation?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PreparedWorkflowTransaction {
|
|
26
|
+
target: string;
|
|
27
|
+
value: string;
|
|
28
|
+
data: string;
|
|
29
|
+
operation: 0;
|
|
30
|
+
scheme?: "zksync-eravm";
|
|
31
|
+
factoryDeps?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ControlledMulticallInnerCall {
|
|
35
|
+
target: string;
|
|
36
|
+
value: string;
|
|
37
|
+
data: string;
|
|
38
|
+
factoryDeps?: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface PreparedControlledMulticallTransaction
|
|
42
|
+
extends PreparedWorkflowTransaction {
|
|
43
|
+
method: "aggregate3" | "aggregate3Value";
|
|
44
|
+
calls: Array<{
|
|
45
|
+
target: string;
|
|
46
|
+
allowFailure: false;
|
|
47
|
+
value: string;
|
|
48
|
+
callData: string;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface SentTransaction {
|
|
53
|
+
hash: string;
|
|
54
|
+
wait: () => Promise<unknown>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const bigintValue = (value: string | undefined): bigint => {
|
|
58
|
+
if (!value || value === "0") return 0n;
|
|
59
|
+
const parsed = BigInt(value);
|
|
60
|
+
if (parsed < 0n) throw new Error("transaction value must be nonnegative");
|
|
61
|
+
return parsed;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const normalizeFactoryDeps = (
|
|
65
|
+
input: string[],
|
|
66
|
+
label: string,
|
|
67
|
+
): string[] => {
|
|
68
|
+
const seen = new Set<string>();
|
|
69
|
+
const output: string[] = [];
|
|
70
|
+
for (const [index, dependency] of input.entries()) {
|
|
71
|
+
if (!/^0x(?:[0-9a-fA-F]{2})+$/.test(dependency)) {
|
|
72
|
+
throw new Error(`${label}[${index}] must be non-empty byte-aligned bytecode`);
|
|
73
|
+
}
|
|
74
|
+
const normalized = dependency.toLowerCase();
|
|
75
|
+
if (seen.has(normalized)) continue;
|
|
76
|
+
seen.add(normalized);
|
|
77
|
+
output.push(normalized);
|
|
78
|
+
}
|
|
79
|
+
return output;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const prepareControlledMulticallTransaction = (
|
|
83
|
+
controlledMulticall: string,
|
|
84
|
+
inputCalls: ControlledMulticallInnerCall[],
|
|
85
|
+
scheme: Create2Scheme = "evm",
|
|
86
|
+
): PreparedControlledMulticallTransaction => {
|
|
87
|
+
if (!isAddress(controlledMulticall)) {
|
|
88
|
+
throw new Error("controlledMulticall must be an EVM address");
|
|
89
|
+
}
|
|
90
|
+
if (inputCalls.length === 0) {
|
|
91
|
+
throw new Error("controlled multicall requires at least one inner call");
|
|
92
|
+
}
|
|
93
|
+
if (scheme === "evm" && inputCalls.some((call) => call.factoryDeps !== undefined)) {
|
|
94
|
+
throw new Error("factoryDeps are only supported for zkSync transactions");
|
|
95
|
+
}
|
|
96
|
+
if (scheme === "zksync-eravm" && inputCalls.some((call) => call.factoryDeps === undefined)) {
|
|
97
|
+
throw new Error("every zkSync GateTool call must include factoryDeps");
|
|
98
|
+
}
|
|
99
|
+
const calls = inputCalls.map((call) => ({
|
|
100
|
+
target: getAddress(call.target),
|
|
101
|
+
allowFailure: false as const,
|
|
102
|
+
value: bigintValue(call.value).toString(10),
|
|
103
|
+
callData: call.data,
|
|
104
|
+
}));
|
|
105
|
+
const totalValue = calls.reduce((sum, call) => sum + BigInt(call.value), 0n);
|
|
106
|
+
const method = totalValue === 0n ? "aggregate3" as const : "aggregate3Value" as const;
|
|
107
|
+
const encodedCalls = method === "aggregate3"
|
|
108
|
+
? calls.map(({ value: _value, ...call }) => call)
|
|
109
|
+
: calls;
|
|
110
|
+
const factoryDeps = scheme === "zksync-eravm"
|
|
111
|
+
? normalizeFactoryDeps(inputCalls.flatMap((call) => call.factoryDeps!), "factoryDeps")
|
|
112
|
+
: undefined;
|
|
113
|
+
return {
|
|
114
|
+
target: getAddress(controlledMulticall),
|
|
115
|
+
value: totalValue.toString(10),
|
|
116
|
+
data: CONTROLLED_MULTICALL_INTERFACE.encodeFunctionData(method, [encodedCalls]),
|
|
117
|
+
operation: 0,
|
|
118
|
+
method,
|
|
119
|
+
calls,
|
|
120
|
+
...(scheme === "zksync-eravm"
|
|
121
|
+
? { scheme: "zksync-eravm" as const, factoryDeps: factoryDeps! }
|
|
122
|
+
: {}),
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const sendPreparedWorkflowTransaction = async (
|
|
127
|
+
prepared: PreparedWorkflowTransaction,
|
|
128
|
+
signer: Signer,
|
|
129
|
+
): Promise<SentTransaction> => {
|
|
130
|
+
if (prepared.scheme === "zksync-eravm" && prepared.factoryDeps === undefined) {
|
|
131
|
+
throw new Error("zkSync transaction requires an explicit factoryDeps list");
|
|
132
|
+
}
|
|
133
|
+
if (prepared.scheme === undefined && prepared.factoryDeps !== undefined) {
|
|
134
|
+
throw new Error("EVM transaction cannot include factoryDeps");
|
|
135
|
+
}
|
|
136
|
+
const transaction = await signer.sendTransaction({
|
|
137
|
+
to: prepared.target,
|
|
138
|
+
value: prepared.value,
|
|
139
|
+
data: prepared.data,
|
|
140
|
+
...(prepared.scheme === "zksync-eravm"
|
|
141
|
+
? { customData: { factoryDeps: prepared.factoryDeps } }
|
|
142
|
+
: {}),
|
|
143
|
+
} as Parameters<Signer["sendTransaction"]>[0]);
|
|
144
|
+
const receipt = await transaction.wait();
|
|
145
|
+
const receiptHash = receipt && typeof receipt === "object"
|
|
146
|
+
? "hash" in receipt && typeof receipt.hash === "string"
|
|
147
|
+
? receipt.hash
|
|
148
|
+
: "transactionHash" in receipt && typeof receipt.transactionHash === "string"
|
|
149
|
+
? receipt.transactionHash
|
|
150
|
+
: undefined
|
|
151
|
+
: undefined;
|
|
152
|
+
if (receiptHash && receiptHash.toLowerCase() !== transaction.hash.toLowerCase()) {
|
|
153
|
+
throw new Error("transaction receipt hash does not match the submitted locked envelope");
|
|
154
|
+
}
|
|
155
|
+
return transaction;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const sendPreparedControlledMulticallTransaction = async (
|
|
159
|
+
prepared: PreparedControlledMulticallTransaction,
|
|
160
|
+
signer: Signer,
|
|
161
|
+
): Promise<SentTransaction> => sendPreparedWorkflowTransaction(prepared, signer);
|
|
162
|
+
|
|
163
|
+
const resolveObject = (
|
|
164
|
+
context: WorkflowContext,
|
|
165
|
+
value: unknown,
|
|
166
|
+
): Record<string, unknown> => {
|
|
167
|
+
const resolved = resolveValue(value || {}, {
|
|
168
|
+
targetId: context.targetId,
|
|
169
|
+
target: context.target,
|
|
170
|
+
params: context.targetParams,
|
|
171
|
+
state: context.state,
|
|
172
|
+
});
|
|
173
|
+
if (!resolved || typeof resolved !== "object" || Array.isArray(resolved)) return {};
|
|
174
|
+
return resolved as Record<string, unknown>;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const stringField = (
|
|
178
|
+
value: Record<string, unknown>,
|
|
179
|
+
key: string,
|
|
180
|
+
): string | undefined => {
|
|
181
|
+
const field = value[key];
|
|
182
|
+
return typeof field === "string" && field.trim() ? field.trim() : undefined;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const numberField = (
|
|
186
|
+
value: Record<string, unknown>,
|
|
187
|
+
key: string,
|
|
188
|
+
): number | undefined => {
|
|
189
|
+
const field = value[key];
|
|
190
|
+
return typeof field === "number" ? field : undefined;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const assertLeadingZeroBytes = (
|
|
194
|
+
stepId: string,
|
|
195
|
+
leadingZeroBytes: number | undefined,
|
|
196
|
+
): void => {
|
|
197
|
+
if (leadingZeroBytes === undefined) return;
|
|
198
|
+
if (!Number.isInteger(leadingZeroBytes) || leadingZeroBytes < 0 || leadingZeroBytes > 20) {
|
|
199
|
+
throw new Error(`Step ${stepId} create2.leadingZeroBytes must be an integer from 0 to 20`);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const resolveCreate2Options = (
|
|
204
|
+
context: WorkflowContext,
|
|
205
|
+
step: WorkflowStep,
|
|
206
|
+
): Create2Options | undefined => {
|
|
207
|
+
const strategy = typeof step.strategy === "string" ? step.strategy : undefined;
|
|
208
|
+
const stepCreate2 = resolveObject(context, step.create2);
|
|
209
|
+
const targetCreate2 = resolveObject(context, context.target.create2);
|
|
210
|
+
const targetUups = resolveObject(context, context.target.uups);
|
|
211
|
+
const targetMulticall = resolveObject(context, context.target.multicall);
|
|
212
|
+
if (strategy !== "create2" && Object.keys(stepCreate2).length === 0) return undefined;
|
|
213
|
+
|
|
214
|
+
const rawScheme = stringField(targetCreate2, "scheme") || "evm";
|
|
215
|
+
if (rawScheme !== "evm" && rawScheme !== "zksync-eravm") {
|
|
216
|
+
throw new Error(`Step ${step.id} create2.scheme must be evm or zksync-eravm`);
|
|
217
|
+
}
|
|
218
|
+
const factory = stringField(stepCreate2, "factory") || stringField(targetCreate2, "factory");
|
|
219
|
+
const salt = stringField(stepCreate2, "salt");
|
|
220
|
+
const leadingZeroBytes = numberField(stepCreate2, "leadingZeroBytes")
|
|
221
|
+
?? numberField(targetCreate2, "leadingZeroBytes");
|
|
222
|
+
const uupsPlaceholderImplementation = stringField(targetUups, "placeholderImplementation");
|
|
223
|
+
const controlledMulticall = stringField(targetMulticall, "controlledMulticall");
|
|
224
|
+
if (!factory) throw new Error(`Step ${step.id} uses create2 but no factory address was provided`);
|
|
225
|
+
if (!salt) throw new Error(`Step ${step.id} uses create2 but no salt was provided`);
|
|
226
|
+
assertLeadingZeroBytes(step.id, leadingZeroBytes);
|
|
227
|
+
if (step.upgradeableStrategy === "uups-placeholder-proxy" && !uupsPlaceholderImplementation) {
|
|
228
|
+
throw new Error(`Step ${step.id} requires target.uups.placeholderImplementation`);
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
scheme: rawScheme,
|
|
232
|
+
factory,
|
|
233
|
+
salt,
|
|
234
|
+
controlledMulticall,
|
|
235
|
+
create2ForwardValue: stringField(stepCreate2, "create2ForwardValue"),
|
|
236
|
+
callForwardValue: stringField(stepCreate2, "callForwardValue"),
|
|
237
|
+
expectedAddress: stringField(stepCreate2, "expectedAddress"),
|
|
238
|
+
leadingZeroBytes,
|
|
239
|
+
uupsPlaceholderImplementation,
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export const hasLeadingZeroBytes = (
|
|
244
|
+
address: string,
|
|
245
|
+
leadingZeroBytes: number,
|
|
246
|
+
): boolean => {
|
|
247
|
+
if (!isAddress(address)) throw new Error(`Invalid CREATE2 address: ${address}`);
|
|
248
|
+
return address.slice(2, 2 + leadingZeroBytes * 2).toLowerCase() === "00".repeat(leadingZeroBytes);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const assertCreate2AddressPolicy = (
|
|
252
|
+
label: string,
|
|
253
|
+
predictedAddress: string,
|
|
254
|
+
options: Pick<Create2Options, "expectedAddress" | "leadingZeroBytes">,
|
|
255
|
+
): void => {
|
|
256
|
+
if (!isAddress(predictedAddress)) {
|
|
257
|
+
throw new Error(`${label} predicted invalid CREATE2 address: ${predictedAddress}`);
|
|
258
|
+
}
|
|
259
|
+
if (options.expectedAddress) {
|
|
260
|
+
if (!isAddress(options.expectedAddress)) {
|
|
261
|
+
throw new Error(`${label} expectedAddress is invalid: ${options.expectedAddress}`);
|
|
262
|
+
}
|
|
263
|
+
if (getAddress(predictedAddress) !== getAddress(options.expectedAddress)) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`${label} predicted ${predictedAddress} but parameters expected ${options.expectedAddress}`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (
|
|
270
|
+
options.leadingZeroBytes !== undefined
|
|
271
|
+
&& !hasLeadingZeroBytes(predictedAddress, options.leadingZeroBytes)
|
|
272
|
+
) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`${label} predicted ${predictedAddress} but create2.leadingZeroBytes requires prefix `
|
|
275
|
+
+ `0x${"00".repeat(options.leadingZeroBytes)}`,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
Interface,
|
|
4
|
+
concat,
|
|
5
|
+
getBytes,
|
|
6
|
+
hexlify,
|
|
7
|
+
keccak256,
|
|
8
|
+
type InterfaceAbi,
|
|
9
|
+
} from "ethers";
|
|
10
|
+
|
|
11
|
+
export type Create2AddressScheme = "evm" | "zksync-eravm";
|
|
12
|
+
|
|
13
|
+
export interface Create2HashArtifact {
|
|
14
|
+
_format?: string;
|
|
15
|
+
abi: InterfaceAbi;
|
|
16
|
+
bytecode: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type CanonicalCreate2Hashes =
|
|
20
|
+
| {
|
|
21
|
+
scheme: "evm";
|
|
22
|
+
initCodeHash: string;
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
scheme: "zksync-eravm";
|
|
26
|
+
bytecodeHash: string;
|
|
27
|
+
constructorInputHash: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const normalizeCreate2Scheme = (value: unknown): Create2AddressScheme => {
|
|
31
|
+
if (value === undefined || value === null || value === "" || value === "evm") {
|
|
32
|
+
return "evm";
|
|
33
|
+
}
|
|
34
|
+
if (value === "zksync-eravm") return value;
|
|
35
|
+
throw new Error("CREATE2 scheme must be evm or zksync-eravm");
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const hashZkSyncBytecode = (bytecode: string): string => {
|
|
39
|
+
const bytes = getBytes(bytecode);
|
|
40
|
+
if (bytes.length === 0 || bytes.length % 32 !== 0) {
|
|
41
|
+
throw new Error("zkSync EraVM bytecode length must be a non-zero multiple of 32 bytes");
|
|
42
|
+
}
|
|
43
|
+
const lengthInWords = bytes.length / 32;
|
|
44
|
+
if (lengthInWords % 2 === 0) {
|
|
45
|
+
throw new Error("zkSync EraVM bytecode must contain an odd number of 32-byte words");
|
|
46
|
+
}
|
|
47
|
+
if (lengthInWords > 0xffff) {
|
|
48
|
+
throw new Error("zkSync EraVM bytecode must contain fewer than 65536 words");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const digest = Uint8Array.from(createHash("sha256").update(bytes).digest());
|
|
52
|
+
digest[0] = 1;
|
|
53
|
+
digest[1] = 0;
|
|
54
|
+
digest[2] = lengthInWords >> 8;
|
|
55
|
+
digest[3] = lengthInWords & 0xff;
|
|
56
|
+
return hexlify(digest).toLowerCase();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const canonicalCreate2Hashes = (input: {
|
|
60
|
+
scheme?: Create2AddressScheme;
|
|
61
|
+
artifact: Create2HashArtifact;
|
|
62
|
+
args: unknown[];
|
|
63
|
+
}): CanonicalCreate2Hashes => {
|
|
64
|
+
const scheme = normalizeCreate2Scheme(input.scheme);
|
|
65
|
+
if (scheme === "zksync-eravm" && input.artifact._format !== "hh-zksolc-artifact-1") {
|
|
66
|
+
throw new Error("zkSync CREATE2 requires a zksolc artifact (hh-zksolc-artifact-1)");
|
|
67
|
+
}
|
|
68
|
+
if (scheme === "evm" && input.artifact._format !== "hh-sol-artifact-1") {
|
|
69
|
+
throw new Error("EVM CREATE2 requires a solc artifact (hh-sol-artifact-1)");
|
|
70
|
+
}
|
|
71
|
+
const constructorInput = new Interface(input.artifact.abi).encodeDeploy(input.args);
|
|
72
|
+
if (scheme === "zksync-eravm") {
|
|
73
|
+
return {
|
|
74
|
+
scheme,
|
|
75
|
+
bytecodeHash: hashZkSyncBytecode(input.artifact.bytecode),
|
|
76
|
+
constructorInputHash: keccak256(constructorInput).toLowerCase(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
scheme,
|
|
81
|
+
initCodeHash: keccak256(concat([input.artifact.bytecode, constructorInput])).toLowerCase(),
|
|
82
|
+
};
|
|
83
|
+
};
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getAddress, isAddress } from "ethers";
|
|
4
|
+
import { readJsonFile, writeJsonFile } from "./config";
|
|
5
|
+
import type { ContractInfo } from "./deployments";
|
|
6
|
+
import { resolveDeployment, validateContractInfo } from "./deployments";
|
|
7
|
+
|
|
8
|
+
export interface DeploymentInfoEntry {
|
|
9
|
+
contractInfoKey: string;
|
|
10
|
+
address: string;
|
|
11
|
+
actualContract: string;
|
|
12
|
+
proxyKind?: "uups" | "transparent";
|
|
13
|
+
implementationAddress?: string;
|
|
14
|
+
source: DeploymentInfoSource;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type DeploymentInfoSource = {
|
|
19
|
+
kind: "deployment";
|
|
20
|
+
deploymentId: string;
|
|
21
|
+
executionId: string;
|
|
22
|
+
recordPath: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "migration";
|
|
25
|
+
migrationId: string;
|
|
26
|
+
evidencePath: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface DeploymentInfo {
|
|
30
|
+
version: 1;
|
|
31
|
+
networks: Record<string, Record<string, DeploymentInfoEntry>>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface DeploymentRecordPromotionInput {
|
|
35
|
+
recordPath: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => (
|
|
39
|
+
Boolean(value) && typeof value === "object" && !Array.isArray(value)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const safeId = (value: unknown, label: string): string => {
|
|
43
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value)) {
|
|
44
|
+
throw new Error(`${label} must be a safe identifier`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const address = (value: unknown, label: string): string => {
|
|
50
|
+
if (typeof value !== "string" || !isAddress(value)) {
|
|
51
|
+
throw new Error(`${label} must be a valid EVM address`);
|
|
52
|
+
}
|
|
53
|
+
return getAddress(value);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const optionalAddress = (value: unknown, label: string): string | undefined => (
|
|
57
|
+
value === undefined ? undefined : address(value, label)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const deploymentInfoPath = (root: string): string => path.join(root, "deploymentInfo.json");
|
|
61
|
+
|
|
62
|
+
const validateSource = (value: unknown, label: string): DeploymentInfoSource => {
|
|
63
|
+
if (!isRecord(value)) throw new Error(`${label} is required`);
|
|
64
|
+
const sourceKeys = Object.keys(value).sort();
|
|
65
|
+
if (value.kind === "migration") {
|
|
66
|
+
if (sourceKeys.join(",") !== "evidencePath,kind,migrationId") {
|
|
67
|
+
throw new Error(`${label} migration source must use the canonical fields`);
|
|
68
|
+
}
|
|
69
|
+
const migrationId = safeId(value.migrationId, `${label}.migrationId`);
|
|
70
|
+
const evidencePath = String(value.evidencePath).split(path.sep).join("/");
|
|
71
|
+
const expectedPath = `scripts/migrations/${migrationId}/state-import.json`;
|
|
72
|
+
if (evidencePath !== expectedPath) {
|
|
73
|
+
throw new Error(`${label}.evidencePath must be ${expectedPath}`);
|
|
74
|
+
}
|
|
75
|
+
return { kind: "migration", migrationId, evidencePath };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const legacyDeployment = value.kind === undefined
|
|
79
|
+
&& sourceKeys.join(",") === "deploymentId,executionId,recordPath";
|
|
80
|
+
const canonicalDeployment = value.kind === "deployment"
|
|
81
|
+
&& sourceKeys.join(",") === "deploymentId,executionId,kind,recordPath";
|
|
82
|
+
if (!legacyDeployment && !canonicalDeployment) {
|
|
83
|
+
throw new Error(`${label} deployment source must use the canonical fields`);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
kind: "deployment",
|
|
87
|
+
deploymentId: safeId(value.deploymentId, `${label}.deploymentId`),
|
|
88
|
+
executionId: safeId(value.executionId, `${label}.executionId`),
|
|
89
|
+
recordPath: String(value.recordPath),
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const confinedRecordPath = (root: string, recordPath: string): string => {
|
|
94
|
+
if (!recordPath || path.isAbsolute(recordPath)) {
|
|
95
|
+
throw new Error(`Deployment record path must be project-relative: ${recordPath}`);
|
|
96
|
+
}
|
|
97
|
+
const absolute = path.resolve(root, recordPath);
|
|
98
|
+
const relative = path.relative(path.resolve(root), absolute);
|
|
99
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
100
|
+
throw new Error(`Deployment record path escapes the project: ${recordPath}`);
|
|
101
|
+
}
|
|
102
|
+
const stat = fs.lstatSync(absolute);
|
|
103
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
104
|
+
throw new Error(`Deployment record must be a regular file: ${recordPath}`);
|
|
105
|
+
}
|
|
106
|
+
return absolute;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const validateEntry = (value: unknown, label: string): DeploymentInfoEntry => {
|
|
110
|
+
if (!isRecord(value)) throw new Error(`${label} must be an object`);
|
|
111
|
+
const allowed = new Set([
|
|
112
|
+
"contractInfoKey",
|
|
113
|
+
"address",
|
|
114
|
+
"actualContract",
|
|
115
|
+
"proxyKind",
|
|
116
|
+
"implementationAddress",
|
|
117
|
+
"source",
|
|
118
|
+
"updatedAt",
|
|
119
|
+
]);
|
|
120
|
+
if (Object.keys(value).some((key) => !allowed.has(key))) {
|
|
121
|
+
throw new Error(`${label} contains unsupported fields`);
|
|
122
|
+
}
|
|
123
|
+
if (typeof value.contractInfoKey !== "string" || !value.contractInfoKey.trim()) {
|
|
124
|
+
throw new Error(`${label}.contractInfoKey is required`);
|
|
125
|
+
}
|
|
126
|
+
if (typeof value.actualContract !== "string" || !value.actualContract.trim()) {
|
|
127
|
+
throw new Error(`${label}.actualContract is required`);
|
|
128
|
+
}
|
|
129
|
+
if (value.proxyKind !== undefined && value.proxyKind !== "uups" && value.proxyKind !== "transparent") {
|
|
130
|
+
throw new Error(`${label}.proxyKind is invalid`);
|
|
131
|
+
}
|
|
132
|
+
if (typeof value.updatedAt !== "string" || !Number.isFinite(Date.parse(value.updatedAt))) {
|
|
133
|
+
throw new Error(`${label}.updatedAt must be an ISO timestamp`);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
contractInfoKey: value.contractInfoKey,
|
|
137
|
+
address: address(value.address, `${label}.address`),
|
|
138
|
+
actualContract: value.actualContract,
|
|
139
|
+
...(value.proxyKind ? { proxyKind: value.proxyKind } : {}),
|
|
140
|
+
...(value.implementationAddress
|
|
141
|
+
? { implementationAddress: address(value.implementationAddress, `${label}.implementationAddress`) }
|
|
142
|
+
: {}),
|
|
143
|
+
source: validateSource(value.source, `${label}.source`),
|
|
144
|
+
updatedAt: value.updatedAt,
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const validateDeploymentInfo = (value: unknown): DeploymentInfo => {
|
|
149
|
+
if (!isRecord(value) || value.version !== 1 || !isRecord(value.networks)) {
|
|
150
|
+
throw new Error("deploymentInfo.json must contain version 1 network metadata");
|
|
151
|
+
}
|
|
152
|
+
if (Object.keys(value).some((key) => key !== "version" && key !== "networks")) {
|
|
153
|
+
throw new Error("deploymentInfo.json contains unsupported fields");
|
|
154
|
+
}
|
|
155
|
+
const networks: DeploymentInfo["networks"] = {};
|
|
156
|
+
for (const [network, entries] of Object.entries(value.networks)) {
|
|
157
|
+
safeId(network, "deploymentInfo network");
|
|
158
|
+
if (!isRecord(entries)) throw new Error(`deploymentInfo network ${network} must be an object`);
|
|
159
|
+
networks[network] = Object.fromEntries(Object.entries(entries).map(([instanceId, entry]) => [
|
|
160
|
+
safeId(instanceId, `deploymentInfo ${network} instanceId`),
|
|
161
|
+
validateEntry(entry, `deploymentInfo ${network}.${instanceId}`),
|
|
162
|
+
]));
|
|
163
|
+
}
|
|
164
|
+
return { version: 1, networks };
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export function loadDeploymentInfo(root: string): DeploymentInfo {
|
|
168
|
+
const filePath = deploymentInfoPath(root);
|
|
169
|
+
if (!fs.existsSync(filePath)) return { version: 1, networks: {} };
|
|
170
|
+
return validateDeploymentInfo(readJsonFile<unknown>(filePath));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function saveDeploymentInfo(root: string, value: DeploymentInfo): void {
|
|
174
|
+
writeJsonFile(deploymentInfoPath(root), validateDeploymentInfo(value));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function prepareDeploymentInfoFromRecords(input: {
|
|
178
|
+
root: string;
|
|
179
|
+
records: DeploymentRecordPromotionInput[];
|
|
180
|
+
expectedContractInfo: ContractInfo;
|
|
181
|
+
}): DeploymentInfo {
|
|
182
|
+
const expectedContractInfo = validateContractInfo(input.expectedContractInfo);
|
|
183
|
+
const current = loadDeploymentInfo(input.root);
|
|
184
|
+
const networks = Object.fromEntries(Object.entries(current.networks).map(([network, entries]) => [
|
|
185
|
+
network,
|
|
186
|
+
{ ...entries },
|
|
187
|
+
]));
|
|
188
|
+
const seenPaths = new Set<string>();
|
|
189
|
+
|
|
190
|
+
for (const inputRecord of input.records) {
|
|
191
|
+
if (seenPaths.has(inputRecord.recordPath)) throw new Error(`Duplicate deployment record path: ${inputRecord.recordPath}`);
|
|
192
|
+
seenPaths.add(inputRecord.recordPath);
|
|
193
|
+
const absolutePath = confinedRecordPath(input.root, inputRecord.recordPath);
|
|
194
|
+
const record = readJsonFile<unknown>(absolutePath);
|
|
195
|
+
if (!isRecord(record) || !Array.isArray(record.changes)) {
|
|
196
|
+
throw new Error(`Deployment record is malformed: ${inputRecord.recordPath}`);
|
|
197
|
+
}
|
|
198
|
+
const network = safeId(record.network, "deployment record network");
|
|
199
|
+
const deploymentId = safeId(record.taskId, "deployment record taskId");
|
|
200
|
+
const executionId = safeId(record.executionId, "deployment record executionId");
|
|
201
|
+
const targetEntries = { ...(networks[network] ?? {}) };
|
|
202
|
+
|
|
203
|
+
for (const [index, rawChange] of record.changes.entries()) {
|
|
204
|
+
if (!isRecord(rawChange)) throw new Error(`Deployment record change ${index} must be an object`);
|
|
205
|
+
const instanceId = safeId(rawChange.instanceId, `deployment record change ${index} instanceId`);
|
|
206
|
+
const actualContract = safeId(rawChange.actualContract, `deployment record change ${index} actualContract`);
|
|
207
|
+
if (typeof rawChange.contractInfoKey !== "string" || !rawChange.contractInfoKey.trim()) {
|
|
208
|
+
throw new Error(`Deployment record change ${index} contractInfoKey is required`);
|
|
209
|
+
}
|
|
210
|
+
const resolved = resolveDeployment(expectedContractInfo, network, instanceId);
|
|
211
|
+
if (!resolved || resolved.key !== rawChange.contractInfoKey) {
|
|
212
|
+
throw new Error(`Deployment metadata conflicts with contractInfo for ${network}.${instanceId}`);
|
|
213
|
+
}
|
|
214
|
+
const deployedAddress = address(
|
|
215
|
+
rawChange.proxyAddress ?? rawChange.candidateAddress ?? rawChange.address,
|
|
216
|
+
`deployment record change ${index} address`,
|
|
217
|
+
);
|
|
218
|
+
if (resolved.address.toLowerCase() !== deployedAddress.toLowerCase()) {
|
|
219
|
+
throw new Error(`Deployment metadata address conflicts with contractInfo for ${network}.${instanceId}`);
|
|
220
|
+
}
|
|
221
|
+
const existing = targetEntries[instanceId];
|
|
222
|
+
if (
|
|
223
|
+
existing
|
|
224
|
+
&& existing.address.toLowerCase() !== deployedAddress.toLowerCase()
|
|
225
|
+
&& optionalAddress(rawChange.previousAddress, `deployment record change ${index} previousAddress`)?.toLowerCase()
|
|
226
|
+
!== existing.address.toLowerCase()
|
|
227
|
+
) {
|
|
228
|
+
throw new Error(`Deployment metadata address conflict for ${network}.${instanceId}`);
|
|
229
|
+
}
|
|
230
|
+
const rawProxyKind = rawChange.proxyKind === undefined
|
|
231
|
+
? existing?.proxyKind
|
|
232
|
+
: rawChange.proxyKind;
|
|
233
|
+
if (rawProxyKind !== undefined && rawProxyKind !== "uups" && rawProxyKind !== "transparent") {
|
|
234
|
+
throw new Error(`Deployment record change ${index} proxyKind is invalid`);
|
|
235
|
+
}
|
|
236
|
+
const proxyKind: DeploymentInfoEntry["proxyKind"] = rawProxyKind;
|
|
237
|
+
const implementationAddress = rawChange.implementationAddress === undefined
|
|
238
|
+
? existing?.implementationAddress
|
|
239
|
+
: optionalAddress(rawChange.implementationAddress, `deployment record change ${index} implementationAddress`);
|
|
240
|
+
const nextEntry = {
|
|
241
|
+
contractInfoKey: rawChange.contractInfoKey,
|
|
242
|
+
address: deployedAddress,
|
|
243
|
+
actualContract,
|
|
244
|
+
...(proxyKind ? { proxyKind } : {}),
|
|
245
|
+
...(implementationAddress ? { implementationAddress } : {}),
|
|
246
|
+
source: {
|
|
247
|
+
kind: "deployment" as const,
|
|
248
|
+
deploymentId,
|
|
249
|
+
executionId,
|
|
250
|
+
recordPath: inputRecord.recordPath,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
const existingComparable = existing
|
|
254
|
+
? {
|
|
255
|
+
contractInfoKey: existing.contractInfoKey,
|
|
256
|
+
address: existing.address,
|
|
257
|
+
actualContract: existing.actualContract,
|
|
258
|
+
...(existing.proxyKind ? { proxyKind: existing.proxyKind } : {}),
|
|
259
|
+
...(existing.implementationAddress
|
|
260
|
+
? { implementationAddress: existing.implementationAddress }
|
|
261
|
+
: {}),
|
|
262
|
+
source: existing.source,
|
|
263
|
+
}
|
|
264
|
+
: undefined;
|
|
265
|
+
targetEntries[instanceId] = {
|
|
266
|
+
...nextEntry,
|
|
267
|
+
updatedAt: existingComparable
|
|
268
|
+
&& JSON.stringify(existingComparable) === JSON.stringify(nextEntry)
|
|
269
|
+
? existing!.updatedAt
|
|
270
|
+
: new Date().toISOString(),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
networks[network] = targetEntries;
|
|
274
|
+
}
|
|
275
|
+
return validateDeploymentInfo({ version: 1, networks });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function updateDeploymentInfoFromRecords(input: {
|
|
279
|
+
root: string;
|
|
280
|
+
records: DeploymentRecordPromotionInput[];
|
|
281
|
+
expectedContractInfo: ContractInfo;
|
|
282
|
+
}): DeploymentInfo {
|
|
283
|
+
const next = prepareDeploymentInfoFromRecords(input);
|
|
284
|
+
saveDeploymentInfo(input.root, next);
|
|
285
|
+
return next;
|
|
286
|
+
}
|