Skip to content

Commit 9676f68

Browse files
authored
feat: compact-deployer tool (#86)
* feat(deployer): add compact-deployer tool and CLI deploy command Introduce the `@openzeppelin/compact-deployer` package: a programmatic and CLI deployer for Compact contracts. Covers config loading, artifact and constructor-arg resolution, wallet/keystore handling, network and proof-server providers, and deployment-record bookkeeping. Wire the deployer into `@openzeppelin/compact-cli` via the new `compact-deploy` command (runDeploy), with passphrase prompting and JSON/verbose output modes. * Add root resolutions for the midnight stack and a `coverage` turbo task so the new package can report coverage. Part 1 of 2 splitting the original deployer PR. Examples and the integration-test suite follow in a stacked PR that builds on this one. Refs: #86 * fix(deployer): address CodeRabbit review feedback Harden the deployer tool and CLI against the issues raised in the #86 review. Each fix verified against current code and covered by tests. * cli/logger: route `--json` pino logs to STDERR (fd 2) so STDOUT carries only the single result object. * cli/prompt: write the passphrase prompt + newline to STDERR, and settle the promise on stdin `end`/`error` so non-interactive input (closed/newline-less pipe) can no longer hang the CLI. * config/schema: mark the file/module ref union members `.strict()` so an ambiguous `{ file, module }` is rejected instead of silently dropping a key. * deployments: write the ledger atomically (temp file + rename) so a crash mid-write can't truncate `*.json`. * loaders/artifact: bind compiled assets to `dirname(entry)` rather than a hardcoded `contract/`, fixing the top-level-`index.js` case. * loaders/constructor-meta: allow an empty named-args object for a no-arg constructor (resolves to `[]`); `reorderNamedArgs` still rejects unexpected keys. * loaders/contract-resolve: use `path.isAbsolute` instead of `startsWith('/')` for OS-correct absolute-path detection. * providers/network: drop `mainnet` from the allow-list while the deployer is testnet/preview-only. * providers/proof-server: reject partial-numeric and out-of-range `PROOF_SERVER_PORT` values (full `\d+` match + 1..65535 bounds). * wallet/handler: use `path.dirname`/`basename` instead of manual `/` splitting so cache paths work on Windows. * wallet/keystore: validate shape in `fromJSON` and throw `WalletError` (not a raw `TypeError`) on malformed keystore JSON. Refs: #86 * fix(deployer): unblock preprod wallet sync preprod's dust history is a ~1M-event global stream that every client replays on sync. Deploying any contract there failed before a tx was ever built: the dust wallet OOMed mid-replay, and once that was worked around the sync gate never completed. Both trace to the pre-fix wallet-sdk-dust-wallet@4.0.0 pin (midnightntwrk/midnight-wallet#425). * wallet/handler: set batchUpdates = { size: 5000, timeout: 1, spacing: 4 } on the shared wallet config so the fresh-sync and the cache-restore paths (shielded + dust alike) stream the replay in larger chunks instead of exhausting the V8 heap at the SDK default batch size of 10. * deployer: replace the strict FacadeState.isSynced tip gate with isCompleteWithin(50) on each sub-wallet. On a live chain the dust stream advances continuously, so strict completion never fires and the gate timed out on a fully usable wallet. The gate still waits on all three sub-wallets to avoid the custom-error-170 regression. * cli + README: document the NODE_OPTIONS max-old-space-size bump still useful for a first preprod sync, plus the tolerant tip gate. Verified on preprod: ShieldedFungibleToken dry-run synced to tip in ~1m12s from a warm cache (dust ~1.08M events, no OOM) and the gate completed even though dust never reached strict completion. Closes #115 * feat(deployer): make sync batch size configurable The dust/shielded sync batch size was hard-coded to 5000 (the #115 OOM workaround). Expose it as a knob so operators can trade memory against replay speed without recompiling, while keeping 5000 as the default. * wallet/handler: add `syncBatchSize` to WalletHandlerBuildOptions; fall back to DEFAULT_SYNC_BATCH_SIZE (5000). timeout/spacing stay at the validated values. * deployer: thread `syncBatchSize` from DeployerOptions through to the owned-wallet build (ignored when a wallet is injected). * cli: add `--sync-batch-size <n>` (positive-integer validated, mirrors --sync-timeout) and document it in --help and the README. Refs: #115 * feat(deployer): allow sync knobs in compact.toml The sync batch size and sync timeout were CLI/programmatic only, so an operator had to pass `--sync-batch-size` / `--sync-timeout` on every run for a long-history network like preprod. Let them live in config too. * config/schema: add optional `sync_timeout` (seconds) and `sync_batch_size` to `[networks.X]`, both positive integers. * deployer: resolve each with precedence CLI/programmatic option > TOML network value > built-in default (10 min / 5000). Applies only to the owned-wallet build; ignored when a wallet is injected. * README: document the keys, the precedence, and a preprod example. Refs: #115 * docs(deployer): document npx + local-install for the CLI Add an "Install & run" section: the `compact-deploy` bin ships in @openzeppelin/compact-cli, and `npx compact-deploy` resolves the local install so the deployer and the compiled contracts share one compact-runtime copy (required for a real deploy). Note that fully ephemeral `npx @openzeppelin/compact-cli …` is fine for help/dry-run but not for real deploys, since its cache-tree runtime differs from the project's and the submit fails the ContractMaintenanceAuthority check. Refs: #115 * fix(deployer): apply review fixes and drop the barrel Secret handling, dead dependency pruning, keystore hardening, and the --version flag, from the review of PR #86. Replaces src/index.ts with explicit package.json subpath exports. Each module is now reached by its own path, so the noBarrelFile suppression is gone and consumers no longer pull the whole package to import one symbol. Internals stay unexported. * refactor(deployer): split services out and cache the unshielded wallet Extracts five single-responsibility modules under src/services/: file-lock, atomic-json, wallet-sync, wallet-cache, and deploy-tx. They stay internal, with no package.json subpath, so the public surface is unchanged. deployments.ts, deployer.ts, and wallet/handler.ts shed the locking, syncing, persistence, and tx-submission code they had absorbed. Caches the unshielded sub-wallet alongside shielded and dust, with a matching --seed-cache-from-unshielded flag. The sync gate already waited on unshielded progress, so an uncached one re-synced from genesis and dominated every warm boot. Narrows two swallowed errors in file-lock. A stale-break fault that is not ENOENT now surfaces instead of retrying into a misleading lock timeout, and a failed release warns rather than vanishing. Release still never throws: it runs in the caller's finally, where throwing would mask a real error or fail an already-successful deploy. Coverage thresholds move to 100 in all four metrics, which the package now meets. * fix(deployer): address CodeRabbit review on PR 86 Catch a rejected onCheckpoint in syncAndVerifyFunds. The callback ran as `onCheckpoint().finally(...)`, so a rejection was never consumed and Node would terminate the process, contradicting the documented best-effort contract of the surrounding comment. Reject a keystore dklen other than 32 in encrypt. The value reached scryptSync directly, and a shorter derived key left an empty MAC key, so encrypt would write a keystore that its own schema then refuses to read back. Fix the default_network test fixture. It appended a second [profile] table, which smol-toml rejects as a redefinition, so the test passed on the TOML parse error and never reached the validation branch it names. Covering that branch properly left the invalid-TOML path untested, so it gets its own case. Document compact-deploy in the CLI README, which listed only the compiler and builder binaries while requiring Node 24 for a deployer it never mentioned. Correct the deployer README's real-network guidance, which pointed at preview while its own known-issues section records preview as null-routed. * build(deployer): move to the ledger-v8 8.1 stack ledger-v8 8.1.2, midnight-js and testkit-js 4.1.1, wallet-sdk-facade 4.0.1 with dust-wallet 4.1.0, shielded 3.0.1, unshielded 3.1.0 and address-format 3.1.2. compact-runtime stays on 0.16.0. compact-js stays on 2.5.1: 2.5.3 depends on an unpublished ledger-v9 alpha. The five wallet-sdk root resolutions are load-bearing. testkit-js pulls wallet-sdk 1.1.0, whose ^4.0.1 facade range resolves to 4.1.0, and that version imports a nonexistent Clock export and fails to load. No resolution is set for midnight-js-protocol: the 4.1.1 packages pin it exactly, and a global pin would downgrade the simulator's 5.0.0-beta.7 tree. * fix(deployer): keep state under the config dir The wallet-state cache and the LevelDB private-state store resolved against process.cwd(), so a deploy run from any other directory re-synced from genesis and started from an empty private state. Both now hang off the compact.toml directory. rootDir is a required option on WalletHandler, WalletCache and buildProviders so a silent CWD default cannot come back. --seed-cache-from-* paths stay CWD-relative because the user typed them at the shell. Wallet-SDK rejections are effect-style tagged records, not Errors, so the warn sites logged "[object Object]". formatError renders _tag and message instead. README: record the 2026-09-04 preprod dry run on this stack (synced to tip, the old deserialize failure did not reproduce) and why the wallet-sdk resolutions must survive dependency bumps. * fix(deployer): format tagged SDK errors on exit paths formatError was wired into the warn-and-continue sites only. A tagged wallet-SDK rejection escaping the sync or the deploy call still hit `(e as Error).message` and printed "[DEPLOY] undefined". The CLI catch, the deploy wrapper and the proof-server dispose now go through it, so the module gains a package export for the CLI. The fallbacks also lose less: an Error appends its cause chain, and util.inspect replaces the hand-rolled JSON serializer, rendering bigint and marking circular references instead of degrading to "[object Object]". * refactor(deployer): drop SDK casts, dedupe rootDir doc The restore casts in wallet-cache were redundant: the wallet-SDK factory and DustWallet.restore already return types assignable to ShieldedWalletAPI and DustWalletAPI. describeProgress cast its input to a loose shape; it now takes a union of the two sub-wallet progress shapes, mirrored structurally because neither SDK type reaches a package root the deployer depends on. Test fixtures gained the counters the real SDK always populates, so the `?? 0n` props are gone. The rootDir explanation lived on three option types; it now lives once on CompactConfig.rootDir with one-line pointers elsewhere. * docs(deployer): state the LevelDB lock scope Both the wallet cache and the LevelDB store now hang off the compact.toml directory, so the project is the unit of state and a second concurrent compact-deploy in the same project fails on the LevelDB lock. * build(deployer): drop unused axios and testcontainers Neither is imported anywhere under packages/deployer/src. The only container use goes through @midnight-ntwrk/testkit-js, which brings its own testcontainers 12; the direct ^10 pin added a second copy with a second dockerode major. * ci(release): wire compact-deployer into the release flow compact-cli depends on @openzeppelin/compact-deployer via workspace:^, which yarn rewrites to a concrete version at pack time, but neither release workflow knew the package, so a cli release after merge would point at an npm version nothing can publish. The deployer joins the workflow choice lists, gets its repository field, and moves ahead of the cli in the first-release order. RELEASING.md requires a CHANGELOG.md per published package. Both new files start with an Unreleased section; the cli entry records the Node 24 floor as Breaking, since compact-deploy relies on explicit resource management (await using, AsyncDisposableStack) that Node 22 lacks and the bump lands on compact-compiler and compact-builder users too. The changelogs are added to the files arrays so they ship in the tarballs. * docs(deployer): state the supported Midnight stack The deployer pins compact-runtime 0.16.0 and ledger-v8, and needs artifacts from compactc 0.31.1; the default 0.34 compiler targets runtime 0.19.0 and the deploy fails with a version mismatch. The README now says so up front, notes that compact-contracts main (runtime 0.19.0, ledger-v9) is not deployable with this tool yet, and names the blocker: compact-js 2.5.3 depends on an unpublished ledger-v9 alpha. The root README gains the deployer package and corrects the Node requirements per package. * docs(deployer): library callers must inject the private-state provider The default LevelDB store is opened per deploy and holds its lock until the process exits, so a script that deploys several contracts in one process needs privateStateProvider next to walletProvider. * chore: gitignore compact-deploy verbose logs * fix(deployer): resolve --seed-file against the CWD --config and --seed-cache-from-* already resolve against the shell CWD; --seed-file alone resolved against the compact.toml directory, so a relative path typed from a subdirectory read the wrong file. A relative [wallet].keystore keeps the rootDir anchor because it comes from the config file, not the shell. * fix(deployer): reject scrypt params the KDF itself refuses The schema bounded n and r separately, but scryptSync also enforces a memory budget and an n < 2^(16r) ceiling, so a keystore with n=2^20 r=8 passed validation and then surfaced as a raw OpenSSL RangeError. One predicate now models both limits and runs in the schema and in encrypt. fromJSON also lost its hand-written pre-checks: they restated what the zod schema already enforces, and the schema error names the field. * fix(deployer): type the wallet sync timeout as WalletError A bare Error exited 1; errors.ts promises a stable code per failure mode, and a sync timeout is a wallet failure (exit 3). * fix(deployer): break stale locks without a double acquire Two waiters that both saw a stale lock could each unlink it: the first re-created the lock, the second removed that fresh file and acquired alongside the holder. The break now renames the lock aside (atomic, one winner), re-checks the parked file's age, and restores it if a waiter had already taken over. The lock timeout is a DeployError instead of a bare Error. * refactor(deployer): drop the unreachable mainnet fee branch applyNetwork rejects mainnet, so the additionalFeeOverhead fallback to the testkit default could never run. * fix(deployer): load args and init state before the wallet sync Both depend only on the config and the artifact, and a typo in either used to surface after a 30-60 min first preprod sync. * fix(deployer): format tagged SDK rejections in runDeploy The library entrypoint still rendered a wallet-SDK tagged record as "[object Object]"; 298def0 fixed the CLI arm only. * fix(deployer): record the deploy before waiting for finalization deployContract fuses submission and the wait for finalization and yields no identifier until the tx has landed, so a WebSocket drop or indexer stall left the CLI spinning with nothing to reconcile, and any failure in that window reported a deploy that may already be on chain as failed. The three SDK steps now run explicitly: createUnprovenDeployTx, submitTxAsync, then watchForTxData raced against --tx-timeout (default 600 s), with the private-state and signing-key writes only after a SucceedEntirely status. The deployments ledger gains a pending state. A record with the address and txId is written as soon as the node accepts the tx and promoted to confirmed on finalization. A timeout or rejection leaves it pending and names both identifiers in the error; the next deploy of that contract refuses until --force, and the check runs before proving so a blocked deploy costs no fees. Every ledger write is logged at info first and a failing write (lock timeout, EACCES, malformed <network>.json) rethrows as DeploymentsFileError, exit 6, carrying address, txId and txHash. readJson wraps a SyntaxError with the file path instead of leaking it. IndexerUnreachableError and ProofServerUnreachableError had no call site and are gone; exit code 4 is unused. * test(deployer): cover the untagged-record message fallback * build(cli): drop the ws WebSocket shim Node 24 ships a native global WebSocket and midnight-js reads that global rather than importing ws. Verified on a local node: with the native class pinned, sync, deploy and watchForTxData all completed; with WebSocket forced to undefined the sync never connected. The shim and the ws / @types/ws dependencies go; ws stays in the lockfile only as a transitive dependency of the indexer provider and testkit-js. * fix(deployer): silence the testkit-js module logger testkit-js builds a pino logger at import and pretty-prints through it to stdout, ignoring the logger passed into every call, so `compact-deploy --json | jq` failed on an "Initializing wallet builder" line that landed before the result object. The logger is exported; setting its level to silent at module load stops the line. The empty `<cwd>/logs/tests/` file the same logger creates on import cannot be prevented from user code and needs an upstream change. * fix(deployer): surface the cause behind Effect failures With the proof server down the CLI printed only "Failed to prove transaction". The wallet SDK does attach the connection error as the cause, but Effect.runPromise rejects with a FiberFailure that copies the message and leaves `cause` unset; the real failure hangs off the registered symbol effect/Runtime/FiberFailure/Cause. formatError now unwraps that symbol (no runtime dependency on effect) and collapses a wrapper whose cause repeats its message, so the line reads "Failed to prove transaction: Failed to connect to Proof Server: Transport error (POST http://127.0.0.1:6300/prove): fetch failed".
1 parent fc2a152 commit 9676f68

83 files changed

Lines changed: 18092 additions & 735 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release-publish.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ on:
1818
options:
1919
- compact-builder
2020
- compact-cli
21+
- compact-deployer
2122
- compact-simulator
2223

2324
jobs:
@@ -67,6 +68,7 @@ jobs:
6768
case "$PKG" in
6869
compact-builder) DIR="builder" ;;
6970
compact-cli) DIR="cli" ;;
71+
compact-deployer) DIR="deployer" ;;
7072
compact-simulator) DIR="simulator" ;;
7173
*)
7274
echo "::error::unknown package: $PKG"
@@ -85,6 +87,7 @@ jobs:
8587
case "$DIR" in
8688
builder) PKG="compact-builder" ;;
8789
cli) PKG="compact-cli" ;;
90+
deployer) PKG="compact-deployer" ;;
8891
simulator) PKG="compact-simulator" ;;
8992
*)
9093
echo "::error::unknown package directory: $DIR"

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
options:
1111
- compact-builder
1212
- compact-cli
13+
- compact-deployer
1314
- compact-simulator
1415
version_bump:
1516
description: "Version bump type (pre* strategies are beta-only)"
@@ -85,6 +86,9 @@ jobs:
8586
"compact-cli")
8687
echo "dir=cli" >> $GITHUB_OUTPUT
8788
;;
89+
"compact-deployer")
90+
echo "dir=deployer" >> $GITHUB_OUTPUT
91+
;;
8892
"compact-simulator")
8993
echo "dir=simulator" >> $GITHUB_OUTPUT
9094
;;

.gitignore

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
*.local
66
package-lock.json
77
.pnp.*
8+
**/.pnp.*
89
.yarn/*
910
!.yarn/patches
1011
!.yarn/plugins
1112
!.yarn/releases
1213
!.yarn/sdks
1314
!.yarn/versions
15+
# Nested yarn state (e.g. `examples/<name>/.yarn/cache`) shouldn't ship.
16+
# Examples install standalone; their cache regenerates from yarn.lock.
17+
**/.yarn/cache
18+
**/.yarn/install-state.gz
19+
**/.yarn/build-state.yml
20+
**/.yarn/unplugged
1421

1522
logs
1623
log
@@ -22,10 +29,38 @@ result
2229
dist/
2330
gen/
2431
managed/
32+
# Compiler output. Regenerated by `compact-compiler` on every build, so it
33+
# never gets committed — including under examples/, where the walkthrough
34+
# expects you to compile the contract yourself before deploying.
2535
artifacts/
2636
midnight-level-db
2737
compactc
2838

39+
# Deploy secrets — wallet seeds, signing keys, keystores. Match at any depth
40+
# so nested deploy/ directories (e.g. under examples/) are covered too.
41+
**/deploy/*.seed
42+
**/deploy/*.signingkey
43+
**/deploy/*.keystore.json
44+
45+
# Deployment records — the JSON the deployer writes after a successful
46+
# deploy. Includes the contract signing key, so treat as a secret.
47+
deployments/
48+
49+
# compact-deployer wallet-state cache (per-seed, per-network shielded snapshots).
50+
.states/
51+
**/.states/
52+
53+
# compact-deployer run logs, written by `compact-deploy --verbose`.
54+
.compact/
55+
**/.compact/
56+
57+
# Third-party source pulled in for local experimentation (e.g. the
58+
# midnight-node fork validation under vendor/midnight-node/ — see
59+
# plans/tooling/compact-deploy-rust-fork.md). Never committed.
60+
vendor/
61+
target/
62+
.toolkit-cache/
63+
2964
coverage
3065
**/reports
3166

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ This project extends the Midnight Network with additional developer tooling.
1515
> risk. See [SECURITY.md](./SECURITY.md) for the full disclaimer and for how
1616
> to report a vulnerability.
1717
18-
Tools for compiling, building, and testing Compact smart contracts. This is a monorepo containing:
18+
Tools for compiling, building, testing, and deploying Compact smart contracts. This is a monorepo containing:
1919

2020
- [`packages/builder`](./packages/builder) — programmatic library that drives the Compact compiler + builder
21-
- [`packages/cli`](./packages/cli) — thin bin wrapper around the builder library (`compact-compiler`, `compact-builder`)
21+
- [`packages/cli`](./packages/cli) — thin bin wrapper around the builder and deployer libraries (`compact-compiler`, `compact-builder`, `compact-deploy`)
22+
- [`packages/deployer`](./packages/deployer) — deployer library that submits a compiled contract to a Midnight network. Pins one Midnight stack: see [Supported stack](./packages/deployer/README.md#supported-stack)
2223
- [`packages/simulator`](./packages/simulator) — TypeScript simulator to run and test Compact contracts locally
2324

2425
See each package's README for usage, options, and examples.
@@ -36,14 +37,19 @@ yarn add --dev @openzeppelin/compact-cli
3637

3738
# Simulator — test Compact contracts locally
3839
yarn add --dev @openzeppelin/compact-simulator
40+
41+
# Deployer — deploy a compiled contract from TypeScript
42+
yarn add --dev @openzeppelin/compact-deployer
3943
```
4044

41-
`compact-cli` depends transitively on `compact-builder`, so installing the CLI
42-
gives you both the binaries and the underlying library.
45+
`compact-cli` depends transitively on `compact-builder` and
46+
`compact-deployer`, so installing the CLI gives you both the binaries and the
47+
underlying libraries.
4348

4449
```bash
4550
yarn compact-compiler --help
4651
yarn compact-builder --help
52+
yarn compact-deploy --help
4753
```
4854

4955
```ts
@@ -53,7 +59,7 @@ import { createSimulator } from '@openzeppelin/compact-simulator';
5359

5460
## Requirements
5561

56-
- Node.js >= 20 (root and `packages/cli`), >= 22 for `packages/simulator`
62+
- Node.js >= 22 for `packages/builder` and `packages/simulator`, >= 24 for `packages/cli` and `packages/deployer`
5763
- Yarn 4 (Berry)
5864
- Turbo
5965
- Optional: Midnight Compact toolchain installed and available in `PATH`

RELEASING.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,26 @@ breaking change. After that, only `prerelease` moves the counter. Running
8080

8181
## First-release order
8282

83-
There's a one-step dependency chain across the three published packages:
83+
There's a one-step dependency chain across the four published packages:
8484

8585
```text
8686
compact-cli (bin wrapper)
87-
└─ depends on compact-builder
87+
├─ depends on compact-builder
88+
└─ depends on compact-deployer
8889
compact-builder (library)
90+
compact-deployer (library)
8991
compact-simulator (library)
9092
```
9193

92-
The `workspace:^` dep is rewritten by yarn into the resolved version at
94+
The `workspace:^` deps are rewritten by yarn into the resolved versions at
9395
`yarn pack` time. For the very first release, publish in dependency order so
9496
each dependent finds its deps already on npm:
9597

9698
1. `compact-builder` (no internal deps)
9799
2. `compact-simulator` (no internal deps)
98-
3. `compact-cli` (depends on `compact-builder`; pull `main` first so the bump
99-
commit is present locally before triggering)
100+
3. `compact-deployer` (no internal deps)
101+
4. `compact-cli` (depends on `compact-builder` and `compact-deployer`; pull
102+
`main` first so both bump commits are present locally before triggering)
100103

101-
After the first release, the three packages version independently — bump any
104+
After the first release, the four packages version independently — bump any
102105
one of them in isolation without re-publishing the others.

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"scripts": {
1010
"build": "turbo run build --log-prefix=none",
1111
"test": "turbo run test --log-prefix=none",
12+
"coverage": "turbo run coverage --log-prefix=none",
1213
"lint": "biome check .",
1314
"lint:fix": "biome check . --write",
1415
"lint:ci": "biome ci . --no-errors-on-unmatched",
@@ -17,10 +18,24 @@
1718
},
1819
"devDependencies": {
1920
"@biomejs/biome": "2.5.6",
21+
"@openzeppelin/compact-deployer": "workspace:^",
2022
"@types/node": "26.1.2",
23+
"@vitest/coverage-v8": "4.1.9",
24+
"pino": "^9.7.0",
2125
"ts-node": "^10.9.2",
2226
"turbo": "^2.10.8",
2327
"typescript": "^6.0.3",
2428
"vitest": "^4.1.9"
29+
},
30+
"resolutions": {
31+
"@midnight-ntwrk/ledger-v8": "8.1.2",
32+
"@midnight-ntwrk/wallet-sdk-address-format": "3.1.2",
33+
"@midnight-ntwrk/wallet-sdk-dust-wallet": "4.1.0",
34+
"@midnight-ntwrk/wallet-sdk-facade": "4.0.1",
35+
"@midnight-ntwrk/wallet-sdk-shielded": "3.0.1",
36+
"@midnight-ntwrk/wallet-sdk-unshielded-wallet": "3.1.0",
37+
"undici": "^6.24.0",
38+
"glob": "^11.0.0",
39+
"uuid": "^13.0.0"
2540
}
2641
}

packages/cli/CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Changelog
2+
3+
All notable changes to `@openzeppelin/compact-cli` are documented in this
4+
file.
5+
6+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7+
and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8+
Releases before this file see the `compact-cli/v*` tags.
9+
10+
## Unreleased
11+
12+
### Added
13+
14+
- `compact-deploy` bin, a wrapper around [`@openzeppelin/compact-deployer`](../deployer) that deploys a compiled contract to a Midnight network (#86)
15+
16+
### Changed
17+
18+
- **Breaking:** compact-cli now requires Node 24. compact-deploy uses explicit resource management (`await using`, `AsyncDisposableStack`), unavailable on Node 22. (#86)

packages/cli/README.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# @openzeppelin/compact-cli
22

3-
CLI wrapper around [`@openzeppelin/compact-builder`](../builder).
4-
Provides the `compact-compiler` and `compact-builder` binaries for use in
5-
`package.json` scripts. Contains no programmatic API of its own. If you want
6-
to call the compiler/builder from TypeScript, use the library package directly.
3+
CLI wrapper around [`@openzeppelin/compact-builder`](../builder) and
4+
[`@openzeppelin/compact-deployer`](../deployer). Provides the
5+
`compact-compiler`, `compact-builder`, and `compact-deploy` binaries for use
6+
in `package.json` scripts. Contains no programmatic API of its own. If you
7+
want to call the compiler, builder, or deployer from TypeScript, use the
8+
library package directly.
79

810
## Install
911

@@ -16,6 +18,7 @@ yarn add --dev @openzeppelin/compact-cli
1618
```bash
1719
yarn compact-compiler --help
1820
yarn compact-builder --help
21+
yarn compact-deploy --help
1922
```
2023

2124
Typical `package.json` scripts (replace `<version>` with the Compact
@@ -34,9 +37,20 @@ toolchain release you want to pin, e.g. `+0.29.0`):
3437

3538
## Options
3639

37-
Both binaries accept the same compiler-side options (forwarded to the
38-
underlying library); `compact-builder` additionally accepts dist-layout
39-
options:
40+
### `compact-deploy`
41+
42+
Deploys a compiled contract to a Midnight network. Options are documented
43+
in full under [`@openzeppelin/compact-deployer`](../deployer); the common
44+
ones are `--network`, `--config`, `--seed-file`, `--dry-run`, and `--json`.
45+
46+
```bash
47+
compact-deploy <Contract> --network local
48+
```
49+
50+
### `compact-compiler` and `compact-builder`
51+
52+
Both accept the same compiler-side options (forwarded to the underlying
53+
library); `compact-builder` additionally accepts dist-layout options:
4054

4155
| Flag | Applies to | Description |
4256
|---|---|---|
@@ -55,7 +69,7 @@ documentation, programmatic API, and behavioural details.
5569

5670
## Requirements
5771

58-
- Node.js >= 20
72+
- Node.js >= 24 for `compact-deploy`, which uses explicit resource management (`await using` / `AsyncDisposableStack`), global only from Node 24. The compiler and builder binaries run on older releases.
5973
- Midnight Compact toolchain installed and available in `PATH`
6074

6175
```bash

packages/cli/package.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"name": "@openzeppelin/compact-cli",
3-
"description": "CLI for compiling and building Compact smart contracts",
3+
"description": "CLI for compiling, building, and deploying Compact smart contracts",
44
"version": "0.0.3",
55
"keywords": [
66
"compact",
77
"cli",
88
"compiler",
99
"builder",
10+
"deployer",
1011
"testing"
1112
],
1213
"author": "OpenZeppelin Community <maintainers@openzeppelin.org>",
@@ -19,24 +20,28 @@
1920
"type": "module",
2021
"exports": {
2122
"./run-builder": "./dist/runBuilder.js",
22-
"./run-compiler": "./dist/runCompiler.js"
23+
"./run-compiler": "./dist/runCompiler.js",
24+
"./run-deploy": "./dist/runDeploy.js"
2325
},
2426
"files": [
2527
"dist",
2628
"README.md",
29+
"CHANGELOG.md",
2730
"LICENSE"
2831
],
2932
"engines": {
30-
"node": ">=22"
33+
"node": ">=24"
3134
},
3235
"bin": {
3336
"compact-builder": "dist/runBuilder.js",
34-
"compact-compiler": "dist/runCompiler.js"
37+
"compact-compiler": "dist/runCompiler.js",
38+
"compact-deploy": "dist/runDeploy.js"
3539
},
3640
"scripts": {
3741
"build": "tsc -p .",
3842
"types": "tsc -p tsconfig.json --noEmit",
3943
"test": "yarn vitest run",
44+
"coverage": "yarn vitest run --coverage",
4045
"clean": "git clean -fXd"
4146
},
4247
"devDependencies": {
@@ -47,7 +52,10 @@
4752
},
4853
"dependencies": {
4954
"@openzeppelin/compact-builder": "workspace:^",
55+
"@openzeppelin/compact-deployer": "workspace:^",
5056
"chalk": "^6.0.0",
51-
"ora": "^9.4.1"
57+
"ora": "^9.4.1",
58+
"pino": "^9.7.0",
59+
"pino-pretty": "^13.0.0"
5260
}
5361
}

0 commit comments

Comments
 (0)