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:
entityValidatorshelpers for ISSUER / stakeholder structs.- Exported Zod schemas (
utils) for hostile JSON ingestion. - Lower-level
convertToDamltraces 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)