Skip to content

OpenCapTable Module

Cursor Agent edited this page May 4, 2026 · 10 revisions

OpenCapTable Module

This module contains the Open Cap Table Protocol (OpenCapTable) DAML implementation for the Canton Network. It mirrors the concepts and schema defined by the Open Cap Format (OCF) and is organized around the same core building blocks: objects and transactions using the schema's types and enums.

For shared coding guidelines that apply to all OCP DAML packages, see the Home wiki page.

Current version: OpenCapTable-v34

Companion NFT and other non-OCP packages are maintained in fairmint/daml; see Contract-Diagram and Home (@fairmint/daml-js). Old OpenCapTableNft* DARs under dars/ are historical backup bytes only.

Contract Architecture

See Contract-Diagram for visual documentation of the contract hierarchy and relationships.

Key components:

  • OcpFactoryIssuerAuthorizationCapTable (factory chain)
  • CapTable maintains Map Text (ContractId T) for all OCF objects (O(1) lookup)
  • All OCF contracts use dual signatories: issuer + system_operator

Schema Guidance

Source of Truth: Schema (@schema)

The single source of truth for all data structures is the JSON Schema under Open-Cap-Format-OCF/schema/ (@schema).

  • Strict adherence: All models, types, events, and files must strictly match the schema definitions
  • No drift: Do not introduce fields, values, or shapes that are not defined in the schema
  • Validation first: Any produced or ingested JSON must validate against the relevant schema

Implementation Guidance

  • Deprecated fields: Exclude deprecated schema fields from DAML. The SDK is responsible for mapping deprecated inputs to the latest standard.
  • Signatories: All OCP objects must use both the issuer and system operator as signatories.
  • Required numbers may be zero: A required numeric field may validly be 0 unless the schema specifies otherwise.
  • Code comments: Each non-test file starts with a one-line summary; the OCF object block (title, description, raw $id URL) sits immediately after module ... where, before imports. Field-level comments follow Home (DAML coding standards).

Package-Specific Notes

  • In DAML we exclude the object_type field since it is implied from the template used.
  • In StockIssuance, the schema requires stock_legend_ids with minItems: 1. Our implementation temporarily allows an empty array for stock_legend_ids to support existing data that lacks legends.

Field Ordering

All types/records in this package must follow the field ordering rule:

data Example = Example
  with
    -- Identifier for the object
    id: Text

    -- Required fields (alphabetical)
    ---------------------------------
    required_a: Text
    required_b: Int

    -- Arrays (alphabetical)
    ---------------------------------
    items: [Text]

    -- Optional fields (alphabetical)
    ---------------------------------
    optional_a: Optional Text
    optional_b: Optional Int
  deriving (Eq, Show)

Declaration Order Within Template Files

After imports, use: template block first, then the main OCF data record for that template, then subtype/helper data and validators. See Home for the full header/import layout.

Field-Level Comments

For every field, add high-signal comments derived from the schema: field title, then description, then $comment if present. Include constraints like -- minItems: 1 for arrays when relevant.

Example for ObjectReference:

-- Type - Object Reference
-- A type representing a reference to any kind of OCF object
-- OCF: https://raw.githubusercontent.com/Open-Cap-Table-Coalition/Open-Cap-Format-OCF/main/schema/types/ObjectReference.schema.json
data OcfObjectReference = OcfObjectReference
  with
    -- Identifier of the referenced object
    object_id: Text
    -- Type of the referenced object
    object_type: OcfObjectType
  deriving (Eq, Show)

Optional Text Validation

For every Optional Text field, validate with validateOptionalText in the record validator. This ensures that when a value is provided it is non-empty.

Usage Guidance

  • Issuer Management: The Issuer object is the primary object for managing the cap table.
  • Edits: When an object needs to be edited, the original contract(s) should be archived with ArchiveByIssuer choice and then a new contract should be created with the new data. Ideally both commands are bundled into a single transaction so that the edit is atomic.

Clone this wiki locally