Skip to content

feat: compact-deployer tool#86

Draft
0xisk wants to merge 46 commits into
mainfrom
feat/compact-deployer
Draft

feat: compact-deployer tool#86
0xisk wants to merge 46 commits into
mainfrom
feat/compact-deployer

Conversation

@0xisk
Copy link
Copy Markdown
Member

@0xisk 0xisk commented May 19, 2026

Types of changes

What types of changes does your code introduce to OpenZeppelin Midnight Contracts?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update (if none of the other choices apply)

Fixes #???

PR Checklist

  • I have read the Contributing Guide
  • I have added tests that prove my fix is effective or that my feature works
  • I have added documentation of new methods and any new behavior or changes to existing behavior
  • CI Workflows Are Passing

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e17a514-973d-4a89-b152-2219914f62d9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/compact-deployer

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@boostsecurity-io boostsecurity-io Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️  9 New Security Findings

The latest commit contains 9 new security findings.

Findings
Dependency: yarn / brace-expansion@ 2.0.2

SUMMARY

Direct Dependency: brace-expansion
Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-33750 Warning 6.5 0.02% 2.0.3
no no brace-expansion: Zero-step sequence causes process hang and memory exhaustion
Remediation :
  • Please consider upgrading brace-expansion to version 2.0.3 or higher to try to resolve this issue.
Dependency: yarn / ip-address@ 10.0.1

SUMMARY

Direct Dependency: ip-address
Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-42338 Warning -1.0 0.07% 10.1.1
no no ip-address has XSS in Address6 HTML-emitting methods
Remediation :
  • Please consider upgrading ip-address to version 10.1.1 or higher to try to resolve this issue.
Dependency: yarn / minimatch@ 9.0.5

SUMMARY

Direct Dependency: minimatch
Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-26996 Critical 7.5 0.03% 9.0.6
no no minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
CVE-2026-27903 Critical 7.5 0.04% 9.0.7
no no minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
CVE-2026-27904 Critical 7.5 0.03% 9.0.7
no no minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Remediation :
  • Please consider upgrading minimatch to version 9.0.7 or higher to try to resolve this issue.
Dependency: yarn / picomatch@ 4.0.3

SUMMARY

Direct Dependency: picomatch
Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-33671 Critical 7.5 0.02% 4.0.4
no no Picomatch has a ReDoS vulnerability via extglob quantifiers
CVE-2026-33672 Warning 5.3 0.06% 4.0.4
no no Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching
Remediation :
  • Please consider upgrading picomatch to version 4.0.4 or higher to try to resolve this issue.
Dependency: yarn / postcss@ 8.5.6

SUMMARY

Direct Dependency: postcss
Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-41305 Warning 6.1 0.03% 8.5.10
no no PostCSS has XSS via Unescaped </style> in its CSS Stringify Output
Remediation :
  • Please consider upgrading postcss to version 8.5.10 or higher to try to resolve this issue.
Dependency: yarn / rollup@ 4.52.5

SUMMARY

Direct Dependency: rollup
Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-27606 Critical 9.8 0.40% 4.59.0
no no Rollup 4 has Arbitrary File Write via Path Traversal
Remediation :
  • Please consider upgrading rollup to version 4.59.0 or higher to try to resolve this issue.
Dependency: yarn / tar@ 7.5.3

SUMMARY

Direct Dependency: tar
Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-23950 Critical 8.8 0.01% 7.5.4
no no Race Condition in node-tar Path Reservations via Unicode Ligature Collisions on macOS APFS
CVE-2026-24842 Critical 8.2 0.02% 7.5.7
no no node-tar Vulnerable to Arbitrary File Creation/Overwrite via Hardlink Path Traversal
CVE-2026-26960 Critical 7.1 0.01% 7.5.8
no no Arbitrary File Read/Write via Hardlink Target Escape Through Symlink Chain in node-tar Extraction
CVE-2026-29786 Critical 6.3 0.01% 7.5.10
no no tar has Hardlink Path Traversal via Drive-Relative Linkpath
CVE-2026-31802 Critical 5.5 0.01% 7.5.11
no no node-tar Symlink Path Traversal via Drive-Relative Linkpath
Remediation :
  • Please consider upgrading tar to version 7.5.11 or higher to try to resolve this issue.
Dependency: yarn / undici@ 5.29.0

SUMMARY

Direct Dependency: undici
Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-1525 Warning 6.5 0.02% 6.24.0
no no Undici has an HTTP Request/Response Smuggling issue
CVE-2026-1526 Critical 7.5 0.02% 6.24.0
no no Undici has Unbounded Memory Consumption in WebSocket permessage-deflate Decompression
CVE-2026-1527 Warning 4.6 0.01% 6.24.0
no no Undici has CRLF Injection in undici via upgrade option
CVE-2026-22036 Warning 5.9 0.02% 6.23.0
no no Undici has an unbounded decompression chain in HTTP responses on Node.js Fetch API via Content-Encoding leads to resource exhaustion
CVE-2026-2229 Critical 7.5 0.20% 6.24.0
no no Undici has Unhandled Exception in WebSocket Client Due to Invalid server_max_window_bits Validation
Remediation :
  • Please consider upgrading undici to version 6.24.0 or higher to try to resolve this issue.
Dependency: yarn / vite@ 7.1.12

SUMMARY

Direct Dependency: vite
Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-39363 Critical 7.5 6.64% 7.3.2
no no Vite Vulnerable to Arbitrary File Read via Vite Dev Server WebSocket
CVE-2026-39364 Critical 7.5 5.15% 7.3.2
no no Vite: server.fs.deny bypassed with queries
CVE-2026-39365 Warning 5.3 1.25% 7.3.2
no no Vite Vulnerable to Path Traversal in Optimized Deps .map Handling
Remediation :
  • Please consider upgrading vite to version 7.3.2 or higher to try to resolve this issue.

Not a finding? Ignore it by adding a comment on the line with just the word noboost.

Scanner: boostsecurity - Trivy (Filesystem scanning)

Copy link
Copy Markdown

@boostsecurity-io boostsecurity-io Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️  10 New Security Findings

The latest commit contains 10 new security findings.

Findings
Dependency: npm / @substrate/connect@ 0.8.11

SUMMARY

Dependency: @substrate/connect
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CWE-1104 Warning no no Package @substrate/connect has reached its end of life.
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require a higher version of the transitive dependency @substrate/connect.
Dependency: npm / glob@ 10.5.0

SUMMARY

Dependency: glob
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CWE-1104 Warning no no Package glob has reached its end of life.
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require a higher version of the transitive dependency glob.
Dependency: npm / minimatch@ 9.0.5

SUMMARY

Dependency: minimatch
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-26996 Critical 8.7 0.03% 9.0.6
no no minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
CVE-2026-27903 Critical 7.5 0.04% 9.0.7
no no minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
CVE-2026-27904 Critical 7.5 0.03% 9.0.7
no no minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require the transitive dependency minimatch @ 9.0.7 or higher.
Dependency: npm / node-domexception@ 1.0.0

SUMMARY

Dependency: node-domexception
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CWE-1104 Warning no no Package node-domexception has reached its end of life.
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require a higher version of the transitive dependency node-domexception.
Dependency: npm / picomatch@ 4.0.3

SUMMARY

Dependency: picomatch
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-33671 Critical 7.5 0.02% 4.0.4
no no Picomatch has a ReDoS vulnerability via extglob quantifiers
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require the transitive dependency picomatch @ 4.0.4 or higher.
Dependency: npm / rollup@ 4.52.5

SUMMARY

Dependency: rollup
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-27606 Critical 8.8 0.40% 4.59.0
no no Rollup 4 has Arbitrary File Write via Path Traversal
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require the transitive dependency rollup @ 4.59.0 or higher.
Dependency: npm / tar@ 7.5.3

SUMMARY

Dependency: tar
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-23950 Critical 8.8 0.01% 7.5.4
no no Race Condition in node-tar Path Reservations via Unicode Ligature Collisions on macOS APFS
CVE-2026-24842 Critical 8.2 0.02% 7.5.7
no no node-tar Vulnerable to Arbitrary File Creation/Overwrite via Hardlink Path Traversal
CVE-2026-26960 Critical 7.1 0.01% 7.5.8
no no Arbitrary File Read/Write via Hardlink Target Escape Through Symlink Chain in node-tar Extraction
CVE-2026-29786 Critical 8.2 0.01% 7.5.10
no no tar has Hardlink Path Traversal via Drive-Relative Linkpath
CVE-2026-31802 Critical 8.2 0.01% 7.5.11
no no node-tar Symlink Path Traversal via Drive-Relative Linkpath
CWE-1104 Warning no no Package tar has reached its end of life.
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require the transitive dependency tar @ 7.5.11 or higher.
Dependency: npm / undici@ 5.29.0

SUMMARY

Dependency: undici
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-1526 Critical 7.5 0.02% 6.24.0
no no Undici has Unbounded Memory Consumption in WebSocket permessage-deflate Decompression
CVE-2026-2229 Critical 7.5 0.20% 6.24.0
no no Undici has Unhandled Exception in WebSocket Client Due to Invalid server_max_window_bits Validation
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require the transitive dependency undici @ 6.24.0 or higher.
Dependency: npm / uuid@ 10.0.0

SUMMARY

Dependency: uuid
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCE

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CWE-1104 Warning no no Package uuid has reached its end of life.
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require a higher version of the transitive dependency uuid.
Dependency: npm / vite@ 7.1.12

SUMMARY

Dependency: vite
Transitive through:
  • compact-tools-monorepo

Location : yarn.lock

OCCURRENCES

Vulnerability Severity CVSS EPSS Affected
Versions
Fixed
Versions
Contains
Malware
Critical
Risk
Description
CVE-2026-39363 Critical 8.2 6.64% 7.3.2
no no Vite Vulnerable to Arbitrary File Read via Vite Dev Server WebSocket
CVE-2026-39364 Critical 8.2 5.15% 7.3.2
no no Vite: server.fs.deny bypassed with queries
Remediation :
  • Please consider upgrading compact-tools-monorepo to versions that require the transitive dependency vite @ 7.3.2 or higher.

Not a finding? Ignore it by adding a comment on the line with just the word noboost.

Scanner: boostsecurity - BoostSecurity SCA

0xisk added 10 commits May 19, 2026 14:26
Adds @openzeppelin/compact-deploy — a Forge-style deployer library for
Midnight Compact contracts — and exposes its CLI through the existing
@openzeppelin/compact-cli package.

Library (packages/deploy/):
- runPipeline orchestrator (config → wallet → faucet → providers →
  submit → persist), decomposed into per-step helpers
- Deployments class — head + history JSON ledger with read methods
  (getHead, getHistory, listContracts) and atomic rotation on write
- Keystore class — Web3 Secret Storage v3-compatible with a
  "midnight-1" version marker; scrypt + AES-128-CTR + SHA-256 MAC
- ProofServer class — lifecycle wrapper over the five-step precedence
  chain (CLI > TOML URL > "auto" container > PROOF_SERVER_PORT > default)
- Typed error hierarchy with stable exit codes (DeployError + subclasses)
- src/ layout: loaders/, config/, wallet/, providers/ + top-level
  errors.ts / pipeline.ts / deployments.ts / index.ts

CLI (packages/cli/):
- runDeploy.ts: thin shell over the deploy library (chalk + ora + pino
  UX, --json mode, exit-code-mapped errors); ~250 LOC, zero business logic
- compact-deploy bin entry added alongside compact-builder + compact-compiler
- engines.node bumped 20 → 22 to match the deploy library

Integration tests (tests/integrations/):
- vitest config + wallet pool harness + Counter fixture
- specs cover deploy, dry-run, history rotation, errors, wallet pool

Root:
- @openzeppelin/compact-deploy workspace devDep
- test:integration / env:up / env:down scripts
- resolutions pin for @midnight-ntwrk/ledger-v8 8.0.3
- Replace the four loader functions with classes that own their loaded
  values: SigningKey, ConstructorArgs, InitialPrivateState, Artifact.
  Each exposes `static async load()` + readonly fields; pipeline reads
  `.hex` / `.values` / `?.value` at the call site.
- Add shared LoaderContext + RefResolver helpers under loaders/ to absorb
  path resolution, readFile, dynamic-import error wrapping, and the
  { file } | { module, export } dispatch.
- Promote loadConfig + LoadedConfig to a CompactConfig class with
  `network(name)` / `contract(name)` lookups that throw with the
  available set on miss. resolveTargets collapses; wallet/resolve.ts
  drops its redundant rootDir parameter (derived from config.rootDir).
- Rename Zod-inferred CompactConfig -> CompactConfigData (internal);
  the public name is now the class.
Lets callers manage the proof-server container with `await using` and
AsyncDisposableStack instead of explicit try/finally for teardown.
Dispose errors are warn-logged rather than thrown so a failed teardown
doesn't mask the deploy's primary failure.
AsyncDisposableStack landed in Node 24.0; Node 22 only ships
Symbol.asyncDispose and `await using`. Realigns engines with the
@tsconfig/node24 lib defs already in use across these packages.
…lasses; consolidate seed helpers

Reshapes the deploy entry-point around resource-managed classes:

- `Deployer` (deployer.ts) replaces the procedural `runPipeline`
  function. `Deployer.prepare(opts)` loads config + signing key, starts
  the proof server, builds or adopts a wallet, and loads constructor
  args; the returned instance exposes `.deploy()` and `.dryRun()`. CLI
  flow becomes:

      await using deployer = await Deployer.prepare(opts);
      return dryRun ? await deployer.dryRun() : await deployer.deploy();

  Owned resources are accumulated in an `AsyncDisposableStack` inside
  `prepare`, moved to the instance on success, and disposed in reverse
  order from `[Symbol.asyncDispose]`. Mid-prepare failures unwind
  cleanly via the local `await using` on the stack.

- `WalletHandler` (wallet/handler.ts) replaces the
  `buildDeployerWallet` free function. It wraps a built
  `MidnightWalletProvider` and implements `[Symbol.asyncDispose]`, so
  the Deployer adds it to its stack with a single `stack.use(owned)`
  instead of `stack.defer(...)` with custom try/catch. Mirrors the
  `ProofServer` pattern.

- `wallet/seeds.ts` merges the previous `local-seeds.ts`,
  `normalize.ts`, and `resolve.ts` into one module. None of those owned
  state or a lifecycle, so a class wrapper would be ceremony; a single
  file with three exports is the right unit. Test renamed
  `normalize.test.ts` -> `seeds.test.ts`.

Public API: `runPipeline as deploy` and `PipelineOptions/PipelineResult`
are gone; consumers now use `Deployer`, `DeployerOptions`,
`DeployResult`. `buildDeployerWallet` replaced by `WalletHandler`.
Integration harness and CLI updated.
Mock-based unit tests covering orchestration semantics that integration
tests can't isolate cheaply:

- Deployer (6 tests): dryRun returns dryRun:true and never calls
  deployContract; deploy submits the tx and returns the populated
  success result; injected walletProvider is adopted without calling
  WalletHandler.build; missing walletProvider builds and starts a
  wallet; Symbol.asyncDispose stops owned wallets but leaves injected
  ones alone; deployContract failures are wrapped in
  DeployTxFailedError.

- WalletHandler (7 tests): mnemonic seed routes through .withMnemonic;
  hex seed routes through .withSeed; additionalFeeOverhead bumps to
  5e17 for the undeployed network and keeps the testkit default
  otherwise; .provider returns the wallet built by
  MidnightWalletProvider.withWallet; Symbol.asyncDispose stops the
  underlying wallet; dispose swallows stop() failures with a warn log.

testkit-js + ledger-v8 + midnight-js are vi.mock'd; CompactConfig,
SigningKey, Deployments run against real tmpdir fixtures. End-to-end
network flow remains covered by tests/integrations/.
Renames the npm package from `@openzeppelin/compact-deploy` to
`@openzeppelin/compact-deployer`. Updates workspace deps in root +
compact-cli, all `from '@openzeppelin/compact-deploy'` imports across
the CLI, integration harness and specs, the JSDoc/README references,
and the regenerated yarn.lock.

The `compact-deploy` binary name and the `packages/deploy/` directory
layout are intentionally left unchanged.
…oyer

Aligns the workspace folder layout with the @openzeppelin/compact-deployer
package name. Git tracks every file as a rename so blame is preserved.
No path references needed updating — the yarn workspaces glob is
`packages/*`, and no script or tsconfig hardcoded `packages/deploy`.
…... style

Convention sweep across all unit + integration test files. No test
behaviour changes — only the strings passed to `it(...)` (and the
`it.each(...)` template in walletPool.spec.ts). All 50 unit tests still
pass.
Five specs are reorganised by feature theme to make the suite scale:

  specs/
    deploy/
      deploy.spec.ts
      dryRun.spec.ts
      historyRotation.spec.ts
    errors/
      errors.spec.ts
    wallet/
      walletPool.spec.ts

No behaviour change — only relative-import depth bumps from `../_harness`
to `../../_harness`. The existing `specs/**/*.spec.ts` glob in vitest.config
already picks up nested directories.
@0xisk 0xisk force-pushed the feat/compact-deployer branch from 958d227 to 26aa42b Compare May 19, 2026 12:29
@0xisk 0xisk added the enhancement New feature or request label May 19, 2026
@0xisk 0xisk self-assigned this May 19, 2026
@0xisk 0xisk added this to the v1.0.0 milestone May 19, 2026
0xisk added 11 commits May 19, 2026 14:40
…ignore-all

Biome ci was failing with 23 errors + 1 warning across the deployer
package and integration harness — mostly `useImportType` and
`organizeImports` autofixes. `yarn lint:fix` resolved 19 of them; the
remaining 4 were:

- `packages/cli/src/runDeploy.ts` × 3 — `lint/suspicious/noConsole`.
  organizeImports had moved the `@openzeppelin/compact-deployer` import
  ahead of the `biome-ignore-all noConsole` directive, so the directive
  no longer applied to the file. Moved the directive to line 2 (right
  after the shebang, before all imports).
- `packages/deployer/src/deployer.ts` — dead `SeedResolution` type
  import after the seeds-module merge consolidated the type alias.

Lint:ci now clean; 50 unit tests still pass.
…ions

Addresses the BoostSecurity findings on PR #86. Eight of the originally
flagged packages auto-upgraded when yarn.lock regenerated post-rebase
(brace-expansion, ip-address, minimatch, picomatch, postcss, rollup,
tar, vite). The remaining three are forced via root resolutions:

  - undici  ^6.24.0  (was 5.29.0 via testcontainers)
    Fixes CVE-2026-1525/1526/1527/22036/2229 — HTTP smuggling,
    WebSocket memory exhaustion, CRLF injection, decompression DoS.
    Major bump but testcontainers' usage stays within the v6 API.

  - glob    ^11.0.0  (was 10.5.0 via archiver-utils, cacache)
    Replaces the EOL v10 line.

  - uuid    ^13.0.0  (was 10.0.0 via dockerode)
    Replaces the EOL v10 line.

Two findings left unaddressed:

  - @substrate/connect@0.8.11 (EOL warning). Pinned exactly by
    `@polkadot/rpc-provider@16.5.6` (constraint is `0.8.11`, not a
    range), so resolutions can't override it without breaking that
    transitive. Would need to wait for @PolkaDot to publish a release
    that drops the EOL dep.

  - node-domexception@1.0.0 (EOL warning). Pulled by `fetch-blob@3.2.0`.
    Modern Node has a native DOMException; the package only matters
    until fetch-blob/undici upstream drops the polyfill import. No
    safe override exists.

Both are warning-level (EOL classification, no active CVE). Build +
50 unit tests + CLI typecheck still pass after resolutions.
When running multiple wallets + the deployer in one process, the default
levelPrivateStateProvider hits fcntl LOCK contention on midnight-level-db/.
Expose privateStateProvider as a DeployerOptions field so tests can inject
inMemoryPrivateStateProvider() from testkit-js and avoid the lock entirely;
buildProviders falls back to constructing the LevelDB-backed provider when
no override is given.

Also inline the dryRunResult/successResult/logDryRun helpers into the
action methods so deploy.ts reads top-to-bottom without three near-empty
helper functions.
…oot Makefile

Adds:
  - PrivateCounter fixture exercising init_private_state + witnesses =
    { module, export } paths end-to-end
  - asyncDisposeCleanup spec: failure mid-prepare unwinds via
    AsyncDisposableStack
  - historyIsolation spec: same artifact under two contract names keeps
    independent head/history slots
  - proofServerAuto spec: DynamicProofServerContainer lifecycle
  - keystorePassphrase + walletLifecycle wallet specs
  - SecondaryCounter contract entry sharing the Counter artifact

Harness updates: shared inMemoryPrivateStateProvider per deploy, syncWallet
between back-to-back deploys to avoid stale UTXO views.

Consolidates orchestration into a single root Makefile (env-up, env-down,
compile, test-integration). The integration Makefile is removed; yarn
scripts now delegate to make so CI and local flows go through the same
trap-protected recipe.
…loy/ secrets

Adds a top-level compact.toml with a [networks.preprod] block populated
from testkit-js's PreprodTestEnvironment (URLs taken verbatim from the
installed @midnight-ntwrk/testkit-js bundle, including the /api/v4
graphql path and the full /api/request-tokens faucet URL — the README
example showed v3 and a bare host, both stale).

Ignores deploy/*.seed, *.signingkey, and *.keystore.json so wallet
material doesn't accidentally land in commits. The local integration
tests still use tests/integrations/compact.toml; this root file is for
real-network deploys driven from the repo root.
Two intertwined changes that together make 'deploy a Compact contract to
preprod with a funded seed' actually work from compact-deployer.

Wallet-state cache (the structural fix):
  - WalletHandler.build now bypasses FluentWalletBuilder so it can branch
    the shielded sub-wallet between WalletFactory.createShieldedWallet
    (fresh) and WalletFactory.restoreShieldedWallet (resumed from
    './.states/<network>-<seed-hash>.gz').
  - WalletHandler.saveCache() snapshots the shielded sub-wallet via
    WalletSaveStateProvider after every successful sync; deployer.ts
    calls it best-effort, never blocks the deploy on a cache write.
  - Cache filename derives from network id + SHA-256 of the seed bytes
    (testkit-js's own getWalletStateFilename embeds the seed verbatim;
    we don't).
  - On any restore failure (file missing, corrupt, SDK version drift)
    the handler warns and falls through to a fresh build.
  - --no-cache CLI flag (DeployerOptions.skipWalletCache) forces a
    fresh sync.
  - .gitignore covers .states/ and **/.states/.

Observable sync (the UX fix):
  - syncAndVerifyFunds replaces testkit-js's syncWallet with an in-house
    Rx pipeline so we can (a) lift the hardcoded 90s timeout to a
    configurable --sync-timeout (default 10m, exposed as
    DeployerOptions.syncTimeoutMs), (b) gate on state.isSynced
    (same condition, cleaner), and (c) emit progress + balance lines
    instead of testkit-js's per-emission flag spam.
  - Throttled (30s) 'Still syncing — Nm Xs elapsed' line shows
    applied/highest event counts + percentage + balance per sub-wallet
    so a multi-minute preprod sync looks alive rather than hung.
  - Edge-triggered 'X sync complete — balance' lines fire the moment
    each sub-wallet reaches strict-complete, so users with NIGHT see
    their balance the instant unshielded finishes (~30s) instead of
    waiting for the full sync (~hours).
  - logWalletAddresses prints all three bech32m addresses (shielded /
    unshielded / dust) right after wallet.start(false), so the user can
    sanity-check the deployer derived the seed they intended *before*
    committing to a long sync.
  - UnfundedWalletError gate now accepts shielded > 0 OR unshielded > 0
    (was shielded-only); matches midnight-apps's CLI pattern and
    preprod-faucet-funded wallets carry NIGHT not shielded.

README + tests updated to match. Handler tests rewritten end-to-end
since WalletHandler no longer wraps FluentWalletBuilder; deployer
tests' fake provider/handler grew wallet.state() observable + saveCache
spy + unshielded balances.
Mirror the shielded-state cache for the dust sub-wallet so subsequent
runs against a real network resume from the previous run's stopping
point instead of re-walking the unfiltered dustLedgerEvents stream
from id=0. First preprod run still pays the full ~1h+ dust sync; every
later run boots in seconds.

Also add a periodic checkpoint inside syncAndVerifyFunds that snapshots
both sub-wallets every 5 minutes during sync (separate from the final
post-sync save). A Ctrl+C in the middle of a long first-run loses at
most one checkpoint interval, not the whole run.

Cache filename layout switched from `<network>-<seed-hash>.gz` to
`<network>-<seed-hash>-<kind>.gz` so shielded and dust snapshots don't
collide. testkit-js exposes restoreShieldedWallet but no equivalent
restoreDustWallet, so dust restore routes through
`DustWallet(config).restore(serializedState)` directly. WalletHandler
also now exposes the unshielded keystore so downstream helpers (e.g.
upcoming DustBootstrap) can sign NIGHT payloads without re-deriving
keys from the seed.
Opt-in lighter sync gate: when --fast-deploy is set, the deploy attempts
to submit as soon as the unshielded sub-wallet is strict-complete and
NIGHT > 0, instead of waiting for shielded + dust strict-complete.

Intended for preprod wallets whose faucet drop is NIGHT (unshielded) +
dust, where unshielded sync completes in seconds but dust strict-complete
can take an hour+ on cold caches because dustLedgerEvents is an
unfiltered global stream. The risk we accept: if no dust UTXO has
materialised in the wallet by the time the deploy submits, the tx is
rejected with `Insufficient Funds: could not balance dust` and the user
retries without --fast-deploy. The post-sync cache save is skipped in
fast-deploy mode to avoid persisting a partial shielded snapshot.

Default behaviour is unchanged: gate stays on the strict-complete
`state.isSynced` signal that the WalletFacade exposes.
DustBootstrap.register() walks the wallet's unshielded UTXO set, picks
out NIGHT UTXOs that don't have registeredForDustGeneration=true yet,
and submits a single dust-registration tx via
walletFacade.registerNightUtxosForDustGeneration → signRecipe →
finalizeRecipe → submitTransaction.

Useful for wallets that received NIGHT outside the standard preprod
faucet flow (which auto-registers): without registration, those UTXOs
never generate dust and the wallet can't pay any tx fees. The fee for
the registration tx itself is self-funding — paid from the implicit
dust that the NIGHT UTXOs have been accruing since their ctime,
computable locally without dust-sync.

`packages/deployer/scripts/dust-bootstrap.ts` is a standalone runner
(node --experimental-strip-types) that exercises the class against a
real network without going through Deployer.prepare. Intended as an
operator tool and as smoke-test scaffolding; not exposed via the
published CLI.
0xisk added 25 commits May 22, 2026 10:16
…stream

Lets a fresh-funded wallet bypass the full `dustLedgerEvents(id: 0)`
walk (1h+ on preprod) by bootstrapping the dust sub-wallet at an
arbitrary `appliedIndex` near the chain tip. Builds a fresh dust
wallet, harvests its initial-state JSON snapshot, mutates the `offset`
field to N, then restores — the resulting wallet subscribes to
`dustLedgerEvents(id: N)` and only walks the tail.

Wired through `WalletHandler.build` -> `Deployer.prepare` -> CLI
(`compact-deploy --dust-start-id <N>`) and the standalone
`dust-bootstrap` script. Implies the dust cache save is skipped post-
sync, because the partial-history snapshot would silently poison
future runs that resume from it.

`scripts/probe-dust-tip.ts` ships as dev-time scaffolding to estimate
the current chain tip via a graphql-ws subscription against the
preprod indexer.

Caveat: only helps wallets whose entire dust event history (initial
UTXOs + generation registrations) sits at id >= N. Wallets with
historical registrations at id < N will boot with no spendable dust;
indexer-side filtered queries (midnight-indexer#1167) are the longer
fix.
…t wallets

testkit-js's `DEFAULT_DUST_OPTIONS.additionalFeeOverhead` is `5e20` —
a fee-balance safety margin sized for production wallets with very
large dust reserves. Faucet-funded test wallets on preview/preprod
only have ~3e15 dust, so the default makes every deploy fail with
`Insufficient Funds: could not balance dust` even when the computed
fee is microscopic. Override to `5e14` for all non-mainnet networks
(plenty of headroom for the deploy fee, well below a faucet wallet's
balance).

Also: the `costParameters` config layer is RUNTIME state on
`DustWallet(...)`, not baked into the cached snapshot. Our restore
path (and the `--dust-start-id` skip-ahead path) were calling
`DustWallet(config)` without those parameters, so cached wallets
fell back to the SDK's 5e20 default regardless of what we passed in
`dustOptions`. Extracted a `buildDustConfig` helper to layer the
costParameters on, mirroring what `WalletFactory.createDustWallet`
does internally, and applied it everywhere we construct a
`DustWallet` builder.

Adds `[networks.preview]` to `compact.toml` (preview is the
recommended public testnet while preprod is blocked on the
`midnight:event[v9]` DustSpendProcessed deserialization bug — see
the Midnight dev Discord, May 22). Verified end-to-end by deploying
Counter to preview.
…orer URL

The --fast-deploy / --dust-start-id flags and DustBootstrap helper were
experiments aimed at side-stepping the preprod dust-sync stall. They turned
out to be dead ends (the dust commitment tree forbids non-linear inserts,
so resuming mid-stream corrupts state), and the preprod blocker lives
upstream in wallet-sdk anyway. Strip them along with faucet handling — the
deployer now does direct sync-then-deploy. Adds an `explorer` URL field so
successful deploys can print a clickable contract link.
Aligns compact-deployer with the SDK lineage that downstream consumers
(e.g. OpenZeppelin/midnight-apps) ship via vendor tarballs, so wiring
compact-deploy into those projects no longer trips over Symbol-identity
mismatches between mixed 3.x/4.x wallet SDKs sharing one process.

- midnight-js-*: 4.0.2 -> 4.1.0
- testkit-js:    4.0.2 -> 4.1.0
- compact-js:    2.5.0 -> 2.5.1
- wallet-sdk-shielded:           2.1.0 -> 3.0.0
- wallet-sdk-unshielded-wallet:  2.1.0 -> 3.0.0
- wallet-sdk-facade:             3.0.0 -> 4.0.0
- wallet-sdk-dust-wallet:        (new)  4.0.0
- wallet-sdk-hd:                 3.0.1 -> 3.0.2
- wallet-sdk-address-format:     3.1.0 -> 3.1.1

Root resolutions pin all midnight-js + testkit-js packages to the local
vendor/*-4.1.0.tgz tarballs (matching the existing ledger-v8 pin).
…ocker, roadmap

Frame the deployer as developer-preview / testnet-only, document the
upstream preprod dust-sync bug, point bug reports at the right repos, and
list the known TODOs/roadmap so consumers know what's coming.
One-liner per exported symbol; multi-line only when the body encodes a
non-obvious WHY (upstream SDK quirks, hidden invariants, version pins).
Drops step-narration, signature-restating prose, tutorial-style "why
both / mirrors X" paragraphs, and historical asides that belong in
commit messages rather than source.

28 files, -661 net lines (833 deletions, 172 insertions). No behaviour
change — types pass clean on both packages and all 168 tests
(149 deployer + 19 cli) keep passing.

Also drops the stale "multi-contract orchestration" item from the
deployer README — Compact composes via modules into one artifact, so
there's no deploy graph to orchestrate.
Pinned to 4.0.15 to match the workspace vitest version exactly — the
newer 4.1.7 coverage package imports `BaseCoverageProvider` from
`vitest/node`, which 4.0.15 doesn't export. Enables
`yarn vitest run --coverage` on both deployer + cli.
These four CLI source files had 0% effective coverage — no test file
imported them, so v8 didn't even see them in the report. Adds 47 tests
across four new spec files mirroring the existing `runCompiler.test.ts`
patterns (vi.mock external deps, custom helpers, exit-code assertions
via spied process.exit).

Coverage on these files after this commit:
- logger.ts:     100% stmt / 100% branch / 100% func / 100% line
- prompt.ts:     100% stmt / 100% branch / 100% func / 100% line
- runBuilder.ts: 100% stmt / 100% branch / 100% func / 100% line
- runDeploy.ts:  100% stmt / 100% branch / 100% func / 100% line

CLI package aggregate: 100% stmt / 99.13% branch / 100% func / 100% line
across 5 files, 66 tests passing.
…e cases

Closes the three worst statement-coverage gaps in the deployer
package (seeds.ts 52.6%, keystore.ts 68.8%, deployer.ts 74.6%) by
exercising error paths and conditional branches the existing
happy-path tests skipped.

- seeds.test.ts (+14): resolveSeed precedence chain (cli/env/keystore/
  local/final-throw), keystore not-found + no-prompt errors, safeRead
  + absoluteUnder edge cases
- keystore.test.ts (+10): decrypt happy path + MAC-mismatch error,
  AES-CTR round-trip, writeToFile 0o600 mode check, toJSON shape,
  fromJSON KDF + cipher rejections
- deployer.test.ts (+12): syncAndVerifyFunds timeout + UnfundedWallet
  paths (driven indirectly via prepare with a fake wallet provider),
  buildExplorerUrl variants (0x-prefix, trailing slash, empty cases),
  resolveTargets missing-default-network, owned-wallet saveCache
  failure warn-log, describeProgress branch with highest > 0

Coverage after this commit:
- seeds.ts:    52.6%  → 100% stmt / 96.55% branch
- keystore.ts: 68.8%  → 100% stmt / 100%   branch
- deployer.ts: 74.6%  → 98.55% stmt / 92.10% branch

Bonus: the 2 pre-existing logWalletAddresses unhandled-rejection
warnings are now gone — properly mocking
@midnight-ntwrk/wallet-sdk-address-format + getNetworkId lets the
owned-wallet happy path exercise without partial-mock leakage.

201 deployer tests passing (was 149).
Brings six deployer files from the 80-95% statement band up to ≥95%
by exercising remaining error / fallback branches:

- init-state.test.ts (+2): module-ref happy path + missing-export ConfigError
- args.test.ts (+3): module-ref happy + non-array ConfigError + file-ref
  malformed-JSON ConfigError
- compact-config.test.ts (+3): has/list helpers, --config missing-path
  ConfigError, findUpward returning undefined when no compact.toml exists
- artifact.test.ts (+2): witnesses module export not an object
  ConfigError + module-ref happy path
- build.test.ts (+1): invoke privateStoragePasswordProvider callback so
  the closure on line 59 runs
- private-state-password.test.ts (+1): vi.doMock('node:crypto') forces
  the rejection loop to fall through to the explicit throw

Final coverage on these six:
- init-state.ts:             100% / 100%
- args.ts:                   100% / 100%
- artifact.ts:                98.07% / 93.33% (line 128 is dead-defensive
                                              — schema makes it unreachable)
- compact-config.ts:          97.87% / 90.47% (line 48 is a TOCTOU
                                              readFile path — out of scope)
- build.ts:                  100% / 100%
- private-state-password.ts: 100% / 100%
…erver / handler

Three single-branch gaps in otherwise well-covered files:

- deployments.test.ts (+1): constructor with absolute deploymentsDir
- proof-server.test.ts (+1): static-container stop path for
  PROOF_SERVER_PORT env precedence
- handler.test.ts (+1): saveCache() warn-log path when persist throws

Coverage after this commit:
- deployments.ts:  96.15% line → 100% line
- proof-server.ts: 96.55% line → 100% line
- handler.ts:      98.48% line → 100% line
Adds a vitest coverage config to both deployer + cli (deployer didn't
have a vitest.config.ts at all; created one). Enforced thresholds:
  statements: 95, branches: 90, functions: 100, lines: 95

Current state clears all four bars on both packages with headroom:
  deployer: 99.11/94.75/100/99.27
  cli:      100/99.13/100/100

A coverage drop below those thresholds now fails the relevant package's
test script — regressions get caught in CI.

Wires:
- `yarn coverage` per package (`vitest run --coverage`)
- root `yarn coverage` (`turbo run coverage`)
- coverage task in turbo.json with `coverage/**` as output (already
  gitignored)
Sweeps the `Phrase A — Phrase B; Phrase C.` em-dash + semicolon AI-tell
out of every prose-bearing line in the deployer package. Splits the
glued clauses into two or three plain sentences, swaps the em-dash for
a parenthesis or colon, or just uses commas. Single em-dashes setting
off a real parenthetical aside are kept (e.g. the three `Unshielded
sync complete — NIGHT balance: ...` log lines).

- README.md: Known issues numbered list (5 items), Status callout, and
  a handful of other paragraphs. Net -59 lines as the dense paragraphs
  collapse into shorter sentences.
- src/deployer.ts: 6 TSDoc + inline-comment rewrites.
- src/wallet/handler.ts: 3 rewrites.
- src/loaders/context.ts, src/providers/network.ts, src/wallet/keystore.ts: 1 each.
- src/deployer.test.ts: 2 inline-comment rewrites.

201 deployer tests passing, types clean. CLI package untouched (out of
scope for this pass).
Drops the root-level `examples/` directory with one runnable example,
`fungible-token/`, that deploys a small ERC20-flavoured contract via
`compact-deploy`. The contract wraps the OpenZeppelin Compact
FungibleToken module (vendored into `contracts/token/`,
`contracts/security/`, `contracts/utils/` — no submodule) and exposes
a deliberately rich constructor so the example shows every common
Compact primitive type flowing from a JS args file into a deployed
contract:

- Opaque<"string">  (name, symbol)
- Uint<8>           (decimals)
- Bytes<32>         (treasury)
- Uint<128>         (maxSupply)
- Uint<32>          (feeBps)
- Uint<64>          (quorum)
- Boolean           (isMintable)
- Bytes<8>          (tag)

The example is OUTSIDE the yarn workspace per the design discussion —
self-contained, with its own compact.toml, package.json, and committed
artifact. Scripts call into the workspace CLI via relative paths
(`node ../../packages/cli/dist/runDeploy.js`), so users don't need
`yarn install` inside the example folder.

Also tweaks .gitignore:
- Negate the global `artifacts/` rule for `examples/**/artifacts/`
  so committed example artifacts actually ship.
- Change `deploy/*.{seed,signingkey,keystore.json}` to
  `**/deploy/*.{...}` so the rule matches nested deploy directories
  under examples/.
Adds an optional `args?: readonly unknown[]` field to DeployerOptions
so programmatic callers can pass constructor args directly without
round-tripping through a JSON file or .args.mjs module. This is the
ergonomic path for hand-written deploy scripts that want to keep args
inline in JS (BigInts, Uint8Arrays, Booleans).

Precedence (highest first):
  api > --args > inline TOML > file ref > module ref > empty

`ConstructorArgs.source` gains a new 'api' variant so callers can tell
where the args came from. Adds 2 tests covering apiArgs winning over
every other source and an empty-array case.

203 deployer tests passing (was 201), types clean.
…4.1.0)

The Midnight team published the full midnight-js + testkit-js 4.1.0
lineage to the public npm registry on 2026-05-25, so we no longer need
to vendor 12 of the 13 local tarballs in vendor/.

Dropped resolutions for:
  midnight-js-{compact,contracts,fetch-zk-config-provider,
                http-client-proof-provider,indexer-public-data-provider,
                level-private-state-provider,logger-provider,
                network-id,node-zk-config-provider,types,utils}
  testkit-js

Kept @midnight-ntwrk/midnight-js-protocol as a vendor tarball — it's
still not published on npm (404 from registry as of 2026-05-25).

203 deployer tests still pass on npm-resolved 4.1.0; lockfile changes
are the yarn 4 replacement of 12 file: locators with npm: descriptors.
… deploy script

Reworks the fungible-token example so it shows the actual user
experience: install @openzeppelin/compact-deployer like any npm
dependency, then run a hand-written per-contract deploy script.

What changed:

- `package.json` declares @openzeppelin/compact-deployer as a real
  dependency via `file:../../packages/deployer` (the local workspace
  copy). The CLI is intentionally NOT a dep — the example uses the
  programmatic API instead, which sidesteps the CLI's workspace:^
  transitive deps. Only one resolution remains (midnight-js-protocol
  still vendored — not yet on npm).

- New `deploy/deployTokenExample.ts` is the per-contract deploy
  script. Imports `Deployer` from the installed package, declares the
  9 constructor args inline as native JS values (string, number,
  bigint, Uint8Array, boolean), parses a small argv (--network,
  --dry-run, --sync-timeout), then runs `Deployer.prepare()` +
  `.deploy()` / `.dryRun()`. Edit the `constructorArgs` array to
  change what gets deployed.

- Dropped `deploy/TokenExample.args.mjs`. The TOML `args = { module }`
  reference is gone too — args now flow from the script via the new
  `DeployerOptions.args` field added in cb34632.

- `package.json` scripts call `node deploy/deployTokenExample.ts`
  directly. Node 24's native TypeScript stripping runs the .ts file
  without a build step.

- README rewritten around the install + script flow (the prior version
  walked through a `node ../../packages/cli/dist/runDeploy.js`
  workaround that's no longer needed).

- .gitignore extended so nested `.yarn/cache`, `.yarn/install-state.gz`,
  and `.pnp.*` from per-example installs don't ship.

Verified: `yarn install` in examples/fungible-token resolves cleanly
(373 packages, ~80MB) and the script reaches `Deployer.prepare()`
through to the expected "no signing key" error.
Surface a high-level entrypoint so hand-written deploy scripts can be
as short as:

  import { runDeploy } from '@openzeppelin/compact-deployer';
  await runDeploy({ contract: 'Token', args: [...] });

runDeploy() handles argv parsing (--network, --dry-run, --sync-timeout,
--no-cache, --seed-file, --proof-server, --config, --json, -v), pino
logger setup, Deployer.prepare + deploy/dryRun, formatted result
output, and process.exit with the typed DeployError.exitCode on
failure. Explicit options always win over argv.

This is the programmatic mirror of the `compact-deploy` CLI binary
that the `packages/cli/src/runDeploy.ts` shell uses, minus the
CLI-only concerns (ora, chalk, --help, prompts). Lets consumers embed
the deploy flow in their own scripts without reimplementing argv +
exit-code routing.

7 new tests for runDeploy cover argv merging, --sync-timeout
validation, JSON output, and DeployError vs generic-error exit-code
mapping. 210 deployer tests passing.
… flows

Treats the example as a real yarn workspace member instead of a
file:-installed standalone. Three knock-on changes:

- `examples/*` is added to the root `workspaces` array. The example's
  package.json depends on `@openzeppelin/compact-deployer` and
  `@openzeppelin/compact-cli` via `workspace:^`. The midnight-js-protocol
  vendor-tarball resolution lives only in the root package.json now —
  the example inherits it via the workspace.
- The compile script switches from the system `compact` binary to the
  workspace's own `compact-compiler` (hoisted into the example's bin
  via the workspace `node_modules/.bin/` chain).
- The deploy scripts shrink to ~5 lines each by calling the new
  `runDeploy()` helper from the deployer. The boilerplate (argv,
  logger, exit codes) moves out of the user's script.

Two flows are now demonstrated side-by-side:
  - `deployTokenExample.ts` — args inline in the script (recommended
     for rich JS types).
  - `TokenExample.args.mjs` + `deployTokenExampleFromArgs.ts` — args in
     a separate module referenced from compact.toml.

Verified: `cd examples/fungible-token && yarn compile && yarn deploy:local`
reaches `Deployer.prepare()` and fails at the expected missing-signingkey
step.
`compact-compiler --hierarchical` produces an artifact directory for
every .compact file in the example tree, including the vendored
modules (FungibleToken, Initializable, Utils). The module dirs have
no keys/zkir and aren't deployable on their own — they regenerate on
every `yarn compile` and just add diff noise. Ignore them so only
`artifacts/TokenExample/` stays committed.
…+ add preprod

Reorganises the fungible-token example around two clean paths:

  Path 1 — `yarn deploy:{local,preview,preprod}` runs the TS script
           (`deploy/deployTokenExample.ts`) with args inline. Built on
           `runDeploy()` from @openzeppelin/compact-deployer.

  Path 2 — `yarn cli:{local,preview,preprod}` runs the `compact-deploy`
           binary from @openzeppelin/compact-cli. Args come from
           `deploy/TokenExample.args.mjs` via the `args = { module }`
           ref in `compact.toml`. No JS script needed.

Other changes:
- Adds a `[networks.preprod]` block to `compact.toml`.
- Drops `deployTokenExampleFromArgs.ts` — its role (programmatic API
  reading args from the module) is now covered by the CLI demo.
- README rewritten around the two paths + a "when to pick which" table.
Three new ways to pass constructor args to runDeploy, all derived
from the artifact's compiler-emitted .d.ts — no codegen, no
hand-written interface unless the named-object form is chosen:

- Curried call: `runDeploy(Contract)(...args)`. Names the contract
  once via the imported class; resolveContractName walks
  `[contracts.X]` entries in compact.toml and identity-matches the
  Contract class to pick the config. Args are typed function
  parameters, so the editor shows each param's name + type via
  signature help as you type.
- Tuple form via `ConstructorArgsOf<typeof Contract>`: extracts the
  labeled tuple from `Contract.prototype.initialState` for callers
  who prefer the options-object API. Hover shows types; no
  per-comma signature help.
- Named-object form: `args: { _name: '…', … }`. The deployer parses
  the artifact's `index.d.ts` at runtime (parseConstructorParamNames,
  with SSA-suffix stripping) and reorders the object into the
  positional tuple. Requires a hand-written interface today; see
  `logs/feature-compactc-export-constructor-args-interface.md` for
  the upstream fix that would generate it.

Also exports `constructorArgs(Contract, ...args)`, a typed helper
that gives per-comma editor hints inside the options-object call
form.
The deploy script now uses `runDeploy(Contract)(...args)` so the
contract is named once via the imported `Contract` class — the
deployer matches it to `[contracts.TokenExample]` by class
identity. Each constructor arg is a typed function parameter, so
the editor shows the next param's name + type as you type each
comma.

README documents the curried form as the primary path with the
options-object form (string-keyed) listed as the alternate for
multi-contract / TOML-driven / programmatic flows.

TokenExample.args.mjs uses `bigint` literals consistently (Compact
emits every Uint<N> as bigint).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant