gate-evm-tools 1.0.27 → 1.0.28
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/dist-runtime/config/hardhatAdapters.d.ts +2 -0
- package/dist-runtime/config/hardhatAdapters.js +8 -0
- package/dist-runtime/config/hardhatAdapters.js.map +1 -1
- package/dist-runtime/config/networks.d.ts +25 -1
- package/dist-runtime/config/networks.js +8 -2
- package/dist-runtime/config/networks.js.map +1 -1
- package/dist-runtime/runtime-identity.json +2 -2
- package/dist-runtime/workflow/lib/controlledMulticallAbi.d.ts +2 -0
- package/dist-runtime/workflow/lib/controlledMulticallAbi.js +10 -0
- package/dist-runtime/workflow/lib/controlledMulticallAbi.js.map +1 -0
- package/dist-runtime/workflow/lib/parameters.js +3 -2
- package/dist-runtime/workflow/lib/parameters.js.map +1 -1
- package/dist-runtime/workflow/steps/deploy.js +2 -1
- package/dist-runtime/workflow/steps/deploy.js.map +1 -1
- package/dist-runtime/workflow/steps/upgrade.js +2 -1
- package/dist-runtime/workflow/steps/upgrade.js.map +1 -1
- package/docs/direct-package-acceptance.md +20 -0
- package/docs/external-infrastructure-acceptance.md +33 -0
- package/docs/network-runtime-acceptance.md +18 -0
- package/docs/plans/2026-09-09-workflow-extraction.md +16 -0
- package/docs/superpowers/plans/2026-07-22-manifest-bootstrap-transaction.md +50 -0
- package/docs/superpowers/plans/2026-07-22-manifest-review-remediation.md +49 -0
- package/docs/superpowers/plans/2026-08-17-custody-hardhat-signers.md +227 -0
- package/docs/superpowers/plans/2026-09-09-remove-template-infrastructure-contracts.md +11 -0
- package/docs/workflow-reference.md +1181 -0
- package/docs/workflow-runtime-acceptance.md +17 -0
- package/docs/workflow-runtime.md +56 -0
- package/package.json +3 -2
- package/runtime/config/hardhatAdapters.ts +8 -0
- package/runtime/config/networks.ts +20 -11
- package/runtime/workflow/lib/controlledMulticallAbi.ts +6 -0
- package/runtime/workflow/lib/parameters.ts +3 -2
- package/runtime/workflow/steps/deploy.ts +2 -1
- package/runtime/workflow/steps/upgrade.ts +2 -1
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
# Full EVM workflow reference
|
|
2
|
+
|
|
3
|
+
Reference for the full EVM template and GateTool task-based deployment workflow.
|
|
4
|
+
Counter commands in this reference require the optional examples; projects created
|
|
5
|
+
with `--no-examples` provide their own business contracts and tasks.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`npm install` runs `patch-package` and applies the bundled
|
|
14
|
+
`@openzeppelin/hardhat-upgrades` patch. The template retains this patch for
|
|
15
|
+
OpenZeppelin plugin calls. The packaged workflow plans deployment and upgrade
|
|
16
|
+
calls through GateTool and controlled Multicall; it does not call the patched
|
|
17
|
+
`deployProxy` path. The zkSync workflow does not patch
|
|
18
|
+
`@matterlabs/hardhat-zksync-ethers`: local fork simulation uses GateTool's
|
|
19
|
+
node-managed signer, while real execution uses the same private-key or custody
|
|
20
|
+
EIP-712 signer boundary described below.
|
|
21
|
+
|
|
22
|
+
The patch does not write `contractInfo.json`. It only changes how OpenZeppelin
|
|
23
|
+
deploys proxies and implementations. The workflow runtime writes the
|
|
24
|
+
business-facing address to a pending registry candidate after a deploy step
|
|
25
|
+
succeeds. `workflow:commit-contract-info` promotes verified candidates in one
|
|
26
|
+
atomic registry commit; CheckCode is read-only. Implementation, transaction,
|
|
27
|
+
CREATE2, and gate-tool evidence stays in the execution record and
|
|
28
|
+
`deploymentInfo.json`.
|
|
29
|
+
|
|
30
|
+
## Hardhat And Foundry
|
|
31
|
+
|
|
32
|
+
The template supports both toolchains after `npm install`:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm run compile
|
|
36
|
+
npm run compile:forge
|
|
37
|
+
npm test
|
|
38
|
+
npm run test:forge
|
|
39
|
+
npm run test:all
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`foundry.toml` uses Solidity 0.8.28, Cancun EVM, IR compilation, and optimizer
|
|
43
|
+
settings aligned with `hardhat.config.ts`. Normal project initialization installs
|
|
44
|
+
`forge-std` v1.16.2 into `lib/forge-std` without creating a Git submodule. If dependency
|
|
45
|
+
installation was skipped with `--no-install`, run `npm run install:foundry`.
|
|
46
|
+
|
|
47
|
+
Compiler, contract, and build dependencies use exact versions. The published
|
|
48
|
+
scaffold baseline uses `gate-evm-tools` `^1.0.28`, the minimum runtime for this template. Normal project initialization
|
|
49
|
+
installs that release range from the registry. When validating unreleased
|
|
50
|
+
GateTool changes in a local integration, override it with the locally packed
|
|
51
|
+
artifact inside the initialized test project:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install --save-dev /absolute/path/to/gate-evm-tools-local.tgz
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Keep this override and its lockfile change only in the initialized local test
|
|
58
|
+
project; do not put a machine-specific tarball path in the reusable template.
|
|
59
|
+
Keep the lockfile generated by the selected package manager so each initialized
|
|
60
|
+
project remains reproducible. The bundled contracts and Foundry tests pin
|
|
61
|
+
`pragma solidity 0.8.28`.
|
|
62
|
+
|
|
63
|
+
When running this CLI's Project Docs commands on Windows, set
|
|
64
|
+
`GATE_PROJECT_DOCS_GIT_EXECUTABLE` to the verified absolute path of the regular
|
|
65
|
+
Git for Windows `git.exe` file. Project Docs does not search the project or
|
|
66
|
+
`PATH` for that executable.
|
|
67
|
+
|
|
68
|
+
EVM and zkSync deployments share the same manifest and Solidity sources, but
|
|
69
|
+
they never share compiler output. `hardhat.config.ts` reads the selected
|
|
70
|
+
network configuration: a target with `zksync: true` writes zksolc output to
|
|
71
|
+
`artifacts-zk` and `cache-zk`; every EVM target continues to use `artifacts`
|
|
72
|
+
and `cache`. Always compile or run with the intended `--network` before
|
|
73
|
+
deriving CREATE2 hashes so an EVM artifact cannot be mistaken for an EraVM
|
|
74
|
+
artifact (or vice versa).
|
|
75
|
+
|
|
76
|
+
## Wallet Credential
|
|
77
|
+
|
|
78
|
+
The project uses one wallet credential for both native private keys and custody
|
|
79
|
+
wallets. Keep it only in the untracked project `.env` or process environment:
|
|
80
|
+
|
|
81
|
+
```dotenv
|
|
82
|
+
# A normal private key remains supported without script changes.
|
|
83
|
+
PRIVATE_KEY=0x...
|
|
84
|
+
|
|
85
|
+
# Or use the pseudo-private-key form copied from the custody service.
|
|
86
|
+
PRIVATE_KEY=custody:<ProjectSecret>:<WalletAddress>
|
|
87
|
+
CUSTODY_API_BASE_URL=https://custody.example.com
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`CUSTODY_API_BASE_URL` must be an HTTP(S) base URL without embedded
|
|
91
|
+
credentials, query parameters, or fragments.
|
|
92
|
+
|
|
93
|
+
`WALLET_CREDENTIAL` is an optional alias for `PRIVATE_KEY`. If both variables
|
|
94
|
+
are configured, they must contain the same value. `hardhat.config.ts` loads the
|
|
95
|
+
custody plugin after the ethers and OpenZeppelin plugins. A real private key is
|
|
96
|
+
passed through native `networks.accounts`; a custody credential leaves native
|
|
97
|
+
accounts empty and supplies the remote signer through Hardhat and the workflow
|
|
98
|
+
signer factory. Existing deploy scripts that use `ethers.getSigners()`, contract
|
|
99
|
+
factories, or `upgrades.deployProxy()` therefore do not need custody-specific
|
|
100
|
+
branches. The bundled OpenZeppelin patch remains required for its independent
|
|
101
|
+
CREATE2, Multicall, and proxy deployment behavior.
|
|
102
|
+
|
|
103
|
+
SmartFlow derives the public signer address from the selected `.env` credential
|
|
104
|
+
when it creates a task and writes that public address to
|
|
105
|
+
`task.yaml.deploymentSignerAddress`. That task value is a frozen expected
|
|
106
|
+
identity for simulation, audit, and execution locking; it is not a second
|
|
107
|
+
secret or a dynamically selected account. Execute restores the signer from the
|
|
108
|
+
current `.env` credential and rejects it before broadcast unless its address
|
|
109
|
+
matches the frozen `deploymentSignerAddress`. Project Secret and pseudo-private
|
|
110
|
+
key values must never be written to task YAML, result evidence, logs, or Git.
|
|
111
|
+
|
|
112
|
+
zkSync planning uses only a public operator address and does not read a wallet
|
|
113
|
+
credential. zkSync operator execution supports either a local private-key wallet
|
|
114
|
+
or the configured custody wallet. Both paths use the same `zksync-ethers` EIP-712
|
|
115
|
+
envelope, including the locked `factoryDeps`, gas-per-pubdata, and paymaster data.
|
|
116
|
+
An unrelated external Admin route still fails closed before signing.
|
|
117
|
+
|
|
118
|
+
## Workflow Model
|
|
119
|
+
|
|
120
|
+
SmartFlow should create real deployment tasks under `scripts/tasks/<taskId>/`.
|
|
121
|
+
This template only ships an example task at `scripts/tasks/_examples/counter-rollout`.
|
|
122
|
+
|
|
123
|
+
SmartFlow's developer lane selects one `developmentNetwork` and generates:
|
|
124
|
+
|
|
125
|
+
- `manifest.yaml`
|
|
126
|
+
- `parameters.schema.json`
|
|
127
|
+
- `parameters.yaml`
|
|
128
|
+
- `release.json`
|
|
129
|
+
- optional task-local runnable scripts only for `kind: custom`
|
|
130
|
+
- `docs/developer-handoff.md`
|
|
131
|
+
|
|
132
|
+
The development deploy lane executes the complete manifest on that network in
|
|
133
|
+
one process, verifies the business `view`/`check` suffix with the same execution
|
|
134
|
+
ID, and commits the verified candidate before CheckCode. Release verification
|
|
135
|
+
then adds `releaseTargetNetworks`, derives each target's parameters, and runs one
|
|
136
|
+
complete fork simulation per target. The administrator selects only a merged,
|
|
137
|
+
release-qualified task and creates an immutable `parameters.lock.json` plus a
|
|
138
|
+
per-transaction authority plan during final simulation. Execute must reuse that
|
|
139
|
+
exact lock and plan.
|
|
140
|
+
|
|
141
|
+
Every standard CREATE2 deployment, UUPS initial deployment, and UUPS upgrade is
|
|
142
|
+
prepared in-process through `prepareWorkflow(hre, request)` from
|
|
143
|
+
`gate-evm-tools/workflow`. GateTool owns strict artifact loading, OpenZeppelin
|
|
144
|
+
validation, deterministic addresses, Factory and `upgradeToAndCall` calldata,
|
|
145
|
+
and zkSync dependency collection. The template owns target policy, authority,
|
|
146
|
+
controlled Multicall wrapping, immutable plan hashing, signing, broadcasting,
|
|
147
|
+
receipt reconciliation, and registry writes. SmartFlow independently verifies
|
|
148
|
+
those canonical artifacts instead of rebuilding CREATE2 or upgrade calldata.
|
|
149
|
+
|
|
150
|
+
Development uses one protocol-aware current-state wrapper. At startup it must
|
|
151
|
+
**capture current latest once**, binding `chainId`, `blockNumber`, and
|
|
152
|
+
`blockHash` for that run only. EVM resets Hardhat 2.26.3 to that exact snapshot.
|
|
153
|
+
The real-network source process has no wallet accounts: it clears inherited
|
|
154
|
+
wallet credentials, and its tokenized loopback proxy accepts only an audited
|
|
155
|
+
allowlist of read-only fork RPC methods. Signing, submission, mining, and
|
|
156
|
+
unknown RPC methods are rejected before they reach the target provider.
|
|
157
|
+
For zkSync the wrapper also reads the captured target block's `protocolVersion`
|
|
158
|
+
and `l1BatchNumber`, then queries the installed runner capability. The bundled
|
|
159
|
+
anvil-zksync runner capability owns the exact runner identity and supported
|
|
160
|
+
protocol matrix; documentation and callers do not duplicate that matrix. The
|
|
161
|
+
canonical snapshot and evidence retain the real source `protocolVersion`, exact
|
|
162
|
+
runner identity, and the capability-resolved `runnerProtocolVersion`; a verified
|
|
163
|
+
compatibility mapping may make the two protocol values differ. If the installed
|
|
164
|
+
capability does not support the captured protocol, or the runner cannot execute
|
|
165
|
+
it, simulation stops before credentials, signing, or broadcast. Upgrade the
|
|
166
|
+
local runner or workflow dependency, then start a new simulation run.
|
|
167
|
+
An EVM Hardhat fork is never accepted as zkSync evidence. Every zkSync
|
|
168
|
+
transaction carries its explicit scheme and `factoryDeps` in the immutable
|
|
169
|
+
locked execution plan. A
|
|
170
|
+
finalized UUPS deploy or upgrade is incomplete until its `gateToolSync`
|
|
171
|
+
evidence confirms the stage-routed manifest update.
|
|
172
|
+
|
|
173
|
+
## Run The Example
|
|
174
|
+
|
|
175
|
+
The example task is a workflow shape reference. Infrastructure addresses are project config:
|
|
176
|
+
runtime deployment, simulation, and execute-time locking read the CREATE2
|
|
177
|
+
factory, blank UUPS placeholder implementation, and controlled multicall from
|
|
178
|
+
the selected target in `config.yaml`.
|
|
179
|
+
SmartFlow does not ship an infrastructure address registry.
|
|
180
|
+
Before running against a target, configure that target's deployment
|
|
181
|
+
infrastructure in `config.yaml`:
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
gtDev:
|
|
185
|
+
environment: development
|
|
186
|
+
admin: ""
|
|
187
|
+
create2:
|
|
188
|
+
factory: "0x2370B48f20FFd75A46B14A9888B3E79677eFC28c"
|
|
189
|
+
leadingZeroBytes: 4
|
|
190
|
+
caller: "0x0000000000000000000000000000000000000000"
|
|
191
|
+
uups:
|
|
192
|
+
placeholderImplementation: "0x000000009F21E08bE874b78F40A6d038587E0C54"
|
|
193
|
+
multicall:
|
|
194
|
+
controlledMulticall: "0x00000000bD2f006A24b6d99baaD1323D5809948f"
|
|
195
|
+
|
|
196
|
+
zkDev:
|
|
197
|
+
environment: development
|
|
198
|
+
admin: ""
|
|
199
|
+
create2:
|
|
200
|
+
scheme: zksync-eravm
|
|
201
|
+
factory: "0x8Cb318b70b93cEC7E9601Fd3D391Fa28892f6D1e"
|
|
202
|
+
leadingZeroBytes: 4
|
|
203
|
+
caller: "0x0000000000000000000000000000000000000000"
|
|
204
|
+
uups:
|
|
205
|
+
placeholderImplementation: "0x00000000258e0D79F9FE344BADA1bb3c14A11dE1"
|
|
206
|
+
multicall:
|
|
207
|
+
controlledMulticall: "0x00000000ceAA9C77a78aFb970B85DB5917B05908"
|
|
208
|
+
|
|
209
|
+
zkProd:
|
|
210
|
+
environment: production
|
|
211
|
+
admin: ""
|
|
212
|
+
create2:
|
|
213
|
+
scheme: zksync-eravm
|
|
214
|
+
factory: "0x98fe320B225305cA57F83c322d919D477c9f1641"
|
|
215
|
+
leadingZeroBytes: 4
|
|
216
|
+
caller: "0x0000000000000000000000000000000000000000"
|
|
217
|
+
uups:
|
|
218
|
+
placeholderImplementation: "0x00000000258e0D79F9FE344BADA1bb3c14A11dE1"
|
|
219
|
+
multicall:
|
|
220
|
+
controlledMulticall: "0x00000000ceAA9C77a78aFb970B85DB5917B05908"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
npm run workflow:example
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
For a SmartFlow-generated task, development simulation creates the immutable
|
|
228
|
+
execution plan without creating `parameters.lock.json`:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
npm run workflow -- \
|
|
232
|
+
--task 20260608-counter-rollout \
|
|
233
|
+
--network hardhat \
|
|
234
|
+
--mode simulate \
|
|
235
|
+
--target gtDev \
|
|
236
|
+
--releaseKind development \
|
|
237
|
+
--executionId development-gtDev-1
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For zkSync development, use the same wrapper entry. It captures the target
|
|
241
|
+
block once and selects the approved EraVM runner from that block's protocol
|
|
242
|
+
version:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm run workflow -- \
|
|
246
|
+
--task 20260608-counter-rollout \
|
|
247
|
+
--network hardhat \
|
|
248
|
+
--mode simulate \
|
|
249
|
+
--target zkDev \
|
|
250
|
+
--releaseKind development \
|
|
251
|
+
--executionId development-zkDev-1 \
|
|
252
|
+
--simulationSigner <operator-address>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The bundled runner capability resolves the pinned anvil-zksync identity and
|
|
256
|
+
`runnerProtocolVersion` for the captured source protocol. The runner receives
|
|
257
|
+
that capability-resolved value, while canonical evidence records both the real
|
|
258
|
+
source value and the runner value; a verified compatibility mapping may make
|
|
259
|
+
them differ. Documentation and callers do not duplicate the capability's
|
|
260
|
+
numeric protocol matrix.
|
|
261
|
+
The explicitly configured `127.0.0.1` `zkFork` uses GateTool's node-managed
|
|
262
|
+
signer after the workflow asks the local runner to impersonate the simulation
|
|
263
|
+
address. That signer submits the complete EraVM RPC envelope, including a
|
|
264
|
+
non-zero gas-per-pubdata value and `factoryDeps`, without patching third-party
|
|
265
|
+
dependencies or enabling impersonation on a real network. If the installed
|
|
266
|
+
capability does not declare the captured protocol, upgrade the runner or
|
|
267
|
+
workflow dependency and start a new simulation. The wrapper never changes modes
|
|
268
|
+
or connects a planning signer to the real target.
|
|
269
|
+
A reorg, hash mismatch,
|
|
270
|
+
or protocol metadata mismatch also fails closed and requires a new execution ID
|
|
271
|
+
and newly captured current snapshot. After a successful canonical simulation,
|
|
272
|
+
development execute must reuse its execution ID and an explicitly configured
|
|
273
|
+
local `PRIVATE_KEY` whose address matches the locked operator:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
npm run workflow -- \
|
|
277
|
+
--task 20260608-counter-rollout \
|
|
278
|
+
--network zkDev \
|
|
279
|
+
--mode execute \
|
|
280
|
+
--releaseKind development \
|
|
281
|
+
--executionId development-zkDev-1
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`npm run workflow` is a TypeScript wrapper around `hardhat run`; it forwards
|
|
285
|
+
`--network` to Hardhat and passes workflow arguments such as `--task`, `--mode`,
|
|
286
|
+
`--target`, `--step`, `--from`, `--simulationSigner`, and `--operator` to the runtime.
|
|
287
|
+
For `--mode execute` on real configured networks, the runtime fails early when
|
|
288
|
+
neither `WALLET_CREDENTIAL` nor `PRIVATE_KEY` is present in the project `.env`
|
|
289
|
+
or process environment.
|
|
290
|
+
Every target whose `environment` is `production` additionally requires
|
|
291
|
+
`--useLock`; a production execute without the immutable final-simulation plan
|
|
292
|
+
is rejected before credential selection, recorder creation, or broadcasting.
|
|
293
|
+
|
|
294
|
+
Development and Admin `--useLock` simulation each run as one selected public EVM
|
|
295
|
+
address. A normal development simulation may read `deploymentSignerAddress`
|
|
296
|
+
from the task's `task.yaml`; an explicit `--simulationSigner` must match it. An
|
|
297
|
+
Admin final simulation using `--useLock` never falls back to task metadata and
|
|
298
|
+
requires the argument explicitly. Both modes ignore `simulation-callers.yaml`.
|
|
299
|
+
No private key is required to impersonate a public address on the selected local
|
|
300
|
+
Hardhat or EraVM fork.
|
|
301
|
+
|
|
302
|
+
The first real execution is limited to the task's development network. It must
|
|
303
|
+
reuse the simulation execution ID and must not use `--useLock`:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npm run workflow -- \
|
|
307
|
+
--task 20260608-counter-rollout \
|
|
308
|
+
--network gtDev \
|
|
309
|
+
--mode execute \
|
|
310
|
+
--releaseKind development \
|
|
311
|
+
--executionId development-gtDev-1
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Run the business verification suffix with that same execution ID, then commit
|
|
315
|
+
the verified development candidate before CheckCode:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
npm run workflow -- \
|
|
319
|
+
--task 20260608-counter-rollout \
|
|
320
|
+
--network gtDev \
|
|
321
|
+
--mode verify \
|
|
322
|
+
--executionId development-gtDev-1 \
|
|
323
|
+
--from <first-view-or-check-step>
|
|
324
|
+
|
|
325
|
+
npm run workflow:commit-contract-info -- \
|
|
326
|
+
--task 20260608-counter-rollout \
|
|
327
|
+
--commitId development-gtDev-1 \
|
|
328
|
+
--selection gtDev=development-gtDev-1
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
After development verify, commit the development registry and run CheckCode
|
|
332
|
+
against the formal registry. The live `ReleaseSimulationCallersInput` node then
|
|
333
|
+
creates `scripts/tasks/<deployment-id>/simulation-callers.yaml`, after the
|
|
334
|
+
release target set and parameters are known and immediately before target
|
|
335
|
+
qualification. It contains one required default caller per target and optional
|
|
336
|
+
per-Manifest-step overrides; it contains public addresses only.
|
|
337
|
+
|
|
338
|
+
Each full release target qualification runs without a release-target simulation signer
|
|
339
|
+
argument and automatically loads that file. It must execute the complete
|
|
340
|
+
Manifest in order, on one shared fork per target:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
npm run workflow -- \
|
|
344
|
+
--task 20260608-counter-rollout \
|
|
345
|
+
--network hardhat \
|
|
346
|
+
--mode simulate \
|
|
347
|
+
--target gtProd \
|
|
348
|
+
--executionId qualification-gtProd-1
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
There is no `--simulationSigner` compatibility fallback for release-target
|
|
352
|
+
qualification. Development simulation and the existing Admin workflow ignore
|
|
353
|
+
`simulation-callers.yaml` and retain their signer, lock, plan, and calldata
|
|
354
|
+
behavior.
|
|
355
|
+
|
|
356
|
+
After qualification, the Admin final simulation still writes and consumes the
|
|
357
|
+
immutable execution lock, so provide its operator, execution ID, source
|
|
358
|
+
SuperTask, and merged commit:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
npm run workflow -- \
|
|
362
|
+
--task 20260608-counter-rollout \
|
|
363
|
+
--network hardhat \
|
|
364
|
+
--mode simulate \
|
|
365
|
+
--target gtProd \
|
|
366
|
+
--simulationSigner <operator-address> \
|
|
367
|
+
--releaseKind admin_release \
|
|
368
|
+
--useLock \
|
|
369
|
+
--executionId release-gtProd-1 \
|
|
370
|
+
--sourceSuperTaskId <supertask-id> \
|
|
371
|
+
--mergeCommit <40-char-commit>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
EVM simulation mode uses the selected target's configured provider only in a
|
|
375
|
+
loopback source process. That process captures `latest` exactly once and resets
|
|
376
|
+
Hardhat 2.26.3 to the captured block. The workflow then exclusively claims
|
|
377
|
+
`results/<target>/<executionId>/simulation/` before config, manifest, or step
|
|
378
|
+
preflight. It immediately creates `execution.json`, `view.json`, and
|
|
379
|
+
`verification.json` in that directory with empty `steps`, so an early failure
|
|
380
|
+
cannot expose evidence from an older run. Target and execution ID are validated
|
|
381
|
+
as individual safe path segments; dots inside one segment cannot alias another
|
|
382
|
+
target or execution.
|
|
383
|
+
|
|
384
|
+
Before running the manifest, the runtime checks Factory, Controlled Multicall,
|
|
385
|
+
and Placeholder code at the captured block. An existing or old UUPS upgrade also
|
|
386
|
+
checks the proxy code, ERC-1967 implementation slot, and old implementation code
|
|
387
|
+
at that same block. Post-deployment assertions continue to read local `latest`
|
|
388
|
+
because newly deployed code is not present in the source snapshot.
|
|
389
|
+
|
|
390
|
+
Every successful simulation appends a terminal
|
|
391
|
+
`workflow-simulation-completed` step with the exact ordered step IDs that ran,
|
|
392
|
+
the applicable signer/caller binding, the immutable execution plan hash, and a
|
|
393
|
+
sanitized `fork` object containing only `sourceNetwork`, `chainId`, `blockNumber`,
|
|
394
|
+
`blockHash`, and runner kind/version
|
|
395
|
+
to `results/<target>/<executionId>/simulation/execution.json`. There is no
|
|
396
|
+
mutable latest result file. The claimed `(target, executionId, mode)` namespace
|
|
397
|
+
cannot be reused or overwritten, whether the run succeeds or fails; retries must
|
|
398
|
+
use a new execution ID. Distinct execution IDs therefore write independent files.
|
|
399
|
+
|
|
400
|
+
Immediately before terminal success, the wrapper checks the source and local
|
|
401
|
+
fork block hash again. A reorg or hash mismatch must fail closed; retry with a
|
|
402
|
+
new execution ID so the next process captures its then-current `latest`. Fork
|
|
403
|
+
evidence must never include an RPC URL, credential, loopback proxy URL, process
|
|
404
|
+
arguments, or command line.
|
|
405
|
+
|
|
406
|
+
`status.json` records the successful simulation's confined relative path as
|
|
407
|
+
`simulationResult`. Release Verify must copy the exact returned status evidence
|
|
408
|
+
path into `release.json` under the target's `simulationResult`; it must not derive
|
|
409
|
+
or rename the path. A failed current run clears any older top-level
|
|
410
|
+
`simulationResult` from global status. Admin simulation with `--useLock` records
|
|
411
|
+
the same scoped path in `parameters.lock.json.finalSimulation.result`, with the
|
|
412
|
+
lock-success update as the final fallible side effect. Failed or partial
|
|
413
|
+
simulations retain their scoped partial evidence but do not append the terminal
|
|
414
|
+
marker and cannot make a lock executable. Simulation never writes `contractInfo.json`.
|
|
415
|
+
|
|
416
|
+
The shared execution-scoped naming scheme is
|
|
417
|
+
`results/<target>/<executionId>/<mode>/<result>.json`, where simulate uses the
|
|
418
|
+
`simulation` mode directory. Separate mode claims allow Admin simulation,
|
|
419
|
+
execute, and verify to reuse one locked `executionId` without sharing result
|
|
420
|
+
files. Examples include `results/gtProd/release-gtProd-1/execute/execution.json`
|
|
421
|
+
and `results/gtProd/release-gtProd-1/verify/verification.json`.
|
|
422
|
+
A normal development simulation does not write `parameters.lock.json`; the final
|
|
423
|
+
release simulation shown above deliberately creates and marks the immutable lock
|
|
424
|
+
as simulated while retaining its release fingerprint evidence.
|
|
425
|
+
The lock binds `operator`, `target.signer`, and terminal simulation
|
|
426
|
+
`signerAddress` to the same identity. Final simulation resolves authority for
|
|
427
|
+
every state-changing transaction. It executes an operator route with the
|
|
428
|
+
selected signer and impersonates only that transaction's required external
|
|
429
|
+
executor on the fork. A development plan rejects every external route. An Admin
|
|
430
|
+
plan may interleave contiguous operator and external segments in manifest order;
|
|
431
|
+
the immutable transaction order and executor of every route remain fixed.
|
|
432
|
+
|
|
433
|
+
The administrator executes the locked plan with the same `executionId`:
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
npm run workflow -- --task 20260608-counter-rollout --network gtProd --mode execute --useLock --executionId release-gtProd-1
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Execute replays the completed plan prefix without rebroadcasting it, sends the
|
|
440
|
+
next contiguous operator segment, and stops at the following external segment.
|
|
441
|
+
It cumulatively appends operator receipts and external segments to the canonical
|
|
442
|
+
`calldata/<network>/<executionId>/external-execution.json` package, leaving the
|
|
443
|
+
record in `waiting_external`. It never emits the old per-step or batch format.
|
|
444
|
+
After the external executor completes the current segment, reconcile finalized
|
|
445
|
+
chain state in bounded batches:
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npm run workflow:reconcile-external -- --task 20260608-counter-rollout --network gtProd --executionId release-gtProd-1
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Repeat reconciliation while it reports `waiting_external` or
|
|
452
|
+
`waiting_finality`. When it reports `waiting_operator`, rerun the same locked
|
|
453
|
+
execute command to advance the next operator segment, then reconcile again.
|
|
454
|
+
Only `not_required` or `reconciled` permits business verify.
|
|
455
|
+
SmartFlow's Admin confirmation action only records the human confirmation and
|
|
456
|
+
advances the workflow. The following deterministic network runner performs
|
|
457
|
+
reconciliation and then the manifest's read-only `view`/`check` suffix for each
|
|
458
|
+
network, with bounded concurrency across networks. It cannot force completion
|
|
459
|
+
or bypass a reverted, missing, unfinalized, or reorged transaction. A release
|
|
460
|
+
may contain multiple transactions, so verification is bound to the execution ID
|
|
461
|
+
rather than one transaction hash.
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
npm run workflow -- --task 20260608-counter-rollout --network gtProd --mode verify --executionId <same-executionId> --useLock --from <first-view-or-check-step>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
`<same-executionId>` is the immutable lock ID used by final simulation and
|
|
468
|
+
execute. Verify uses its locked resolved parameters and the same pending
|
|
469
|
+
candidate addresses. A successful view/check verification updates
|
|
470
|
+
`verifyStatus` without changing the network's deployment status. After every
|
|
471
|
+
selected network is reconciled and verified, commit all selections atomically:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
npm run workflow:commit-contract-info -- \
|
|
475
|
+
--task 20260608-counter-rollout \
|
|
476
|
+
--commitId admin-release-1 \
|
|
477
|
+
--selection gtProd=<same-executionId>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
The commit writes `deployed_pending_code_audit` before CheckCode. SmartFlow runs
|
|
481
|
+
this commit automatically after its Admin verify node; there is no independent
|
|
482
|
+
registry-write approval button.
|
|
483
|
+
|
|
484
|
+
## CheckCode
|
|
485
|
+
|
|
486
|
+
CheckCode is a signer-free project operation. It compiles the current sources,
|
|
487
|
+
checks the formal `contractInfo.json` with gate-tool, reads proxy classification
|
|
488
|
+
from `deploymentInfo.json`, and conditionally syncs only stale UUPS target
|
|
489
|
+
manifests. It never reads, promotes, or writes a registry candidate.
|
|
490
|
+
|
|
491
|
+
For a SmartFlow SuperTask, create immutable discovery evidence before the
|
|
492
|
+
runner starts:
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
npm run workflow:checkcode-plan -- \
|
|
496
|
+
--runId <checkcode-run-id> \
|
|
497
|
+
--artifactHash <release-artifact-hash> \
|
|
498
|
+
--deploymentId <deployment-id> \
|
|
499
|
+
--sourceSuperTaskId <supertask-id> \
|
|
500
|
+
--developmentNetwork <development-network> \
|
|
501
|
+
--executionId <registry-execution-id> \
|
|
502
|
+
--manifestDir <configured-manifest-dir> \
|
|
503
|
+
--contract <saveAs-or-contract>
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
The four SuperTask identity arguments are all-or-nothing, and the plan may
|
|
507
|
+
target only `developmentNetwork`. This writes the canonical plan to
|
|
508
|
+
`scripts/tasks/<deployment-id>/docs/checkcode-plan.md`; do not hand-edit it.
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
npm run workflow:checkcode -- \
|
|
512
|
+
--runId <checkcode-run-id> \
|
|
513
|
+
--artifactHash <release-artifact-hash> \
|
|
514
|
+
--network gtDev \
|
|
515
|
+
--contract Counter
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Repeat `--network <network>` or `--contract <saveAs-or-contract>` to narrow the
|
|
519
|
+
scope. `--runId <id>` selects the report directory under
|
|
520
|
+
`scripts/checkcode/runs/<id>/`; otherwise the runner creates one. The same entry
|
|
521
|
+
is available through `npm run workflow -- --checkcode ...`. The canonical report
|
|
522
|
+
is `version: 1`; each entry records `manifestDir`, before/after hashes, and one of
|
|
523
|
+
`not_required`, `already_current`, `synced`, `proxy_kind_unknown`, or `failed`.
|
|
524
|
+
CheckCode does not read `PRIVATE_KEY` and is independent of a single deployment
|
|
525
|
+
transaction hash. CheckCode proves runtime bytecode and proxy implementation
|
|
526
|
+
consistency. A business check in the task manifest separately proves
|
|
527
|
+
initialization, ownership, roles, limits, and other configured state; neither
|
|
528
|
+
result substitutes for the other.
|
|
529
|
+
|
|
530
|
+
After the selected canonical report succeeds, finalize a development task with:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
npm run workflow:checkcode-finalize -- \
|
|
534
|
+
--deploymentId <deployment-id> \
|
|
535
|
+
--sourceSuperTaskId <supertask-id> \
|
|
536
|
+
--developmentNetwork <development-network> \
|
|
537
|
+
--executionId <registry-execution-id> \
|
|
538
|
+
--runId <checkcode-run-id>
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
Finalize validates the report and current task identity before updating
|
|
542
|
+
`status.json`, `handoff.json`, and `docs/checkcode-report.md`. It keeps
|
|
543
|
+
`releaseTargetNetworks` empty during development; Admin Verify selects release
|
|
544
|
+
targets later. The formal `contractInfo.json` is byte-for-byte read-only during
|
|
545
|
+
CheckCode.
|
|
546
|
+
|
|
547
|
+
## Developer Static Validation
|
|
548
|
+
|
|
549
|
+
Generate the Developer gate evidence with the template runtime instead of
|
|
550
|
+
assembling the report manually:
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
npm run workflow:static-validate -- --task <deployment-id>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
The command runs `npm run compile`, `npx tsc --noEmit`, the project test command,
|
|
557
|
+
and parses every YAML/JSON document in the task directory. It writes
|
|
558
|
+
`scripts/tasks/<deployment-id>/docs/static-validation.md` and reports `failed`
|
|
559
|
+
if any required check fails.
|
|
560
|
+
|
|
561
|
+
Workflow subprocesses prefer the checked-out project's executable under
|
|
562
|
+
`node_modules/.bin`; when it is absent they use `npx --no-install`, so task runs
|
|
563
|
+
never install a different Hardhat, ts-node, or gate-tool version implicitly.
|
|
564
|
+
During execute, the signer restored from the selected wallet credential must
|
|
565
|
+
match the optional `task.yaml.deploymentSignerAddress` before any workflow step
|
|
566
|
+
or broadcast runs.
|
|
567
|
+
|
|
568
|
+
## Release Verification Finalize
|
|
569
|
+
|
|
570
|
+
After every selected release target has complete parameters and successful
|
|
571
|
+
canonical simulation evidence, finalize the Verifier handoff with one runtime
|
|
572
|
+
command:
|
|
573
|
+
|
|
574
|
+
```bash
|
|
575
|
+
npm run workflow:release-finalize -- \
|
|
576
|
+
--task <deployment-id> \
|
|
577
|
+
--network <release-target-network>
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
Repeat `--network` for every release target. The command requires the exact
|
|
581
|
+
target set from `task.yaml`, `release.json`, and `simulation-callers.yaml`. It
|
|
582
|
+
re-reads the exact Manifest bytes, recomputes each caller network hash, and
|
|
583
|
+
validates the ordered terminal caller map plus every step annotation across the
|
|
584
|
+
execution, view, and verification results. Old signer-only target evidence has
|
|
585
|
+
no compatibility fallback and is stale. Only after all targets pass does it
|
|
586
|
+
write canonical release-ready state to
|
|
587
|
+
`release.json`, `status.json`, `handoff.json`, and
|
|
588
|
+
`docs/release-verification.md`. If any target is invalid, those formal artifacts
|
|
589
|
+
remain unchanged. This step does not create an Admin parameter lock, deploy to a
|
|
590
|
+
release network, or modify `contractInfo.json`.
|
|
591
|
+
|
|
592
|
+
For each selected target, CheckCode resolves one exact OpenZeppelin manifest
|
|
593
|
+
file from the configured `manifestDir` and the RPC chain ID. It never combines
|
|
594
|
+
proxy, implementation, or storage-layout evidence from sibling JSON files. The
|
|
595
|
+
canonical report records that relative file as `manifestFile`.
|
|
596
|
+
|
|
597
|
+
SmartFlow may select multiple simulation target networks, but it should call the
|
|
598
|
+
runtime once per target. If a simulation fails, `status.json` includes
|
|
599
|
+
`errorType` and `retryable`. Retry only when `retryable: true`, which is reserved
|
|
600
|
+
for transient RPC or network failures. Do not retry deterministic failures such
|
|
601
|
+
as `check_failed`, `parameter_error`, `config_error`, `contract_error`, or
|
|
602
|
+
`execution_reverted`; those should be sent back to the developer worktree for a
|
|
603
|
+
script or parameter fix.
|
|
604
|
+
|
|
605
|
+
## Import Legacy Deployment State
|
|
606
|
+
|
|
607
|
+
Use `workflow:import-state` after migrating legacy contracts, artifacts, and the
|
|
608
|
+
two OpenZeppelin state directories. It builds canonical `contractInfo.json` and
|
|
609
|
+
`deploymentInfo.json` records from a reviewed plan and writes immutable evidence
|
|
610
|
+
under `scripts/migrations/<migrationId>/`.
|
|
611
|
+
|
|
612
|
+
Development and production are release stages, not different chains. For
|
|
613
|
+
example, `bscDev` and `bscProd` may use the same chainId (`56`), the same mainnet
|
|
614
|
+
chain, and the same RPC route while identifying two independent contract address
|
|
615
|
+
sets. Confirm them separately: `bscDev` uses `.openzeppelin.dev`, while
|
|
616
|
+
`bscProd` uses `.openzeppelin`. Never merge those manifests or infer the stage
|
|
617
|
+
from the chain ID.
|
|
618
|
+
|
|
619
|
+
Create a project-local plan such as
|
|
620
|
+
`scripts/migrations/legacy-bsc-state.plan.json`:
|
|
621
|
+
|
|
622
|
+
```json
|
|
623
|
+
{
|
|
624
|
+
"version": 1,
|
|
625
|
+
"migrationId": "legacy-bsc-state",
|
|
626
|
+
"sourceProject": "legacy-vault",
|
|
627
|
+
"networks": {
|
|
628
|
+
"bscDev": {
|
|
629
|
+
"sourceNetwork": "development",
|
|
630
|
+
"releaseStage": "development",
|
|
631
|
+
"chainId": 56,
|
|
632
|
+
"manifestDir": ".openzeppelin.dev",
|
|
633
|
+
"contracts": [
|
|
634
|
+
{
|
|
635
|
+
"instanceId": "vault",
|
|
636
|
+
"contractInfoKey": "vault:Vault",
|
|
637
|
+
"address": "0x1111111111111111111111111111111111111111",
|
|
638
|
+
"actualContract": "Vault",
|
|
639
|
+
"proxyKind": "uups",
|
|
640
|
+
"implementationAddress": "0x2222222222222222222222222222222222222222"
|
|
641
|
+
}
|
|
642
|
+
]
|
|
643
|
+
},
|
|
644
|
+
"bscProd": {
|
|
645
|
+
"sourceNetwork": "production",
|
|
646
|
+
"releaseStage": "production",
|
|
647
|
+
"chainId": 56,
|
|
648
|
+
"manifestDir": ".openzeppelin",
|
|
649
|
+
"contracts": [
|
|
650
|
+
{
|
|
651
|
+
"instanceId": "vault",
|
|
652
|
+
"contractInfoKey": "vault:Vault",
|
|
653
|
+
"address": "0x3333333333333333333333333333333333333333",
|
|
654
|
+
"actualContract": "Vault",
|
|
655
|
+
"proxyKind": "uups",
|
|
656
|
+
"implementationAddress": "0x4444444444444444444444444444444444444444"
|
|
657
|
+
}
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
First print the plan-bound confirmation tokens. Review the network, release
|
|
665
|
+
stage, manifest directory, proxy address, implementation address, and chain ID
|
|
666
|
+
before copying each token:
|
|
667
|
+
|
|
668
|
+
```bash
|
|
669
|
+
npm run workflow:import-state -- \
|
|
670
|
+
--plan scripts/migrations/legacy-bsc-state.plan.json \
|
|
671
|
+
--print-confirmations
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Then provide every token independently:
|
|
675
|
+
|
|
676
|
+
```bash
|
|
677
|
+
npm run workflow:import-state -- \
|
|
678
|
+
--plan scripts/migrations/legacy-bsc-state.plan.json \
|
|
679
|
+
--confirm 'migration-v1:legacy-bsc-state:bscDev:development:.openzeppelin.dev:<plan-bound-hash>' \
|
|
680
|
+
--confirm 'migration-v1:legacy-bsc-state:bscProd:production:.openzeppelin:<plan-bound-hash>'
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
The importer verifies target configuration, chain ID, deployed code, artifact,
|
|
684
|
+
ERC-1967 proxy kind and implementation, then runs gate-tool bytecode checking
|
|
685
|
+
before committing any formal state. A zero gate-tool exit is insufficient by
|
|
686
|
+
itself: the bytecode report must exist as a regular file, contain valid JSON,
|
|
687
|
+
and retain the hash recorded in the evidence.
|
|
688
|
+
|
|
689
|
+
Every output is durably staged before a transaction journal is written.
|
|
690
|
+
`contractInfo.json`, `deploymentInfo.json`, and the bytecode report are renamed
|
|
691
|
+
first; immutable `state-import.json` evidence is renamed last as the commit
|
|
692
|
+
marker. Its `integrityHash` binds the complete evidence document. On the next
|
|
693
|
+
run, crash recovery reads the journal and either restores every exact pre-import
|
|
694
|
+
byte stream or confirms the fully committed hashes before deleting staged
|
|
695
|
+
files. Retrying the same completed migration is therefore idempotent, while a
|
|
696
|
+
missing/tampered report, evidence field, symlinked path component, or mixed
|
|
697
|
+
formal state fails closed.
|
|
698
|
+
|
|
699
|
+
This is a signer-free, non-broadcast migration operation. Confirmation printing
|
|
700
|
+
finishes before Hardhat loads; an actual import sets `GATE_MIGRATION_READ_ONLY`
|
|
701
|
+
so Hardhat config exposes no signer accounts. It never reads a private key and
|
|
702
|
+
must not broadcast a transaction to mainnet. Acceptance uses
|
|
703
|
+
compiler/tests, this import audit, `workflow:checkcode`, and dry-run or fork
|
|
704
|
+
simulation for legacy script entrypoints. A real mainnet transaction is never an
|
|
705
|
+
acceptance requirement.
|
|
706
|
+
|
|
707
|
+
## Configuration
|
|
708
|
+
|
|
709
|
+
`hardhat.config.ts` owns RPC route selection, public fallbacks, chain ids, and
|
|
710
|
+
signer setup. The tracked `rpc.env` file provides the shared `BASE_RPC_URI` for
|
|
711
|
+
the project; SmartFlow may populate its initially empty value once when it
|
|
712
|
+
creates a new internal project. `.env` remains untracked and only provides
|
|
713
|
+
`WALLET_CREDENTIAL`/`PRIVATE_KEY`, `CUSTODY_API_BASE_URL`, and the optional full
|
|
714
|
+
`ZKSYNC_RPC_URL`.
|
|
715
|
+
|
|
716
|
+
Each remote network declares `route`, `publicFallback`, and `useBaseRpc`
|
|
717
|
+
explicitly. When `useBaseRpc` is `true` and `BASE_RPC_URI` is non-empty, the
|
|
718
|
+
resolved URL is `<BASE_RPC_URI>/<route>/` with exactly one separator and a
|
|
719
|
+
trailing slash. Otherwise Hardhat uses that network's public fallback. A
|
|
720
|
+
`BASE_RPC_URI` already present in the process environment overrides
|
|
721
|
+
`rpc.env`.
|
|
722
|
+
|
|
723
|
+
The internal Base RPC routes enabled by the template are `eth`, `bsc`,
|
|
724
|
+
`optimism`, `arbitrum`, `base`, `avaxc`, `blast`, `polygon`,
|
|
725
|
+
`world`, `gatelayer`, `eni`, `bera`, `sonic`, and `linea`. Networks without an
|
|
726
|
+
internal route, such as Fantom and Sepolia, explicitly disable Base RPC and
|
|
727
|
+
always use their public fallback.
|
|
728
|
+
|
|
729
|
+
zkSync capture must call `zks_getBlockDetails`, so `zkDev` and `zkProd` do not
|
|
730
|
+
inherit `BASE_RPC_URI`. They use `ZKSYNC_RPC_URL` when set, otherwise the full
|
|
731
|
+
official `https://mainnet.era.zksync.io` endpoint.
|
|
732
|
+
|
|
733
|
+
`config.yaml` owns long-lived business parameters and deployment
|
|
734
|
+
infrastructure addresses. Top-level keys starting with `_` are shared profiles,
|
|
735
|
+
not Hardhat networks; concrete network keys inherit those profiles:
|
|
736
|
+
|
|
737
|
+
```yaml
|
|
738
|
+
_profiles:
|
|
739
|
+
create2Base: &create2Base
|
|
740
|
+
leadingZeroBytes: 4
|
|
741
|
+
caller: "0x0000000000000000000000000000000000000000"
|
|
742
|
+
|
|
743
|
+
create2Dev: &create2Dev
|
|
744
|
+
<<: *create2Base
|
|
745
|
+
factory: "0xYourDevCreate2Factory"
|
|
746
|
+
|
|
747
|
+
create2Prod: &create2Prod
|
|
748
|
+
<<: *create2Base
|
|
749
|
+
factory: "0xYourProdCreate2Factory"
|
|
750
|
+
|
|
751
|
+
zkCreate2Base: &zkCreate2Base
|
|
752
|
+
<<: *create2Base
|
|
753
|
+
scheme: zksync-eravm
|
|
754
|
+
|
|
755
|
+
zkCreate2Dev: &zkCreate2Dev
|
|
756
|
+
<<: *zkCreate2Base
|
|
757
|
+
factory: "0x8Cb318b70b93cEC7E9601Fd3D391Fa28892f6D1e"
|
|
758
|
+
|
|
759
|
+
zkCreate2Prod: &zkCreate2Prod
|
|
760
|
+
<<: *zkCreate2Base
|
|
761
|
+
factory: "0x98fe320B225305cA57F83c322d919D477c9f1641"
|
|
762
|
+
|
|
763
|
+
uups: &defaultUups
|
|
764
|
+
placeholderImplementation: "0xYourBlankUUPSPlaceholderImplementation"
|
|
765
|
+
|
|
766
|
+
zkUups: &zkUups
|
|
767
|
+
placeholderImplementation: "0x00000000258e0D79F9FE344BADA1bb3c14A11dE1"
|
|
768
|
+
|
|
769
|
+
multicall: &defaultMulticall
|
|
770
|
+
controlledMulticall: "0xYourControlledMulticall"
|
|
771
|
+
|
|
772
|
+
zkMulticall: &zkMulticall
|
|
773
|
+
controlledMulticall: "0x00000000ceAA9C77a78aFb970B85DB5917B05908"
|
|
774
|
+
|
|
775
|
+
dev: &devProfile
|
|
776
|
+
environment: development
|
|
777
|
+
admin: ""
|
|
778
|
+
create2: *create2Dev
|
|
779
|
+
uups: *defaultUups
|
|
780
|
+
multicall: *defaultMulticall
|
|
781
|
+
|
|
782
|
+
prod: &prodProfile
|
|
783
|
+
environment: production
|
|
784
|
+
admin: "0xYourSafeOrAdminAddress"
|
|
785
|
+
create2: *create2Prod
|
|
786
|
+
uups: *defaultUups
|
|
787
|
+
multicall: *defaultMulticall
|
|
788
|
+
|
|
789
|
+
zkDev: &zkDevProfile
|
|
790
|
+
environment: development
|
|
791
|
+
admin: ""
|
|
792
|
+
create2: *zkCreate2Dev
|
|
793
|
+
uups: *zkUups
|
|
794
|
+
multicall: *zkMulticall
|
|
795
|
+
|
|
796
|
+
zkProd: &zkProdProfile
|
|
797
|
+
environment: production
|
|
798
|
+
admin: "0xYourSafeOrAdminAddress"
|
|
799
|
+
create2: *zkCreate2Prod
|
|
800
|
+
uups: *zkUups
|
|
801
|
+
multicall: *zkMulticall
|
|
802
|
+
|
|
803
|
+
ethDev:
|
|
804
|
+
<<: *devProfile
|
|
805
|
+
|
|
806
|
+
ethProd:
|
|
807
|
+
<<: *prodProfile
|
|
808
|
+
|
|
809
|
+
zkDev:
|
|
810
|
+
<<: *zkDevProfile
|
|
811
|
+
|
|
812
|
+
zkProd:
|
|
813
|
+
<<: *zkProdProfile
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
Every selectable target has an explicit `environment` inherited from its
|
|
817
|
+
profile: `local`, `development`, or `production`. Consumers should classify a
|
|
818
|
+
parsed target from this field. The workflow does not infer the environment from
|
|
819
|
+
the network name, alias, RPC URL, or chain ID.
|
|
820
|
+
|
|
821
|
+
Every target also declares an explicit `openzeppelin.manifestDir`. Local tooling
|
|
822
|
+
uses `.openzeppelin.local`, development networks use `.openzeppelin.dev`,
|
|
823
|
+
Sepolia uses `.openzeppelin.sepolia`, and production networks use
|
|
824
|
+
`.openzeppelin`. Non-production targets must never fall back to the production
|
|
825
|
+
directory. The CLI exports `MANIFEST_DEFAULT_DIR` before Hardhat loads the
|
|
826
|
+
OpenZeppelin plugin. Multi-network CheckCode starts one isolated sync child per
|
|
827
|
+
target so one target cannot mutate another target's manifest.
|
|
828
|
+
|
|
829
|
+
`admin: ""` or `admin: "deployer"` resolves to the current signer. The workflow
|
|
830
|
+
does not use a default fallback; if `--network ethProd` is selected, `config.yaml`
|
|
831
|
+
must contain an `ethProd` entry.
|
|
832
|
+
|
|
833
|
+
For setup or upgrade calls, final simulation checks the contract's `owner()` or
|
|
834
|
+
`admin()` and controlled-multicall permissions. It records the exact operator or
|
|
835
|
+
external executor per transaction. Real execute enforces every locked
|
|
836
|
+
`to/value/data/operation/executor` tuple, advances one ordered operator segment,
|
|
837
|
+
and appends the following external segment without changing its executor. A
|
|
838
|
+
`view`/`check` step runs only after external reconciliation completes.
|
|
839
|
+
|
|
840
|
+
## Workflow Steps
|
|
841
|
+
|
|
842
|
+
Use standard workflow steps whenever the action can be described declaratively:
|
|
843
|
+
|
|
844
|
+
- `deploy`: deploy a contract and stage its address in a pending registry candidate.
|
|
845
|
+
- `call`: plan and execute state-changing contract calls under per-transaction authority.
|
|
846
|
+
- `view`: read contract state and record the result.
|
|
847
|
+
- `check`: read contract state and fail when it does not match the expected value.
|
|
848
|
+
- `upgrade`: upgrade execution slot for proxy projects.
|
|
849
|
+
|
|
850
|
+
For `call`, `view`, and `check`, `contract` is the ABI or fully qualified
|
|
851
|
+
contract name. If the deployed instance is recorded under a different `saveAs`,
|
|
852
|
+
set `deployment` to that instance ID. The runtime resolves both an exact
|
|
853
|
+
contract key and a unique `<saveAs>:<actualContract>` alias:
|
|
854
|
+
|
|
855
|
+
```yaml
|
|
856
|
+
- id: call-core-vault-cap
|
|
857
|
+
kind: call
|
|
858
|
+
contract: contracts/core/Vault.sol:Vault
|
|
859
|
+
deployment: CoreVault
|
|
860
|
+
calls:
|
|
861
|
+
- method: setCap
|
|
862
|
+
args:
|
|
863
|
+
- $param.cap
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
Every `deploy` step must use `strategy: create2`, define a `saveAs` value, and
|
|
867
|
+
`saveAs` must be unique within the task. It is the deployment instance id used
|
|
868
|
+
by `contractInfo.json` and by network-scoped salts. It must not contain `:` because
|
|
869
|
+
that separator belongs to the gate-tool alias format. If `saveAs` equals the
|
|
870
|
+
actual Solidity contract name, the registry key is just the contract name.
|
|
871
|
+
Otherwise the key is `<saveAs>:<actualContract>`. Fully qualified artifact names
|
|
872
|
+
are reduced to their final Solidity contract name for this key.
|
|
873
|
+
|
|
874
|
+
If two Solidity contracts have the same name, use a fully qualified contract
|
|
875
|
+
name and different `saveAs` values:
|
|
876
|
+
|
|
877
|
+
```yaml
|
|
878
|
+
- id: deploy-core-vault
|
|
879
|
+
kind: deploy
|
|
880
|
+
contract: contracts/core/Vault.sol:Vault
|
|
881
|
+
saveAs: CoreVault
|
|
882
|
+
|
|
883
|
+
- id: deploy-reward-vault
|
|
884
|
+
kind: deploy
|
|
885
|
+
contract: contracts/reward/Vault.sol:Vault
|
|
886
|
+
saveAs: RewardVault
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
Those steps produce `CoreVault:Vault` and `RewardVault:Vault`. Later steps use
|
|
890
|
+
`deployment: CoreVault` or `deployment: RewardVault`; they do not need to repeat
|
|
891
|
+
the suffix.
|
|
892
|
+
|
|
893
|
+
CREATE2 deploys keep salt values in each network's task parameters under
|
|
894
|
+
`params.<network>.salts.<saveAs>`:
|
|
895
|
+
|
|
896
|
+
- A development salt is reused only when the target scheme, factory, caller salt prefix, scheme-specific hashes, and leading-zero policy still produce the recorded expected address. Otherwise SmartFlow mines a target-specific salt.
|
|
897
|
+
- different `saveAs` values must not share one salt value; each contract or deployment instance gets its own salt group.
|
|
898
|
+
- For UUPS contracts, the canonical business address is the proxy. The business task only needs a network-scoped proxy salt. Later business implementation addresses are ordinary upgrade outputs and do not use vanity salts.
|
|
899
|
+
|
|
900
|
+
SmartFlow is the CREATE2 salt source. The developer lane should use the built-in
|
|
901
|
+
CREATE2 resolver/miner, write the requested artifacts to
|
|
902
|
+
`scripts/tasks/<taskId>/create2-requests.json`, and write confirmed results to
|
|
903
|
+
`scripts/tasks/<taskId>/create2-results.json`. Runtime execution must only
|
|
904
|
+
consume the locked salt values already written to `parameters.yaml`; it must not
|
|
905
|
+
mine new salts, call an external salt service, or silently replace a salt during
|
|
906
|
+
simulation or execution.
|
|
907
|
+
|
|
908
|
+
`create2-requests.json` uses the version-2 catalog exclusively. It contains
|
|
909
|
+
exactly `profiles.evm.requests` and `profiles.zksync-eravm.requests`, with an
|
|
910
|
+
explicit `scheme` on every request. Top-level `requests`, profile-level
|
|
911
|
+
`targetNetworks`, the retired `resolver` and `addressParam` fields, mixed
|
|
912
|
+
EVM/zkSync hash fields, and salt paths under `params.global` are invalid.
|
|
913
|
+
SmartFlow binds the selected profile to each actual
|
|
914
|
+
target network at runtime. `create2-results.json` remains version 1 and every
|
|
915
|
+
trusted result records its explicit scheme and target network.
|
|
916
|
+
|
|
917
|
+
For non-upgradeable EVM contracts, SmartFlow mines the salt from the standard
|
|
918
|
+
CREATE2 inputs:
|
|
919
|
+
|
|
920
|
+
```text
|
|
921
|
+
predicted = last20bytes(keccak256(0xff ++ factory ++ salt ++ initCodeHash))
|
|
922
|
+
initCodeHash = keccak256(contract bytecode ++ abi.encode(constructorArgs))
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
`leadingZeroBytes: 4` means the mined address must start with
|
|
926
|
+
`0x00000000`. Under both schemes, `caller` is the required 20-byte salt prefix
|
|
927
|
+
stored in the high 20 bytes of every 32-byte salt; the remaining 12 bytes are
|
|
928
|
+
the mined nonce. It is not the Factory address or the deployment transaction
|
|
929
|
+
sender, and it enters address derivation only through the complete salt. SmartFlow supplies the scheme-specific
|
|
930
|
+
bytecode and constructor-input hashes, while GateTool remains the canonical
|
|
931
|
+
EraVM address and deployment-calldata implementation.
|
|
932
|
+
|
|
933
|
+
```yaml
|
|
934
|
+
- id: deploy-counter
|
|
935
|
+
kind: deploy
|
|
936
|
+
contract: Counter
|
|
937
|
+
saveAs: Counter
|
|
938
|
+
strategy: create2
|
|
939
|
+
create2:
|
|
940
|
+
factory: $target.create2.factory
|
|
941
|
+
salt: $param.salts.Counter.deploySalt
|
|
942
|
+
expectedAddress: $param.salts.Counter.expectedAddress
|
|
943
|
+
leadingZeroBytes: $target.create2.leadingZeroBytes
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
Non-upgradeable CREATE2 deployments default through the configured
|
|
947
|
+
`target.multicall.controlledMulticall`.
|
|
948
|
+
The runtime sends the factory `deploy(...)` call through
|
|
949
|
+
that contract instead of calling the factory directly, and any future
|
|
950
|
+
post-deploy calls can be included in the same batch.
|
|
951
|
+
|
|
952
|
+
Upgradeable deployments should use the pre-deployed blank `UUPSPlaceholder`
|
|
953
|
+
implementation as the initial UUPS implementation. Treat `UUPSPlaceholder` as
|
|
954
|
+
chain-level infrastructure, like the CREATE2 factory. Before generating or
|
|
955
|
+
executing a proxySalt, every target network must declare the exact factory and
|
|
956
|
+
configured placeholder implementation address it will use. The placeholder
|
|
957
|
+
implementation is read from `target.uups.placeholderImplementation`. The proxy
|
|
958
|
+
CREATE2 initCode is `ERC1967Proxy(placeholderImplementation, 0x)`; if the
|
|
959
|
+
factory or placeholder implementation address differs by chain, the same proxy
|
|
960
|
+
salt will not produce the same proxy address.
|
|
961
|
+
|
|
962
|
+
Production rollouts should pre-deploy and verify this `UUPSPlaceholder`
|
|
963
|
+
infrastructure on every target network before the business deployment task.
|
|
964
|
+
The business deployment workflow does not mine or deploy a placeholder
|
|
965
|
+
implementation; it consumes `target.uups.placeholderImplementation` and
|
|
966
|
+
only deploys the CREATE2 proxy.
|
|
967
|
+
|
|
968
|
+
For `upgradeableStrategy: uups-placeholder-proxy`, SmartFlow mines the proxy
|
|
969
|
+
salt from the proxy init code hash:
|
|
970
|
+
|
|
971
|
+
```text
|
|
972
|
+
initCodeHash = keccak256(ERC1967Proxy bytecode ++ abi.encode(placeholderImplementation, 0x))
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
The business implementation bytecode hash is not used for proxy salt mining.
|
|
976
|
+
|
|
977
|
+
The placeholder has no owner, admin, or business state. It only exists so the
|
|
978
|
+
proxy initCode is stable. The deployment workflow must run an atomic first upgrade
|
|
979
|
+
in the same transaction that creates the proxy. If the first upgrade
|
|
980
|
+
fails, the whole transaction reverts and no placeholder proxy is left behind.
|
|
981
|
+
|
|
982
|
+
The real business implementation owns its own `initialize(...)` parameters,
|
|
983
|
+
owner/admin setup, and future `_authorizeUpgrade` policy. Add a final ownership handoff
|
|
984
|
+
step after deployment-time setup to transfer control to `$target.admin`
|
|
985
|
+
or the production Safe.
|
|
986
|
+
|
|
987
|
+
`UUPSPlaceholder` does not touch OpenZeppelin Initializable state. The real
|
|
988
|
+
business implementation runs its own `initialize(...)` during the atomic first
|
|
989
|
+
`upgradeToAndCall` call.
|
|
990
|
+
|
|
991
|
+
Use `upgradeableStrategy: uups-placeholder-proxy` in task metadata/docs. The
|
|
992
|
+
`saveAs` value identifies the business proxy. The `firstUpgrade.contract` value
|
|
993
|
+
is the actual business contract used for the `contractInfo.json` key; the
|
|
994
|
+
placeholder contract is never used as that alias suffix. Implementation and
|
|
995
|
+
placeholder addresses are retained only in the per-execution deployment record.
|
|
996
|
+
|
|
997
|
+
```yaml
|
|
998
|
+
- id: deploy-counter-placeholder-proxy
|
|
999
|
+
kind: deploy
|
|
1000
|
+
contract: CounterUUPS
|
|
1001
|
+
saveAs: Counter
|
|
1002
|
+
strategy: create2
|
|
1003
|
+
upgradeableStrategy: uups-placeholder-proxy
|
|
1004
|
+
proxy:
|
|
1005
|
+
kind: uups
|
|
1006
|
+
initializer: false
|
|
1007
|
+
create2:
|
|
1008
|
+
factory: $target.create2.factory
|
|
1009
|
+
salt: $param.salts.Counter.proxySalt
|
|
1010
|
+
leadingZeroBytes: $target.create2.leadingZeroBytes
|
|
1011
|
+
expectedAddress: $param.salts.Counter.proxyAddress
|
|
1012
|
+
firstUpgrade:
|
|
1013
|
+
contract: CounterUUPS
|
|
1014
|
+
call:
|
|
1015
|
+
method: initialize
|
|
1016
|
+
args:
|
|
1017
|
+
- $target.admin
|
|
1018
|
+
- $param.initialValue
|
|
1019
|
+
|
|
1020
|
+
- id: call-transfer-counter-owner
|
|
1021
|
+
kind: call
|
|
1022
|
+
contract: CounterUUPS
|
|
1023
|
+
deployment: Counter
|
|
1024
|
+
calls:
|
|
1025
|
+
- method: transferOwnership
|
|
1026
|
+
args:
|
|
1027
|
+
- $target.admin
|
|
1028
|
+
```
|
|
1029
|
+
|
|
1030
|
+
Task parameters should group salts by deployment instance id:
|
|
1031
|
+
|
|
1032
|
+
```yaml
|
|
1033
|
+
params:
|
|
1034
|
+
gtDev:
|
|
1035
|
+
salts:
|
|
1036
|
+
Counter:
|
|
1037
|
+
proxySalt: "0x..."
|
|
1038
|
+
proxyAddress: "0x00000000..."
|
|
1039
|
+
CoreVault:
|
|
1040
|
+
deploySalt: "0x..."
|
|
1041
|
+
RewardVault:
|
|
1042
|
+
deploySalt: "0x..."
|
|
1043
|
+
gtProd:
|
|
1044
|
+
salts:
|
|
1045
|
+
Counter:
|
|
1046
|
+
proxySalt: "0x..."
|
|
1047
|
+
proxyAddress: "0x00000000..."
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
Real and simulated proxy upgrades call the in-process GateTool workflow first.
|
|
1051
|
+
`prepareWorkflow(hre, request)` compiles, performs the scheme-appropriate
|
|
1052
|
+
OpenZeppelin implementation and storage-layout validation, and returns a
|
|
1053
|
+
prepare-only plan containing the implementation deployment and canonical
|
|
1054
|
+
`upgradeToAndCall` transaction. It never signs, broadcasts, or mutates a
|
|
1055
|
+
manifest. The workflow then applies its own authority policy, locks the exact
|
|
1056
|
+
transactions, and sends only in execute mode. On zkSync, bytecode-publishing
|
|
1057
|
+
transactions also lock and send the exact normalized `factoryDeps` through the
|
|
1058
|
+
local `zksync-ethers` wallet.
|
|
1059
|
+
|
|
1060
|
+
After a non-simulation UUPS receipt is finalized, the workflow runs GateTool
|
|
1061
|
+
sync against the target's configured `openzeppelin.manifestDir` and records the
|
|
1062
|
+
committed transaction as `gateToolSync` evidence. A failed or missing sync keeps
|
|
1063
|
+
the deployment or upgrade incomplete; prepare mode itself never writes the
|
|
1064
|
+
manifest.
|
|
1065
|
+
|
|
1066
|
+
EVM remains the default when `create2.scheme` is absent. zkSync uses the target
|
|
1067
|
+
block's protocol with the bundled runner capability, while canonical evidence
|
|
1068
|
+
retains the captured source protocol version and the capability-resolved runner
|
|
1069
|
+
protocol version; a verified compatibility mapping may make them differ.
|
|
1070
|
+
The workflow does not copy a protocol matrix: support comes only from the
|
|
1071
|
+
installed capability. An undeclared protocol or runner failure requires upgrading
|
|
1072
|
+
the runner or workflow dependency and starting a new simulation. For an approved
|
|
1073
|
+
protocol, the local private-key or custody operator sends the immutable
|
|
1074
|
+
`customData.factoryDeps`; external Admin routes still fail closed until an exact
|
|
1075
|
+
EIP-712 handoff transport exists. An EVM Hardhat fork must not be used as an EraVM simulation. zkSync CheckCode is supported
|
|
1076
|
+
through the scheme-aware target compiler and GateTool evidence path, including
|
|
1077
|
+
the finalized UUPS deployment record and immutable manifest-sync evidence.
|
|
1078
|
+
|
|
1079
|
+
```yaml
|
|
1080
|
+
- id: upgrade-counter
|
|
1081
|
+
kind: upgrade
|
|
1082
|
+
proxy: Counter
|
|
1083
|
+
old: CounterUUPS
|
|
1084
|
+
contract: CounterUUPSV2
|
|
1085
|
+
proxyKind: uups
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
`custom` is the special-case escape hatch for logic that cannot be represented
|
|
1089
|
+
by standard steps:
|
|
1090
|
+
|
|
1091
|
+
```yaml
|
|
1092
|
+
- id: custom-init
|
|
1093
|
+
kind: custom
|
|
1094
|
+
script: scripts/tasks/20260611-counter/scripts/02_custom_init.ts
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
Deployer review should treat `kind: custom` as arbitrary project code. Custom
|
|
1098
|
+
scripts should still reuse workflow helpers for deployment records, authority
|
|
1099
|
+
checks, and reports. Admin release plans reject a custom step after the external
|
|
1100
|
+
suffix begins.
|
|
1101
|
+
|
|
1102
|
+
## Files
|
|
1103
|
+
|
|
1104
|
+
- `config.yaml`: long-lived business config by Hardhat network key.
|
|
1105
|
+
- `contractInfo.json`: current business-facing contract or proxy addresses by network key.
|
|
1106
|
+
- `deploymentInfo.json`: proxy kind, implementation, source record, and other deployment metadata.
|
|
1107
|
+
- `gate-workflow run`: packaged workflow entrypoint used by deployers.
|
|
1108
|
+
- `gate-evm-tools/runtime/steps/*`: packaged standard step executors for deploy, call, view, check, upgrade, and custom.
|
|
1109
|
+
- `gate-evm-tools/runtime/lib/*`: packaged shared workflow runtime helpers for config, parameters, CREATE2, calldata, authority, reporting, and deployment records.
|
|
1110
|
+
- `scripts/tasks/<taskId>/manifest.yaml`: task workflow execution plan.
|
|
1111
|
+
- `scripts/tasks/<taskId>/parameters.yaml`: per-network task parameters and explicitly shared business parameters.
|
|
1112
|
+
- `scripts/tasks/<taskId>/release.json`: source SuperTask, development network, and release-qualified network fingerprints.
|
|
1113
|
+
- `scripts/tasks/<taskId>/create2-requests.json`: SmartFlow built-in CREATE2 resolver requests for deploy steps.
|
|
1114
|
+
- `scripts/tasks/<taskId>/create2-results.json`: resolved CREATE2 salts, expected addresses, and resolver metadata.
|
|
1115
|
+
- `scripts/tasks/<taskId>/scripts`: optional SmartFlow-generated custom scripts.
|
|
1116
|
+
- `scripts/tasks/<taskId>/results/<target>/<executionId>/<mode>/<result>.json`: single-use execution, view, and verification envelopes for one workflow mode.
|
|
1117
|
+
- `scripts/tasks/<taskId>/results/<network>.<executionId>.deployment.json`: deploy and upgrade evidence for one execute run.
|
|
1118
|
+
- `scripts/tasks/<taskId>/results/<network>/<executionId>/execution-plan.json`: immutable per-transaction authority plan.
|
|
1119
|
+
- `scripts/tasks/<taskId>/calldata/<network>/<executionId>/external-execution.json`: canonical cumulative external segment package.
|
|
1120
|
+
- `scripts/tasks/<taskId>/results/<network>/<executionId>/external-reconciliation.json`: finalized chain reconciliation evidence.
|
|
1121
|
+
- `scripts/tasks/<taskId>/results/registry/<commitId>.json`: all-network registry commit evidence.
|
|
1122
|
+
- `contractInfo.pending.<executionId>.json`: stable v1 evidence locator; read its JSON `status` (`pending` or `promoted`) for the lifecycle state.
|
|
1123
|
+
- `scripts/checkcode/runs/<runId>`: effective registry, raw gate-tool report, sync evidence, and final CheckCode report.
|
|
1124
|
+
|
|
1125
|
+
`contractInfo.json` uses the same string-only shape as gate-tool. Values are
|
|
1126
|
+
always address strings; nested address or deployment objects are rejected:
|
|
1127
|
+
|
|
1128
|
+
```json
|
|
1129
|
+
{
|
|
1130
|
+
"gtDev": {
|
|
1131
|
+
"Vault": "0xVaultProxy",
|
|
1132
|
+
"USDCVault:Vault": "0xUsdcVaultProxy",
|
|
1133
|
+
"USDTVault:Vault": "0xUsdtVaultProxy"
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
Every execute run containing a deploy or upgrade step writes
|
|
1139
|
+
`scripts/tasks/<taskId>/results/<network>.<executionId>.deployment.json`. The
|
|
1140
|
+
record starts as `prepared`, captures the complete `contractInfo.json` before
|
|
1141
|
+
snapshot, and records each step's instance ID, actual contract, registry key,
|
|
1142
|
+
factory, multicall, salt, expected and actual addresses, implementation,
|
|
1143
|
+
transaction hash, and CREATE2 reuse state. A successful deployment writes an
|
|
1144
|
+
immutable before backup plus `contractInfo.pending.<executionId>.json`, then
|
|
1145
|
+
waits in `waiting_code_check`. It does not change the formal registry.
|
|
1146
|
+
`workflow:commit-contract-info` requires finalized execution and same-execution
|
|
1147
|
+
business verification, promotes all selected candidates in one atomic registry
|
|
1148
|
+
write, updates `deploymentInfo.json`, and records `deployed_pending_code_audit`.
|
|
1149
|
+
CheckCode then audits the formal registry without changing it.
|
|
1150
|
+
Promotion retains the canonical `.pending.` file as terminal evidence because
|
|
1151
|
+
deployment records and recovery reports continue to reference that path. Do not
|
|
1152
|
+
delete, move, or rename it while those evidence references exist.
|
|
1153
|
+
|
|
1154
|
+
Upgrade records include the gate-tool output path and parsed output but do not
|
|
1155
|
+
rewrite `contractInfo.json`, because the business proxy address is unchanged.
|
|
1156
|
+
An external-admin run is marked `waiting_external`; a failed run remains
|
|
1157
|
+
`partial`, preserving prepared changes and the before and planned snapshots for
|
|
1158
|
+
recovery.
|
|
1159
|
+
|
|
1160
|
+
When a normal deployed contract already exists, the administrator must choose
|
|
1161
|
+
`reuse` or `redeploy`. Reuse requires passed CheckCode evidence for the same
|
|
1162
|
+
address and release artifact hash. Redeploy requires a new salt, a different
|
|
1163
|
+
expected address, and a reason; the old registry is backed up at registry commit.
|
|
1164
|
+
ERC1967/UUPS proxies cannot use redeploy and must continue through an upgrade
|
|
1165
|
+
step.
|
|
1166
|
+
|
|
1167
|
+
The runtime serializes `contractInfo.json` read-modify-write operations across
|
|
1168
|
+
workflow processes, so parallel network executions cannot overwrite one
|
|
1169
|
+
another. A concurrent candidate for the same network and deployment instance is
|
|
1170
|
+
rejected. Generated execution IDs include
|
|
1171
|
+
millisecond precision and a random suffix, and an existing deployment-record
|
|
1172
|
+
path is never overwritten. Locks left by a terminated workflow process are
|
|
1173
|
+
detected and recovered before the next registry update.
|
|
1174
|
+
|
|
1175
|
+
### Packaged workflow preview
|
|
1176
|
+
|
|
1177
|
+
Workflow implementations are provided by `gate-evm-tools`; npm scripts invoke the packaged CLI directly; the template contains no workflow forwarding directory. Business scripts, contracts, configuration and task data stay in this project. The dependency allows `^1.0.28`; the lockfile pins the installed version. Before npm publication, validate using its local `.tgz` with `npm install --save-dev --save-exact /path/to/gate-evm-tools-1.0.28.tgz` before running commands. Keep the updated lockfile with package.json.
|
|
1178
|
+
|
|
1179
|
+
`npx --no-install gate-workflow --help` exposes compiled workflow commands without a manual ts-node preload. Existing npm commands remain supported. Before upgrading an existing task, finish it with the original runtime or reconcile it and prepare a new execution ID; execution bindings reject a different package build. `gate-workflow migrate` previews removal of recognized baseline files and shims; `--apply` backs up originals, updates known commands/imports and removes known runtime scripts; customized files or unresolved references require manual review. It does not change business data or install dependencies.
|
|
1180
|
+
|
|
1181
|
+
Network declarations live directly in `hardhat.config.ts` under its single `networks` block. Edit names, chain IDs, RPC routes/fallbacks and zkSync flags there; do not create a second network list. `createNetworkHelpers` from `gate-evm-tools/runtime-config/networks` supplies only account/RPC/fork mechanics. Add `rpcUrl: process.env.BSC_DEV_RPC_URL` to one `remoteNetwork({ ... })` entry to override its RPC, or spread that entry into `{ ...remoteNetwork({ ... }), timeout: 60000 }` for other Hardhat fields. Load rpc.env/.env before creating the helpers. Dev/Prod represent business environments, not testnet/mainnet. Compiler versions/settings remain project-owned; overlay migration merges the network block and keeps the original toolchain. The deprecated `createTemplateNetworks` export remains for older generated projects only and is not called by the current template.
|