Skip to content

OCP Canton SDK

Workflow recipes

Practical patterns for OcpClient — Canton wiring, cap table batches, streams, payments, valuation, and validation.

Install peers first: @fairmint/canton-node-sdk (transport) and @open-captable-protocol/canton (this library). The Canton Node SDK owns OAuth, JSON API commands, and Validator calls; this package layers OCF projections and DAML command builders on top.

Wire OcpClient with Canton

import { Canton } from '@fairmint/canton-node-sdk';
import type { DisclosedContract } from '@fairmint/canton-node-sdk/build/src/clients/ledger-json-api/schemas/api/commands';
import { OcpClient, toContractId, toPartyId } from '@open-captable-protocol/canton';

const canton = new Canton({ network: 'localnet' });
const ocp = new OcpClient({
  ledger: canton.ledger,
  validator: canton.validator,
});

// FeaturedAppRight must be loaded as a disclosure from your onboarding / allocation flow.
const featuredAppRight: DisclosedContract = {
  templateId: 'REPLACE_WITH_TEMPLATE_ID',
  contractId: 'REPLACE_WITH_CONTRACT_ID',
  createdEventBlob: 'REPLACE_WITH_CREATED_EVENT_BLOB',
  synchronizerId: 'REPLACE_WITH_SYNCHRONIZER_ID',
};

ocp.context.setFeaturedAppRight(featuredAppRight);
ocp.context.setIssuerParty(toPartyId('REPLACE_WITH_ISSUER_PARTY'));

Expected outcome: ocp.context.requireFeaturedAppRight() and requireIssuerParty() succeed; subsequent namespace calls that depend on context stop throwing OcpValidationError for missing cache entries.

The REPLACE_* strings mark data you must obtain from your environment (ledger query + disclosure payload) — they are not valid runtime IDs.


Create issuer and read as OCF

After an issuer contract exists on-ledger, read it through the OpenCapTable namespace:

const { data: issuer, contractId } = await ocp.OpenCapTable.issuer.get({
  contractId: toContractId('REPLACE_WITH_ISSUER_CONTRACT_ID'),
  readAs: [toPartyId('REPLACE_WITH_READER_PARTY')],
});

console.log(issuer.object_type); // ISSUER

Expected outcome: issuer matches the OCF ISSUER schema; failures surface as ContractResult errors or OcpContractError when the contract is missing or not visible.

Creation flows use issuer.buildCreate — exact arguments follow your DAML package; keep editor TypeScript preview open beside Reference entries.


Batch cap-table update

OpenCapTable.capTable.update(...) returns a CapTableBatch:

const batch = ocp.OpenCapTable.capTable.update({
  capTableContractId: 'REPLACE_CAP_TABLE_CID',
  actAs: ['REPLACE_ISSUER_PARTY'],
});

// entity tags follow OCF: 'stakeholder', 'stockClass', 'stockIssuance', ...
batch.create('stockClass', stockClassOcfPayload);
batch.create('stockIssuance', stockIssuanceOcfPayload);

await batch.execute();

Replace stockClassOcfPayload / stockIssuanceOcfPayload with real OCF objects validated by convertToDaml / entity validators before submission where applicable.

Expected outcome: The batch submits through the injected ledger client using the negotiated command payloads; synchronous failures bubble up as Canton ApiError / OCP errors depending on validation stage.

Tune row payloads using convertToDaml helpers when bridging spreadsheet / CSV imports — see converters under src/functions/OpenCapTable in GitHub.


Archive cap table

Use archiveCapTable / archiveFullCapTable for lifecycle teardown. Parameters differ by governance — consult TypeScript typings for ArchiveCapTableParams.

Expected outcome: Commands archive the on-ledger cap table template instances; always confirm downstream readers before archival in production environments.


Payment streams

ocp.PaymentStreams exposes factory + lifecycle helpers. Several code paths enumerate disclosed contracts via ValidatorApiClient (Amulet context). Instantiate OcpClient with validator: canton.validator when exercising those helpers.

Expected outcome: Disclosure-heavy calls succeed once validator endpoints can load wallet-linked contract metadata expected by Canton Network deployments.


Payments and airdrops

ocp.CantonPayments groups airdrop and lifecycle command builders mirroring Canton payment contracts. Compose with CantonPayments + batch execution identical to cap-table batches when multiple commands must commit atomically.


Valuation report

ocp.OpenCapTableReports mirrors company valuation DAML choices: build → add observers → create/update. Follow generated types for buildCreateCompanyValuationReportCommand outputs.

Expected outcome: New valuation contracts appear on-ledger tied to issuer / stakeholder metadata enforced by converters.


Debug converter validation

Boundary validation mixes:

  • entityValidators helpers for ISSUER / stakeholder structs.
  • Exported Zod schemas (utils) for hostile JSON ingestion.
  • Lower-level convertToDaml traces when batch translations fail tests.

Workflow: reproduce the smallest OCF snippet in a unit test, call the explicit converter (*ToDaml), and read OcpValidationError.details — the error hierarchy is listed in Reference → Errors.


See also

  • Quickstart — dependency versions
  • Guides hub — contributor LocalNet/testing
  • Architecture — layering diagrams
  • Canton transport docs live with @fairmint/canton-node-sdk (repository README + wiki)