OCP Canton SDK
Quickstart
Install the SDK, wire OcpClient, and read an issuer as typed Open Cap Format (OCF) data.
Install
npm install \
@open-captable-protocol/canton \
@fairmint/canton-node-sdk \
@fairmint/open-captable-protocol-daml-js
Before you run the example below, have two things ready:
- A running Canton node (or local sandbox).
- The issuer party ID (and the issuer contract ID when you want to read).
On-chain contracts (open source)
The ledger templates behind OCP live in open-captable-protocol-daml (wiki). NPM @fairmint/open-captable-protocol-daml-js ships typings built from that repo. See Architecture for how Canton, the contracts, and this SDK fit together.
Minimal example
Every cap table starts with the OCP Factory — a shared on-chain contract that registers your issuer party and grants it permission to create a cap table. The flow is three steps:
- Authorize — call the factory with your issuer party. You get back an
IssuerAuthorizationcontract. - Create — exercise that authorization to create the issuer and its cap table together.
- Read — fetch the issuer (and everything else) through
OcpClient.
On custom networks (localnet, staging): Before you can authorize issuers, you need to deploy a factory. On mainnet and devnet the factory is already deployed — skip this. For everything else, create one first:
import { createFactory } from '@open-captable-protocol/canton'; const factory = await createFactory(canton.ledger, { systemOperator: 'YOUR_SYSTEM_OPERATOR_PARTY', }); const ocp = new OcpClient({ ledger: canton.ledger, factory: { contractId: factory.contractId, templateId: factory.templateId }, });
import { Canton } from '@fairmint/canton-node-sdk';
import { OcpClient } from '@open-captable-protocol/canton';
const canton = new Canton({ network: 'localnet' });
const ocp = new OcpClient({ ledger: canton.ledger });
// Step 1 — register the issuer party with the OCP Factory
const authorization = await ocp.OpenCapTable.issuerAuthorization.authorize({
issuer: 'REPLACE_WITH_ISSUER_PARTY',
});
// Step 2 — create the issuer + cap table in one transaction
const { command, disclosedContracts } = ocp.OpenCapTable.issuer.buildCreate({
issuerAuthorizationContractDetails: authorization,
issuerParty: 'REPLACE_WITH_ISSUER_PARTY',
issuerData: {
id: 'issuer-1',
legal_name: 'Acme Corp',
country_of_formation: 'US',
formation_date: '2024-01-01',
},
});
await ocp.createBatch({ actAs: ['REPLACE_WITH_ISSUER_PARTY'] })
.addBuiltCommand({ command, disclosedContracts })
.submitAndWaitForTransactionTree();
// Step 3 — read it back
const { data: issuer } = await ocp.OpenCapTable.issuer.get({
contractId: 'REPLACE_WITH_ISSUER_CONTRACT_ID',
});
console.log(issuer.object_type); // ISSUER
Swap each REPLACE_* value for the real IDs from your environment. Once an issuer exists on the ledger you can skip straight to step 3.