We are building otcDigital, a multi-participant RWA marketplace on Canton connecting buy-side institutions, sell-side firms, qualified custodians, transfer agents, and FMIs. We have now completed a fuller implementation of
CIP-0112 and also reviewed the
OpenZeppelin settlement experiment in Daml as a reference. Both point to the same structural gap, which I want to raise here.
The gap: Account is a data struct, not an on-ledger objectIn
CIP-0112 today,
Account is a plain data record —
{ owner: Optional Party, provider: Optional Party, id: Text } — embedded inside HoldingV2.HoldingView, AllocationV2.AllocationSpecification, and TransferLeg. The
OpenZeppelin (OZ) experiment follows the same pattern in its fixture types.
An account therefore has no independent existence on the ledger. The practical gaps this creates:
- No account-level lifecycle. There is no contract to represent an account being opened, suspended, closed, or transferred between custodians. A regulatory freeze must touch every individual holding rather than act on the account itself.
- No pre-position existence. An account cannot be opened before any positions are taken, nor formally closed as a ledger event. Account existence is inferred from holdings — the inverse of how custody works.
- No classification anchor. Accounts differ by type (client custody, affiliate, proprietary, internal), by instrument category (cash, futures, options, swaps), by product type (equities, bonds, FX), and by regulatory category (SEC 144A, Reg D/S, 15c3-3). None of this is representable in the current data struct.
- Preapproval limitation. Canton preapprovals are party-to-party and cannot distinguish two segregated accounts of the same investor at the same custodian, because
Account.id plays no role in the preapproval mechanism.
What we observed in OZ's settlement experimentThe OZ canton-specs experiment has Account as a plain data type in its local fixture — identical to the
CIP-0112 pattern. The experiment does have sophisticated features we do not yet have: a two-phase AllocationInstruction/Allocation flow, Optional Lock on holdings, D1 compliance attestation hooks, D2 lawful seizure, SettlementReceipt, and a TrustedAttesterRegistry. These are features we intend to adopt into our Splice-based implementation. However, Account remains a data struct throughout — compliance and seizure operations act on individual Allocation contracts, not on the account itself.
What we would miss if we replaced Splice with OZ's implementationWe investigated whether switching from Splice DARs to OZ's fixture types would give us a production-viable path. It would not, and the reason is fundamental. OZ's fixture types carry different Daml package IDs from Splice's HoldingV2, AllocationV2, and related interfaces. Canton's type system uses package hashes in interface IDs. A counterparty running Splice-based holdings cannot pass their contracts as inputs to OZ-typed allocations, and vice versa. We assume that OZ's fixture exists precisely because they cannot redistribute Splice DAR binaries in an open-source repository — it is a licensing constraint, not a design alternative.
In production on the Canton Global Synchronizer, all participants hold Splice-typed holdings. Switching to OZ types would isolate us to a private network where every participant also runs OZ fixture types — a network that does not exist in production. We would additionally lose Canton Coin for network fees, the Validator and Super Validator infrastructure, and the Scan API.
The correct approach is to implement OZ's pattern improvements — holding locks, the instruction step, compliance hooks, event receipts — on top of Splice types. Our phased upgrade plan does exactly this. OZ is a reference implementation that shows what Splice-based contracts should look like; it is not a replacement for Splice.
This observation also informs the Account question directly. A Daml contract solution — an AccountV1 interface in the Splice framework — would have the same structural ceiling: standardized within the Splice ecosystem, fragmented everywhere else. A ledger-level construct would cross that boundary and benefit all Canton implementations regardless of whether they use the Splice token standard.
Our proposal: a first-class Account contract
We propose an Asset Holding Account contract representing the bilateral custody relationship between an investor and their custodian.
Signatory: custodian (provider) only. Observer: investor (owner).
The account agreement is reached via our L2 workflow — off-chain KYC, account opening, investment mandate — after which the custodian creates the account on-ledger unilaterally. This reflects how qualified custody operates in practice: the custodian is the legal operator; the investor is the beneficial owner with contractual but not operational rights on-chain.
A sketch:
template Account
with
owner : Party -- investor; observer only
provider : Party -- custodian; sole signatory
accountId : Text
accountType : AccountType
instrumentCategory : Optional Text -- e.g. "cash" | "futures" | "options" | "swaps"
productType : Optional Text -- e.g. "equities" | "bonds" | "FX" | "crypto"
regulatoryCategory : Optional Text -- e.g. "SEC-144A" | "Reg-D" | "Reg-S" | "15c3-3"
status : AccountStatus
openedAt : Time
where
signatory provider
observer owner
choice Account_Suspend : ContractId Account controller provider
choice Account_Reinstate : ContractId Account controller provider
choice Account_Close : () controller provider
choice Account_TransferCustody : ContractId Account controller provider
data AccountType
= ClientCustody -- external investor at a qualified custodian
| Affiliate -- related-party or affiliated entity account
| Proprietary -- firm's own book / principal positions
| Internal -- between internal desks
The three classification fields use Optional Text so application providers can record and maintain their own value sets without a DAR upgrade — the standard determines the axes, implementations determine the values. Admin is deliberately excluded: account is purely a custodian-to-investor relationship and orthogonal to instrument registry concerns.
Why custodian-only signatoryWe concluded against bilateral signatories for three reasons.
First, regulatory. A qualified custodian must be able to freeze, suspend, or transfer custody when legally obliged, without requiring the investor's on-chain co-signature. If the investor is a signatory, every account-level operation involves them as a contracting party — a dependency custodians and their counsel may not accept. The custodian's unilateral operational control reflects their legal position under custody regulation.
Second, infrastructure. Making the investor a signatory means every investor needs a participant node with KMS online for every account operation. As an observer, an investor with a Canton node receives automatic disclosure. An investor without a Canton node accesses their account via their custodian's portal — a realistic model for institutional and retail clients alike.
Third, internal accounts. Where a bank's front office and back office are the same Canton party, owner and provider are identical. With custodian-only as signatory, the account degenerates cleanly to a single-signatory record — precisely what an internal bank account is. No "contract with yourself" problem, no forced party-splitting that banks would object to.
Questions for the group1. Is a first-class on-ledger Account contract the right direction? Are others implementing qualified custody on Canton running into the same absence?
2. Should this be a ledger construct or a Daml contract construct? Given that a Daml/Splice contract construct would not benefit implementations outside the Splice ecosystem — as the OZ/Splice package-ID incompatibility illustrates — is Account identity something that belongs in Canton's participant node and synchronization protocol, analogous to party identity and domain membership? Or is a new Daml interface within the Splice token standard the right scope?
3. If a Daml contract construct, would the Foundation consider defining an AccountV1 interface as part of the Splice token standard, so account lifecycle operations (open, suspend, close, custody transfer) are interoperable across Canton participants?
4. How should holdings reference accounts — by ContractId Account (strong cryptographic binding, but fragile across account upgrades and custody transfers) or by an account key tuple (looser, but more resilient to lifecycle events)?
5. On the broader OZ/Splice fragmentation: we observe that the open-source community cannot use Splice DAR binaries, so any interface defined in the Splice framework is unavailable to them at the type level. Does the Foundation have a path toward allowing open-source Canton implementations to interoperate with Splice-based production networks without redistributing Splice DAR binaries? This question is relevant to Account interoperability but extends beyond it.
Keen to hear from others building custody and settlement workflows on Canton.
Thanks,
Mani Pillai, mani@...