Token (CIP-0112)
The token package implements the Token Standard V2 interfaces from CIP-0112. It provides holdings, transfer instructions, allocations with batch settlement, allocation requests, registry rules, event logging, compliance hooks, and CIP-0086 allowances. Its closest Solidity analogue is a partial ERC20.
The package has no dependency on other OpenZeppelin packages. It depends on the Token Standard V2 interface DARs.
Experimental. Version 0.x, unaudited, and subject to a redesign before
release. The upstream Token Standard V2 interfaces are at devnet stage. The
interface DARs in dars/vendor/ are local builds from a pinned
splice commit, and every
package ID changes when upstream publishes a release. Source:
experiments/token/tokenCIP112-v1
in OpenZeppelin/canton-contracts.
Modules
The package has no root module. Import the submodules that you need, for example:
import OpenZeppelin.TokenCIP112V1.HoldingEach module implements the matching Splice.Api.Token.*V2 interfaces.
| Module | Templates | Purpose |
|---|---|---|
.Holding | TokenHolding | An asset holding, maintained jointly by the instrument admin and the account parties, with an optional lock. |
.Transfer | TokenTransferInstruction | The transfer-instruction lifecycle: accept, reject, withdraw, and expire. |
.Allocation | TokenAllocation, BatchSettlementAuthorization | Allocations backed by locked holdings, and all-or-nothing batch settlement. A batch must exactly cover its allocations. |
.AllocationRequest | TokenAllocationRequest | The request from an application that a wallet turns into an allocation. |
.Allowance | TokenAllowance | A CIP-0086 spending budget with ERC-20 approve and transferFrom behavior. |
.Registry | TokenRules | The registry rules: transfer, allocation, and settlement factories, plus mint, burn, and allowance choices. |
.Base | TokenEventLog | The event log for holding changes. |
.D1 | TrustedAttesterRegistry, ComplianceAttestation, SeizureOrder | Compliance attestation (D1) and lawful-process seizure authority (D2). |
The package defines no Daml interfaces or exceptions of its own, so it ships as one implementation package.
Authority
- The instrument admin and the account parties jointly maintain holdings.
TokenRules_MintandTokenRules_Burnneed both. - Wallets act through the Token Standard V2 interface choices. The choices check identity, funding, and expiry before they move value.
- The application selects and discloses the canonical
TokenRulescontract for its instrument. - If
allowedExecutorsonTokenRulesisNone, any party can be named as a settlement executor. Set it when that matters for your deployment.
Compliance Hooks
- D1 attestation. If
requiredAttesterRegistryCidis set onTokenRules, every batch settlement must present a validComplianceAttestationfrom an attester in thatTrustedAttesterRegistry. - D2 seizure. The admin can mark an allocation for seizure. A sweep of the locked holdings also needs a
SeizureOrderfrom a party other than the admin.
Allowances
The owner approves a TokenAllowance for a spender with TokenRules_ApproveAllowance. The spender draws on it with TokenRules_TransferFrom, and the registry applies the current configuration.
- A transfer to the spender's own account completes in one step. A transfer to another receiver creates a pending
TokenTransferInstructionthat the receiver accepts. - Each spend writes the spender party into the transfer metadata under the key
openzeppelin.com/spender. - Each spend needs explicit disclosure of the owner's funding holdings. A partial spend returns change at a new holding contract ID, so a disclosure cannot be used twice.
- A spend archives the allowance and creates the remaining budget at a new contract ID. Wallets must track the new ID.
The owner's account parties can revoke an allowance in three ways. The admin cannot revoke it.
TokenAllowance_Revoke.TokenAllowance_SetRemainingwith zero. A positive value creates the allowance again at a new contract ID.TokenRules_ApproveAllowancewith amount zero and the current contract ID, as with ERC-20approve(spender, 0).
V1 Compatibility
The V2 interfaces are separate from V1. They share only splice-api-token-metadata-v1, so V1 tooling cannot see this token. To support V1, add the V1 interface instances to the same templates. The V1 DARs are in dars/vendor/, and the vendored splice-token-standard-utils DAR has helpers for this.
Build and Consume
From the root of canton-contracts:
DAML_PACKAGE=experiments/token/tokenCIP112-v1 dpm buildIn your project, add the token DAR and the vendored interface DARs as data-dependencies:
data-dependencies:
- ../canton-contracts/experiments/token/tokenCIP112-v1/.daml/dist/openzeppelin-tokenCIP112-v1-0.1.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-metadata-v1-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-holding-v1-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-holding-v2-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-transfer-events-v2-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-allocation-v1-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-allocation-v2-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-transfer-instruction-v1-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-transfer-instruction-v2-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-allocation-instruction-v1-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-allocation-instruction-v2-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-allocation-request-v1-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-api-token-allocation-request-v2-1.0.0.dar
- ../canton-contracts/dars/vendor/splice-token-standard-utils-2.0.0.dar
build-options:
- --target=2.1The vendored DARs are built with SDK 3.5.1 and used from the 3.4.11 baseline. This works because both target Daml-LF 2.1, so --target=2.1 is required. dars/manifest.yaml records the source commit and digest of each vendored DAR.
Testing
- Unit tests.
dpm testruns the Daml Script suites on an in-memory ledger. CI requires full template and choice coverage. - Sandbox gate.
scripts/check-sandbox.shruns token creation, transfer, allowance, query, and burn scripts against a static-time Canton sandbox over the Ledger API. It needsdpm, Java 21 or newer,lsof, and a free Ledger API port (6865by default, or setOZ_LEDGER_PORT). CI does not run this gate.
Related
- Reference Implementations that settle through Token Standard V2.
- Access Control, Ownable, and Pausable for application-level authorization.