Skip to content

Establish @fairmint/canton-dev-tools with LocalNet CLI and test helpers (ENG-1635 PR1) - #1

Merged
HardlyDifficult merged 19 commits into
mainfrom
cursor/establish-canton-dev-tools-5279
Aug 10, 2026
Merged

Establish @fairmint/canton-dev-tools with LocalNet CLI and test helpers (ENG-1635 PR1)#1
HardlyDifficult merged 19 commits into
mainfrom
cursor/establish-canton-dev-tools-5279

Conversation

@HardlyDifficult

@HardlyDifficult HardlyDifficult commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Establishes @fairmint/canton-dev-tools@0.1.0 as the shared owner of Fairmint Canton LocalNet lifecycle and reusable integration-test helpers (ENG-1635).

Ready for merge once CI on tip is green and the Trusted Publisher checklist below is done (or accepted as a post-merge blocker before first publish succeeds).

Included

  • Native CLI (canton-dev-tools / legacy canton-localnet): start, readiness, diagnostics, teardown (+ legacy aliases)
  • TypeScript exports via @fairmint/canton-dev-tools and @fairmint/canton-dev-tools/testing
  • Central pin ownership (quickstart/splice/scribe/protocol) in CLI + COMPATIBILITY.md
  • Package artifact guard forbids .dar, libs/**, and fixtures/**
  • CI: lint/typecheck/unit/pack + LocalNet smoke workflow
  • Splice CIP-56/CIP-112 builders + fixture fetch (SHA-pinned splice-test-token-v2@0.6.11)
  • Auto-publish on main: .github/workflows/publish.yml + scripts/prepare-release.ts (OIDC Trusted Publishing; no NPM_TOKEN on the publish step; first expected version 0.1.0)

Companion consumer PR: Fairmint/canton-node-sdk#398

Test plan

  • npm install && npm run build && npm run lint && npm test && npm run pack:check
  • Unit CI (build-and-test) green on tip
  • npm run fixture:splice-test-token-v2:fetch verifies SHA
  • LocalNet CIP-56 transfer smoke exercised (GH Actions LocalNet smoke on tip)
  • Publish workflow + prepare-release landed on tip; OIDC npmrc + first-release changelog fixes on tip (9d83dfd)

Human checklist — npm Trusted Publisher (required before first successful publish)

Auto-publish is implemented and will run on merge to main. A human must configure the Trusted Publisher once so the first publish can succeed:

  1. Open npm package access for @fairmint/canton-dev-tools (create the package under @fairmint if it does not exist yet), or use org Add Trusted Publisher:
  2. Add a GitHub Actions Trusted Publisher with:
    • Provider: GitHub Actions
    • Organization/user: Fairmint
    • Repository: canton-dev-tools
    • Workflow filename: publish.yml (exact; no path prefix)
    • Environment: leave empty (workflow does not use a GitHub Environment)
  3. Confirm the package publish ACL allows the @fairmint org / maintainers as needed.
  4. After merge, confirm the Publish to NPM workflow on main succeeds and tags v0.1.0 (or the prepared floor version).

Notes from the workflow:

  • Publish uses OIDC (id-token: write); do not set NODE_AUTH_TOKEN / NPM_TOKEN on the Publish step.
  • Install may still use NPM_TOKEN only to fetch private @fairmint/* peers.
  • Provenance is omitted (repo may be private); OIDC trusted publishing still works without --provenance.

Follow-ups (post-merge)

  • After first npm publish: swap consumer git deps to semver ranges on @fairmint/canton-dev-tools
  • Hard cutover: remove LocalNet from canton-node-sdk publish tarball
  • DAR fixture build/upload/vet/breaking-upgrade scenarios with dpm
  • GH Actions LocalNet Docker hardening for CI smoke (as needed)
  • Remaining consumer migrations (explorer, ocp-daml, etc.)
  • Update dev-docs LocalNet page for Dev Tools ownership

Note

Medium Risk
Large greenfield package that becomes the shared LocalNet pin owner and PR regression gate (Docker smoke); publish workflow needs one-time npm Trusted Publisher setup before first release.

Overview
Introduces @fairmint/canton-dev-tools as Fairmint’s shared owner of Canton LocalNet lifecycle and integration-test helpers (ENG-1635).

The canton-dev-tools / canton-localnet CLI wraps scripts/localnet-cloud.sh with product commands (start, readiness, diagnostics, teardown), automatic cn-quickstart cache fetch, and central default pins (quickstart ref, Splice/Scribe 0.6.14, protocol 35, oauth2 auth) documented in COMPATIBILITY.md. TypeScript is exported from the main entry and @fairmint/canton-dev-tools/testing (LocalNet client config, ledger helpers, and CIP-56 / CIP-112 command builders plus runCip56TransferSmoke against Splice TestTokenV2).

CI adds standard build/lint/test/pack:check (forbids fixtures/**, *.dar, libs/** in the npm tarball and verifies CLI pins), a PR LocalNet smoke workflow (start → readiness → CIP-56 transfer → teardown), and publish.yml on main with npm OIDC trusted publishing, prepare-release versioning, and git tags.

Internal-only fixtures (DAR lifecycle sources, SHA-pinned TestTokenV2 fetch) support smoke tests but are excluded from publish.

Reviewed by Cursor Bugbot for commit 9d83dfd. Bugbot is set up for automated code reviews on this repo. Configure here.

Extract LocalNet lifecycle from canton-node-sdk into @fairmint/canton-dev-tools
with product CLI commands, reusable testing exports, artifact guards, and CI.
Hardcode scribe/protocol defaults in bin/canton-dev-tools so npx start
matches package.json pins, extend pack/unit pin-parity checks, document
oauth2 vs shared-secret auth, add CI-only dar-lifecycle fixture scaffolding,
and run LocalNet smoke diagnostics before teardown on failure.
Sparse-fetch splice-test-token-v2 DAR with SHA/package-id asserts, export
unit-testable TokenRules/mint/transfer/accept builders for provider-less
accounts, add LocalNet integration skeleton and smoke workflow steps.
Central evidence covering node-sdk, fairmint-sdk, ocp-canton-sdk, privy-sdk,
and canton-dev-tools publish surfaces.
Wire validate→upload/vet→createParty→TokenRules→OfferMint→Accept→
TransferFactory_Transfer→Accept→holdings assert using Splice
splice-test-token-v2 only, plus LocalNet validator client helper.
The bin is a shell script; running it under node broke localnet:start.
Prefer OAuth validator for createParty, use package-name template ids
for ACS, and disclose pending offers with readAs so bob can Accept.
@HardlyDifficult
HardlyDifficult marked this pull request as ready for review August 10, 2026 17:40
@HardlyDifficult
HardlyDifficult requested a balanced review from Copilot August 10, 2026 17:40
Comment thread docs/sdk-package-boundary-audit.md Outdated
Comment thread scripts/run-cip56-transfer-smoke.ts
Comment thread .github/workflows/localnet-smoke.yml
Drop docs/sdk-package-boundary-audit.md (references private repos)
and README link; note ENG-1635 ownership in COMPATIBILITY.md.
Treat Ledger HTTP 401 as reachable in CIP-56 smoke (oauth2 parity).
Comment thread package.json Outdated
Comment thread package.json
Comment thread package.json Outdated
Comment thread src/testing/localnetLedgerClients.ts
Comment thread src/testing/localnetLedgerClients.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Establishes the canonical LocalNet CLI and testing package supporting the canton-node-sdk migration.

Changes:

  • Adds LocalNet lifecycle CLI and centralized compatibility pins.
  • Adds reusable CIP-56/CIP-112 testing and transfer-smoke helpers.
  • Adds fixture, packaging, unit-test, and CI safeguards.

Reviewed changes

Copilot reviewed 34 out of 39 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
tsconfig.json Configures strict TypeScript builds.
test/unit/testing/helpers.test.ts Tests shared helper basics.
test/unit/testing/cip56.test.ts Tests CIP-56 builders and parsing.
test/unit/scripts/package-artifacts.test.ts Tests package exclusions.
test/unit/scripts/localnet-cloud.test.ts Tests LocalNet restart handling.
test/unit/scripts/canton-dev-tools.test.ts Tests CLI pins and aliases.
test/integration/localnet/cip56-transfer.test.ts Adds LocalNet transfer integration smoke.
src/testing/transactionHelpers.ts Adds transaction inspection helpers.
src/testing/testConfig.ts Adds test configuration and retry helpers.
src/testing/localnetLedgerClients.ts Adds role-specific LocalNet clients.
src/testing/index.ts Exports testing APIs.
src/testing/cip56/transferSmoke.ts Implements transfer-smoke orchestration.
src/testing/cip56/index.ts Exports CIP-56 APIs.
src/testing/cip56/ids.ts Defines template and interface IDs.
src/testing/cip56/fixture.ts Defines fixture provenance and validation.
src/testing/cip56/commands.ts Builds ledger commands.
src/testing/cip56/accounts.ts Builds account and context records.
src/index.ts Exposes testing helpers at package root.
scripts/run-cip56-transfer-smoke.ts Runs the transfer smoke.
scripts/localnet-cloud.sh Implements LocalNet lifecycle operations.
scripts/fetch-splice-test-token-v2.ts Fetches and verifies the reference DAR.
scripts/check-package-artifacts.ts Guards the published package surface.
scripts/build-dar-lifecycle-fixture.sh Builds the internal DAR fixture.
README.md Documents installation and usage.
package.json Defines package metadata and commands.
jest.config.js Configures unit tests.
fixtures/splice-test-token-v2/README.md Documents the Splice fixture.
fixtures/splice-test-token-v2/.gitignore Excludes downloaded DARs.
fixtures/dar-lifecycle/README.md Documents lifecycle fixtures.
fixtures/dar-lifecycle/daml/DarLifecycle/Marker.daml Adds a minimal DAML template.
fixtures/dar-lifecycle/daml.yaml Configures the DAML fixture.
eslint.config.mjs Configures linting.
docs/sdk-package-boundary-audit.md Records package-boundary findings.
COMPATIBILITY.md Documents centralized compatibility pins.
bin/canton-dev-tools Adds the distributable CLI wrapper.
.prettierrc Configures formatting.
.gitignore Excludes generated artifacts.
.github/workflows/localnet-smoke.yml Adds scheduled LocalNet smoke CI.
.github/workflows/ci.yml Adds build, test, and packaging CI.
Suppressed comments (1)

src/testing/cip56/commands.ts:124

  • The transfer builder has the same numeric serialization failure: valid small numeric inputs become exponent notation via String, which the Ledger's DAML Decimal decoder rejects. Restrict amounts to strings or apply a validated fixed-point conversion.
    amount: String(params.amount),

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread package.json Outdated
Comment thread scripts/run-cip56-transfer-smoke.ts Outdated
Comment thread test/integration/localnet/cip56-transfer.test.ts Outdated
Comment thread src/testing/testConfig.ts Outdated
Comment thread src/testing/cip56/transferSmoke.ts Outdated
Comment thread scripts/localnet-cloud.sh Outdated
Comment thread src/testing/testConfig.ts Outdated
Comment thread src/testing/localnetLedgerClients.ts Outdated
Comment thread src/testing/cip56/commands.ts Outdated
Comment thread scripts/localnet-cloud.sh Outdated
Continue role-check fallback across auth candidates and clear
memoized client promises on rejection. Preserve caller LocalNet env
overrides via bin defaults, honor FAIRMINT_TEST_* URL overrides,
cap retry sleep to the deadline, and harden Bash 3.2 / scoped npx
recursion guards.
Comment thread src/testing/testConfig.ts
URL-only FAIRMINT_TEST_*_URL overrides now use Keycloak password-grant
defaults instead of accidentally emitting a shared-secret bearer token.
Use base-unit decimal helpers instead of String(number)/toFixed(1),
run LocalNet smoke on PRs, and pin Splice/Scribe to MainNet 0.6.14.
Comment thread fixtures/dar-lifecycle/README.md Outdated
Comment thread test/integration/localnet/cip56-transfer.test.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 40 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

src/testing/cip56/numeric.ts:59

  • This separately exported formatter can bypass the parser and format a base-unit value wider than Daml Numeric's 38-digit precision. For example, formatDamlNumericFromBaseUnits(10n ** 38n, 10) returns an invalid 29-digit integer instead of rejecting it. Validate the base-unit width here as well.
  const digits = absolute.toString().padStart(scale + 1, '0');

src/testing/cip56/numeric.ts:43

  • This parser enforces fractional scale but not Daml Numeric's 38-digit precision. At the default scale 10, a 29-digit integer is accepted and then emitted by the command builders even though Numeric 10 permits only 28 integer digits, so the supposedly validated value is rejected only by the Ledger. Reject integer parts wider than 38 - scale.

This issue also appears on line 59 of the same file.

  if (fractionRaw.length > scale) {
    throw new Error(
      `Daml Numeric ${JSON.stringify(amount)} has more than ${scale} fractional digits`
    );
  }

Comment thread src/testing/cip56/numeric.ts
Comment thread fixtures/splice-test-token-v2/README.md Outdated
Comment thread scripts/build-dar-lifecycle-fixture.sh Outdated
Align integration assertions with normalized Numeric strings, reject
non-representable number amounts, and strip private/TODO fixture notes.
Comment thread scripts/build-dar-lifecycle-fixture.sh Outdated
Do not treat a missing Daml toolchain as a successful fixture build.
Comment thread bin/canton-dev-tools
Use return instead of exit so the RETURN trap removes incomplete clones.
Wire prepare-release with package.json floor / first-publish 0.1.0
behavior and publish.yml using trusted publishing (no NPM_TOKEN on publish).
Comment thread scripts/localnet-cloud.sh Outdated
Bash parses `set -eou pipefail` as `set -o u`, which fails and
applies none of the options. Use `set -euo pipefail` in Canton and
Splice healthcheck patches so failed http_check calls fail the script.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9bdb80e. Configure here.

Comment thread .github/workflows/publish.yml
Comment thread scripts/prepare-release.ts
Validator createUser maps insufficient reserved traffic to HTTP 429.
Wait and retry with a fresh party prefix until auto top-up lands so
CIP-56 LocalNet smoke is not flaky right after readiness.
Strip setup-node's empty _authToken line before npm publish so Trusted
Publisher OIDC can run. On first publish (no tags), use recent commit
history instead of empty main..HEAD.
@HardlyDifficult
HardlyDifficult merged commit 45c7909 into main Aug 10, 2026
3 checks passed
@HardlyDifficult
HardlyDifficult deleted the cursor/establish-canton-dev-tools-5279 branch August 10, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants