Skip to content

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:

  1. Read getLedgerEnd so you have an offset for the completions stream.
  2. Call asyncSubmit and read submissionId from the response.
  3. Pass submissionId, partyId, userId, and beginExclusive (from step 1) into waitForCompletion.
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.