Canton Node SDK
Ledger operations
Active contract snapshots, async submit paired with completions, and WebSocket ledger updates using the Ledger JSON API client.
Read active contracts (ACS)
getActiveContracts uses the WebSocket implementation (the REST variant is intentionally not exposed). You can filter by parties and template IDs and optionally stream each item through onItem.
import { Canton } from '@fairmint/canton-node-sdk';
const canton = new Canton({ network: 'localnet' });
const acs = await canton.ledger.getActiveContracts({
parties: [canton.getPartyId()],
templateIds: [
// optional: restrict to known template IDs for your DARs
],
});
console.log('ACS item count', acs.activeContracts?.length ?? 0);
Expected outcome: A snapshot of active contracts at or before the resolved ledger offset; structure matches the generated JsGetActiveContractsResponse type (see package typings).
Submit a command and wait for completion
Typical flow:
- Read
getLedgerEndso you have an offset for the completions stream. - Call
asyncSubmitand readsubmissionIdfrom the response. - Pass
submissionId,partyId,userId, andbeginExclusive(from step 1) intowaitForCompletion.
import { Canton, waitForCompletion } from '@fairmint/canton-node-sdk';
const canton = new Canton({ network: 'localnet' });
const ledgerEnd = await canton.ledger.getLedgerEnd({});
const userId = canton.ledger.getUserId();
if (userId === undefined) {
throw new Error('Configure userId on CantonConfig or env before submitting commands.');
}
// `myCommand` must be a Json API `Command` value from your builders / openapi typings.
const submitted = await canton.ledger.asyncSubmit({ userId, command: myCommand });
const updateId = await waitForCompletion(canton.ledger, {
submissionId: submitted.submissionId,
partyId: canton.getPartyId(),
userId,
beginExclusive: ledgerEnd.offset,
timeoutMs: 120_000,
});
console.log('Completed update', updateId);
Define myCommand in your codebase using the generated Command schema from @fairmint/canton-node-sdk.
Expected outcome: updateId is the ledger update that committed your submission. On failure, the promise rejects with the completion error message.
For paidTrafficCost when the ledger reports it, use waitForCompletionWithMetadata (re-exported next to waitForCompletion).
Subscribe to ledger updates
subscribeToUpdates opens a WebSocket. Supply beginExclusive (defaults to current ledger end) and optionally endInclusive:
- Bounded — both offsets set → historical catch-up, connection closes at the end.
- Live — omit
endInclusive→ stream stays open for real-time updates.
import { Canton } from '@fairmint/canton-node-sdk';
const canton = new Canton({ network: 'localnet' });
await canton.ledger.subscribeToUpdates({
parties: [canton.getPartyId()],
onMessage: (msg) => {
console.log('update message', msg);
},
});
Expected outcome: Callback receives update envelopes until you close the subscription or the connection ends. Long-lived streams should implement token refresh handling via onTokenExpiring / onTokenRefreshNeeded (see TypeScript JSDoc on subscribeToUpdates).
See also
- Reference catalog — links to source files for
getActiveContracts,asyncSubmit,waitForCompletion,subscribeToUpdates. - Examples — runnable repo scripts.