npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bootnodedev/canton-wallet-service

v0.3.0

Published

JSON-RPC bridge between a CIP-0103 Canton wallet and a Canton participant

Readme

Canton Wallet Service

Express JSON-RPC bridge between a CIP-0103 wallet and a Canton participant.

It is intentionally app-agnostic: app-specific Daml commands come from the consumer, the wallet owns signing and approval UI, and this service only handles Canton connectivity, participant reads, prepared transaction execution, and wallet-internal party onboarding.

Install

pnpm add -D @bootnodedev/canton-wallet-service
pnpm exec canton-wallet-service

The package ships dist/ prebuilt, so nothing of its own runs at install time. pnpm may still stop twice, for reasons outside this package:

  • protobufjs, a dependency of the Canton SDK, has an install script that only prints a version warning. Allow or deny it with pnpm approve-builds; pnpm 12 refuses pnpm exec until that is decided.
  • pnpm 12 holds back versions published less than a day ago (minimumReleaseAge). To take a release the same day, list this package under minimumReleaseAgeExclude or lower the setting.

Pin the exact version: a release is cut whenever the wire surface changes.

Configuration is environment-only (see .env.example), so a consumer supplies it however it already supplies env to its own processes.

Run

Requires Node 24 (see .nvmrc) and a reachable Canton participant.

pnpm install
cp .env.example .env   # then fill CANTON_BACKEND_TOKEN
pnpm run dev           # or: pnpm run build && pnpm start
curl -fsS http://localhost:3010/health

The image builds from this repository root and needs no wider build context:

docker build -t canton-wallet-service .

docker-compose.yml builds that image and reads an .env beside it, which is how the service is deployed on a host of its own:

cp .env.example .env   # then fill in the credentials for that network
docker compose up --build -d

Token

Real Canton calls require a bearer token the participant's ledger API accepts. This service never mints one from a signing recipe; it takes credentials one of two ways, and refuses to start with neither.

Static token. CANTON_BACKEND_TOKEN is a token minted wherever the participant's signing recipe lives — on Splice LocalNet, a dev JWT with subject ledger-api-user. This is the LocalNet path and needs nothing else.

OAuth client credentials. A hosted validator issues short-lived tokens, so setting any EXTERNAL_OAUTH_* variable switches the service to fetching one itself by client-credentials grant, caching it, and refreshing a minute before it expires. CANTON_BACKEND_TOKEN is then neither read nor needed.

| Variable | Purpose | | ------------------------------ | ---------------------------------------------- | | EXTERNAL_OAUTH_TOKEN_URL | The provider's token endpoint. | | EXTERNAL_OAUTH_CLIENT_ID | Machine-to-machine client id. | | EXTERNAL_OAUTH_CLIENT_SECRET | Its secret. Never comes from a preset. | | EXTERNAL_OAUTH_SCOPE | Usually daml_ledger_api. | | EXTERNAL_OAUTH_AUDIENCE | Optional. Sent only when set, for providers that gate on aud. | | EXTERNAL_PRESET | Optional. Supplies defaults for a known validator. |

On the OAuth path no localhost default is allowed to stand in for an endpoint: CANTON_JSON_API_URL and the three SPLICE_* URLs must name the hosted validator, whether from the environment or from EXTERNAL_PRESET. Missing, malformed, or loopback values fail at startup naming the variable rather than at the first Canton call — a service that boots green and refuses every Canton call is the worse outcome.

A preset (src/presets/) is checked-in source, so it carries endpoints and public OAuth fields only, never the secret. It fills what it knows and no more: fivenorth has no published scan host, so SPLICE_SCAN_API_URL still has to be supplied alongside it.

API Boundary

The public dApp surface is CIP-0103. The wallet exposes that provider to dApps; this service exposes only the HTTP JSON-RPC bridge the wallet needs at:

POST /rpc

Service-specific methods:

| Method | Caller | Purpose | | -------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | prepareTransaction | the wallet | Calls Canton interactive submission prepare and returns the prepared transaction payload/hash for local signing. | | executePrepared | the wallet | Submits the wallet's signature over a prepared transaction to Canton. | | ledgerApi | the wallet on behalf of the dApp | Proxies app-user JSON API reads/writes and injects the Canton bearer token. resource must resolve to the configured JSON API origin; anything off it is refused with -32602, since the injected token would otherwise travel with it. |

CIP-56 token methods

These add Canton token-standard reads and transfers plus Amulet (Canton Coin) preapproval. They are token-standard / Amulet logic, not consumer-dApp logic.

| Method | Purpose | | --- | --- | | cip56.listHoldingSummary | Per-instrument token balance summaries for a party, from the participant's active contract snapshot. | | cip56.listHoldings | Raw token holding UTXOs for a party. | | cip56.listPendingTransfers | Pending incoming CIP-56 transfer instructions for a party. | | cip56.createTransfer | Prepares a token transfer for the caller to sign and execute. | | cip56.acceptTransfer | Prepares acceptance of a pending incoming transfer. | | amulet.preapproval.status | Reads the Amulet transfer-preapproval (auto-accept) status for a receiver. | | amulet.preapproval.create | Prepares enabling Amulet auto-accept. | | amulet.preapproval.cancel | Prepares disabling Amulet auto-accept. | | amulet.preapproval.acceptProposal | Accepts a TransferPreapprovalProposal for the receiver. | | amulet.tap | Prepares the fixed 100 AMT Splice DevNet faucet tap for a receiver (DevNet only). |

The write methods (create*, acceptTransfer, amulet.preapproval.create/cancel/acceptProposal, amulet.tap) return prepared transactions; the wallet signs locally and submits via executePrepared.

prepareExecute, prepareExecuteAndWait, and signMessage stay in the wallet because they require the user's key and approval UI.

For ledgerApi semantics, read the upstream spec instead of duplicating it:

Admin Endpoints

External party onboarding is wallet/provider operational logic, not generic dApp API. See CIP-0103 topology-related capabilities.

The wallet uses these wallet-internal endpoints:

| Endpoint | Purpose | | ---------------------------- | ---------------------------------------------------------------------------------------------------- | | POST /admin/party/prepare | Prepares the external party topology transaction and returns { onboardingId, partyId, multiHash }. | | POST /admin/party/complete | Submits the signed topology transaction, grants user rights, and returns the created party. |

These endpoints stay outside /rpc so the dApp API remains a projection of the CIP/OpenRPC surface.

Releasing

Merging a version bump to main is the release. Nothing is published from a laptop.

pnpm release minor                 # or patch / major: branch release/X.Y.Z, commit "chore: release X.Y.Z"
git push -u origin release/X.Y.Z   # open the pull request, wait for the checks, merge

On the merge, .github/workflows/release.yml sees a version that is not on npm yet, runs the same gates as a pull request, publishes, and creates the tag vX.Y.Z and the GitHub Release with notes since the previous tag. Ordinary merges change nothing. A run that failed halfway can be re-run: what already exists is skipped.

Publishing needs no token. npm's trusted publishing accepts the identity GitHub gives the run, registered on npmjs.com under the package's settings as GitHub Actions, repository BootNodeDev/canton-wallet-service, workflow release.yml. Provenance is attached automatically.