Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compatibility matrix

What this SDK is tested against, and what it therefore claims. Nothing is listed on the strength of “it should work” — but not everything is checked by CI on every push, and the table under What CI runs, and what it cannot says exactly which rows run only in the nightly LocalNet workflow.

Version 0.3.2, released 2026-10-23 (the review fixes over 0.3.1: contract keys under hashing scheme V3, point reads, transport-neutral update types).

Rust

ToolchainStatusWhere it is checked
1.88 (MSRV)supportedthe msrv CI job builds the whole workspace with it
stablesupportedevery other CI job
beta / nightlyuntested—

The MSRV is workspace.package.rust-version. Raising it is a minor version bump, not a patch.

Platform

TargetStatusWhere it is checked
Linux (ubuntu-latest)supportedthe test job’s matrix
macOS (macos-latest)supportedthe test job’s matrix
Windows (windows-latest)supportedthe test job’s matrix
aarch64-unknown-linux-gnucompilesthe cross-targets job (cargo check --workspace --all-features); no test run
x86_64-unknown-linux-muslcompilesthe cross-targets job; no test run

The whole test suite runs on all three. The live suites need a Canton participant; CI runs them against LocalNet in the LocalNet workflow, nightly and on demand — see below.

Canton and the Ledger API

Ledger APIv2
Canton3.5.7 — the release the .proto files are vendored from
Verified againsta Canton 3.5.7 participant (LocalNet): submission, streaming, recovery, TLS/mTLS, auth, typed end-to-end on both transports, and interactive submission with an externally-signed party, including contract keys under hashing scheme V3. A Canton 3.5.17 and, since 0.3.2, 3.6.3 participant on the Canton Network DevNet: the token-standard workflows, the ledger live suite, interactive submission under V2 and V3, JSON package and party reads, PQS over TLS, gRPC over TLS with a Keycloak token
Run in CIthe LocalNet workflow boots canton-localnet v0.8.4-3, which vendors Splice 0.8.4, and runs the live suites against it nightly and on demand. The Canton participant is the one that Splice release ships; the workflow does not pin it, and the 3.5.7 row above is the developer-run verification

Moving the supported Canton range re-vendors the protos in a new SDK minor. See the stability policy in canton-proto and ADR-0002.

Daml-LF

VersionStatus
LF 2.1supported
LF 2.2supported
LF 2.3supported since 0.3.1 (the schema is Canton 3.5.17’s; a DevNet participant vets Digital Asset’s utility apps and daml-stdlib 3.5.1 at this version)
LF 2.x, other minorsrefused, naming the minor and this build’s range
LF 1.xrefused, naming the major

An LF 1 archive carries minors like 15, so judging it by the minor gate would report “Daml-LF 2.15 is unsupported” and send a reader to upgrade an SDK that will never read it. The major is checked first for that reason.

The decoder is held to the official JVM reader by a conformance oracle — the lf-conformance CI job.

Token standard

VersionTypesWorkflow
CIP-56 (-v1)generated: holding, metadata, transfer instruction, allocation, allocation instruction, allocation request, burn/mint, featured appcanton-token: registry client, choice contexts with disclosure, TransferFactory_Transfer, accept/reject/withdraw, allocate + execute/withdraw/cancel
CIP-0112 (-v2)generated: holding (with Account), transfer instruction, transfer events (EventLog), allocation (with SettlementFactory, Allocation_Settle), allocation instruction, allocation requestcanton_token::v2: the V2 registry paths, TransferFactory_Transfer over accounts, accept/reject/withdraw, allocate, settle_batch, allocation and allocation-instruction choices, and events::holdings_changes for reading what moved

V2 reuses splice-api-token-metadata-v1; there is no metadata-v2.

Which standard each crate targets, crate by crate:

CrateStandardWhat it holds
canton-splice-api-token-metadata-v1V1 and V2 (shared)ChoiceContext, ExtraArgs, Metadata, AnyValue
canton-splice-api-token-holding-v1CIP-56 V1Holding, InstrumentId, Lock
canton-splice-api-token-transfer-instruction-v1CIP-56 V1TransferFactory, TransferInstruction
canton-splice-api-token-allocation-v1CIP-56 V1Allocation, SettlementInfo, TransferLeg
canton-splice-api-token-allocation-instruction-v1CIP-56 V1AllocationFactory, AllocationInstruction
canton-splice-api-token-allocation-request-v1CIP-56 V1AllocationRequest
canton-splice-api-token-burn-mint-v1CIP-56 V1BurnMintFactory
canton-splice-api-featured-app-v1V1 (featured-app API)FeaturedAppRight, activity markers
canton-splice-api-token-holding-v2CIP-0112 V2Holding, Account, InstrumentId, Lock
canton-splice-api-token-transfer-instruction-v2CIP-0112 V2TransferFactory, TransferInstruction over accounts
canton-splice-api-token-transfer-events-v2CIP-0112 V2EventLog, the transfer events events::holdings_changes reads
canton-splice-api-token-allocation-v2CIP-0112 V2Allocation, SettlementFactory, SettlementInfo, TransferLegSide
canton-splice-api-token-allocation-instruction-v2CIP-0112 V2AllocationFactory, AllocationInstruction
canton-splice-api-token-allocation-request-v2CIP-0112 V2AllocationRequest
canton-splice-amulet, canton-splice-wallet, canton-splice-wallet-paymentsV1 and V2 — Amulet implements boththe Splice application packages, referencing the crates above rather than copying them
canton-tokenV1 (canton_token::*) and V2 (canton_token::v2)the workflows

Verified against live registries — the Splice scan of a cn-quickstart LocalNet (Splice 0.6.11), the Scan of a Canton Builder Tool LocalNet (the three-participant settlement), and the public Scan of the Canton Network DevNet (Splice 0.8.1). The instrument on both is Amulet (Canton Coin), which declares both standards (splice-api-token-transfer-instruction-v1 and -v2). That is a V2 implementation, exercised as one. The registry’s OpenAPI documents at 0.8.1 differ from the vendored 0.6.11 copies by one additive field, which the client already reads.

On the “V2 reference token”. Splice documented a separate Token Standard V2 DevNet — a temporary, single-SV network run by Digital Asset for organizations validating the V2 standard, with a public scan and a weekly Monday reset. It appears to have been wound down: the document’s version line was bumped weekly through 23 June 2026 and has not moved since, and the network’s hostnames are absent from the certificate its address now presents (a valid certificate covering the ordinary DevNet names and no token-std-v2-dev name at all — a retired ingress rather than an IP allowlist, which would answer 403 over a good handshake). V2 has meanwhile landed in the ordinary stack, which is why a plain LocalNet’s Amulet declares every V2 API. So the runs below are against a V2 implementation on a normal network, which is the target that exists.

WhatLocalNet (Canton 3.5.7)DevNet (Canton 3.5.17)
V1 transfer, end to endcommitted at offset 67684, 6 events, kind offercommitted at offset 3316298, kind direct — settled on submission
V2 Account-based transfercommitted at offset 67690, 6 eventscommitted at offset 3316304, direct
V2 event parsing (events::holdings_changes) on a committed transactionone holdings change: 1 spent, 2 produced, 2 legstwo holdings changes: 1 spent / 1 produced, 0 spent / 1 produced
V2 allocation, three distinct parties (sender / receiver / executor)allocated at offset 67693, 6 events, 3 contracts createdallocated at offset 3316319, executor the validator’s party
V2 allocation withdrawn by the sender—withdrawn at offset 3316364; the reserved coin released
V2 allocation settled by its executor (settle_batch)settled by a third party on a three-participant LocalNet (Canton Builder Tool, 2026-10-13): sender, receiver and executor each on its own participantsettled at offset 3317221 through Amulet’s settlement factory; a one-sided leg was refused first, as the standard requires

The runs are kept verbatim, with their environment and commands, in docs/verification/token-standard-live-runs.md; the DevNet update ids were read back from the participant by id after the run.

The allocation is the half that needs more than two parties: the sender reserves holdings for a settlement a third party — the executor — completes later, which is what delivery-versus-payment is built from. On the LocalNet the three roles are three parties on three participants, each submitting with its own participant’s token, and the executor settled both sides of the leg as one batch (the run of 2026-10-13); on DevNet, where one token was held, the settlement was run with the sender as executor and both sides of the leg in one allocation, after the settlement factory had refused a one-sided leg. Both are on record.

Each run resolved the factory against the registry and submitted with the contracts it named for disclosure, so the choice-context and createdEventBlob → DisclosedContract path are proven against a real registry rather than a stub.

The stub has its place too, and CI runs it: every workflow function in canton-token — V1 and V2 transfer, the instruction choices, allocation, settle_batch, the allocation and allocation-instruction choices — is driven against an in-process registry that answers what the OpenAPI documents say and records what it was asked, and the holdings read is driven against an in-process StateService. What those pin is the request each workflow sends and that the registry’s context lands inside the exercise argument, which is the part a mutation run found nothing else guarding.

The V1 packages come from cn-quickstart at a pinned commit, fetched in CI and checked against their SHA-256. The V2 packages ship as no DAR anyone publishes, so they were taken from a participant and committed under testdata/token-standard-v2, where each file name ends with the package id that hashes its bytes.

PQS

Scribe3.5.4
Verified againsta running store: 969 active contracts read as typed payloads, payload and party-column predicates, containment, lookup by id, pinned-offset reads

What CI runs, and what it cannot

SuiteCINeeds
unit, in-process, TLS, WebSocketyesnothing
submit → observe → query, on gRPC and on JSON, as one flowyes, against an in-process participant (submit_observe_query_* in canton-ledger’s tests)nothing; the same flow runs against a real participant in the live suites (create_contract_and_read_transaction, json_submit_and_read_back)
conformance (canton-conformance)yesnothing
bindings drift, eighteen of the nineteen generated cratesyesthe pinned DARs, fetched and checksummed; the V2 and stdlib crates need nothing. canton-quickstart-licensing is the exception — its DAR is built from source, so it is guarded locally and not in the drift job; the DAR is committed under testdata/live for the LocalNet workflow
Daml-LF conformance oracleyesa JVM
live Ledger API, interactive submissionyes, against LocalNet in the LocalNet workflow (nightly, on demand)a Canton participant
live admin (canton-admin)yes, against LocalNet in the LocalNet workflow (nightly, on demand); the vetted(synchronizer) branch of the topology test does not run, because CANTON_TEST_SYNC_ID is unseta Canton participant with its admin API reachable
live PQSyes, against LocalNet in the LocalNet workflow (nightly, on demand)a Scribe store following the participant — tools/pqs/compose.yaml runs one against any LocalNet on the host
token-standard registryyes, against LocalNet in the LocalNet workflow (nightly, on demand)a Splice Scan — the super-validator runs one: a Splice LocalNet or Canton Builder Tool publishes it at scan.localhost:4000; cn-quickstart with SV_PROFILE=on serves it on :5012 without publishing the port. The public DevNet Scan serves the same paths unauthenticated

The LocalNet workflow sets CANTON_TEST_REQUIRE_LIVE, so a suite that cannot reach its environment fails rather than skips, and it asserts each suite’s exact passed count and zero ignored tests: ledger 39, interactive 5, admin 5, PQS 9, token 5. One PQS test skips without that guard when the store holds no Amulet, so the workflow also requires the line that test prints when it compares. Outside CI, a suite gated on an environment variable skips when the variable is unset and fails when it is set and the connection fails — a connection failure and an unconfigured machine used to look the same in a green run, and they are not the same thing.

Conformance to the Ledger Client Standard

conformance/capabilities.toml lists every capability of the standard this SDK claims, and canton-conformance has one test named for each; conformance/ledger-client-standard-mapping.md is the same map for reading, row by row, with the rows the grant does not claim. A completeness guard asserts the two agree in both directions: no capability without a test, and no test claiming a capability the registry does not list.

One row is worth reading carefully. Contract keys are covered by a real keyed DAR, testdata/contract-keys (Daml-LF 2.3, six keyed templates): the conformance row generates bindings from it, canton-codegen’s compile test builds them and an exercise-by-key command from them, and the lf-conformance job holds the decoder to the JVM reader on it. A transaction that uses keys is signed under hashing scheme V3, which the SDK recomputes like V2.