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.
Not published to npm yet. Consume it from git, pinned to a tag. No dist/ is
committed, so the binary comes from the package's own prepare build — and pnpm
blocks a dependency's build scripts by default, so it has to be allowed through
before the install produces anything runnable:
pnpm add -D "git+ssh://git@github.com/BootNodeDev/canton-wallet-service.git#v0.1.1"
pnpm approve-builds # allow this package's `prepare`, then re-install
pnpm exec canton-wallet-servicepnpm matches a git dependency by its resolved tarball id, so the allowance is keyed to a commit sha and moving the tag means re-approving.
Configuration is environment-only (see .env.example), so a consumer supplies
it however it already supplies env to its own processes.
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/healthThe image builds from this repository root and needs no wider build context:
docker build -t canton-wallet-service .Real Canton calls require a bearer token the participant's ledger API accepts.
This service does not mint one at boot: it requires CANTON_BACKEND_TOKEN and
refuses to start without it. Mint it wherever the participant's signing recipe
lives; on Splice LocalNet that is a dev JWT with subject ledger-api-user.
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 CANTON_BACKEND_TOKEN. |
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 (Amulet summaries via scan proxy; other tokens via holding UTXOs). |
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:
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.