> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Canton Fundamentals for LayerZero Developers

> Canton concepts for LayerZero developers: the Daml ledger, parties and participant nodes, explicit disclosure, DAR vetting, CIP-0056 holdings, and Amulet fees.

This page explains the Canton concepts that you need to understand before you build with LayerZero on Canton. If you're coming from EVM chains, it explains how Canton differs and why LayerZero's Canton implementation works the way it does.

**What you'll learn**:

* The Daml ledger model: templates, contracts, and choices
* Parties, participant nodes, validators, Super Validators, and the synchronizer
* How transactions are confirmed, and why disclosed contracts are part of a submission
* Immutable contracts, changing contract IDs, and UTXO-style CIP-0056 holdings
* Authorization with signatories, controllers, and observers, and package vetting
* Traffic and Amulet fees

> [!TIP]
>
> For the LayerZero message lifecycle on Canton, see [Protocol Overview](https://docs.layerzero.network/v2/developers/canton/protocol-overview.md). For the differences from the EVM in summary form, see [Getting Started](https://docs.layerzero.network/v2/developers/canton/getting-started.md).

## Ledger Architecture

Canton does not have a single global state that every node stores. Each participant node stores only the contracts of the parties that it hosts. A synchronizer orders the transactions between participant nodes. Canton's [Network Architecture Comparison](https://docs.canton.network/appdev/modules/m2-network-architecture) describes this model in full.

### The Daml Ledger Model

Canton applications are written in Daml.

* A **template** defines the data fields of a contract and the choices that parties can exercise on it. A template is similar to a Solidity contract definition.
* A **contract** is an instance of a template. A contract is immutable.
* A **choice** is an action on a contract. A [consuming choice](https://docs.canton.network/appdev/modules/m3-choices) archives the contract, and it can create new contracts. A nonconsuming choice leaves the contract active.

A LayerZero OApp on Canton is a set of Daml contracts. The OApp's configuration, its token holdings, and its requests to the endpoint are all contracts.

### Parties and Participant Nodes

A [party](https://docs.canton.network/appdev/deep-dives/manage-daml-parties) is an identity on the ledger. A participant node (also called a validator node) hosts one or more parties. The participant node stores the contracts that its parties can see. It submits commands for its parties, and it confirms transactions for them.

To read a party's contracts, you connect to the participant node that hosts the party. Canton has no public RPC endpoint that returns all ledger state.

### Validators, Super Validators, and the Global Synchronizer

The Canton Network's public synchronizer is the **Global Synchronizer**. A group of **Super Validators** operates it. Each Super Validator runs a sequencer and a mediator. A validator node that wants to connect to the Global Synchronizer needs a Super Validator sponsor. On every network, the Super Validators must add the validator's egress IP to their allowlist. On TestNet and MainNet, the validator also needs a one-time onboarding secret from its sponsor. See the [Validator Onboarding Process](https://docs.dev.sync.global/validator_operator/validator_onboarding.html) in the Splice documentation.

### The Synchronizer

The synchronizer orders transactions and delivers each participant node its part of a transaction. It does not store contract state and cannot read transaction contents. See Canton's [privacy model](https://docs.canton.network/appdev/deep-dives/privacy-model).

## Message Flow Overview

On other chains, an OApp calls an endpoint contract. On Canton, an OApp creates request contracts on the ledger, and LayerZero's **Virtual Endpoint Rollup (VER)** processes them. VER commits each result back to the ledger:

* **Send**: the sender exercises a send choice on the OApp. This creates a request contract on the ledger. VER processes the request and commits an accept or a reject. On accept, the DVNs verify the message for the destination chain. The LayerZero executor settles an accepted send on Canton separately. The refund for a rejected send is available through the protocol, but it is not settled automatically today.
* **Receive**: DVNs verify the message on Canton. The LayerZero executor submits the delivery, VER processes the request, and the executor settles it, which credits the receiver.

```mermaid theme={null}
flowchart LR
    A[Sender] --> B[OApp on Canton]
    B --> C[Request contract]
    C --> D[Virtual Endpoint Rollup]
    D --> E[DVNs]
    E --> F[Destination chain]
```

> [!NOTE]
>
> **Complete Protocol Details**
>
> For the send, verification, and receive workflows on Canton, see [Protocol Overview](https://docs.layerzero.network/v2/developers/canton/protocol-overview.md).

## Transaction Execution Model

### Commands and Atomic Transactions

A party's participant node submits a **command**, for example "exercise this choice on this contract". The participant node runs the Daml code. The result is a transaction: a tree of creates and exercises. The transaction is atomic. Either all of it commits, or none of it does.

### Confirmation by Participant Nodes

The synchronizer sends each participant node the parts of the transaction that its parties can see. Each participant node validates its parts. The nodes that host the signatories and actors send confirmations. Observers' nodes receive their parts but do not confirm. A party hosted on several nodes is confirmed once enough of them approve. The synchronizer's mediator collects the confirmations and decides the outcome. See Canton's [privacy model](https://docs.canton.network/appdev/deep-dives/privacy-model).

A send or receive uses contracts that the [OApp admin](https://docs.layerzero.network/v2/developers/canton/oft/overview.md#parties-involved) signs and contracts that LayerZero parties sign. So the participant nodes that host the OApp admin, the custody or mint escrow party, and LayerZero's parties must be online and must confirm each message.

### Disclosed Contracts

A party can only use a contract that it can see. When a transaction needs a contract that the submitter is not a stakeholder of, the submitter attaches the contract to the command as a [disclosed contract](https://docs.canton.network/appdev/deep-dives/explicit-contract-disclosure).

A LayerZero send uses contracts that LayerZero parties sign. The sender is not a stakeholder of those contracts, so the sender's submission includes them as disclosed contracts. The OApp's own contracts name a LayerZero party as an observer when they are created, so LayerZero can see them. LayerZero provides the disclosures that a sender needs as part of an integration.

## State Management Model

### Immutable Contracts

A Daml contract is never modified. To change state, a choice archives the old contract and creates a new one. On EVM chains, a balance or a configuration value changes in place. On Canton, each change produces a new contract.

### Contract IDs Change

When a contract is archived and recreated, the new contract has a new contract ID. Do not store a contract ID as a permanent identifier for an OApp, a configuration, or a token. Use stable identifiers, such as party IDs, and look up the current contract when you need it. Fetch the current contract before each submission.

### UTXO-Style Holdings

A [CIP-0056](https://github.com/global-synchronizer-foundation/cips/blob/main/cip-0056/cip-0056.md) token balance is a set of [holding contracts](https://docs.canton.network/appdev/deep-dives/token-standard), similar to Bitcoin UTXOs. A transfer consumes input holdings and creates new ones for the receiver and for any change.

The sender's wallet supplies the input holdings for a LayerZero send. For most CIP-0056 tokens, only a holding's stakeholders can see it. Canton Coin holdings are an exception: Scan publishes them by owner. The send also needs separate Amulet holdings to pay the protocol fee. When the sent token is Canton Coin, the amount and the fee must come from different Amulet holdings, so a party with one Amulet holding cannot send. The Splice wallet merges the Amulet holdings of the parties it manages into one about every 30 seconds, so send Canton Coin from an external party.

### LayerZero Protocol State

On EVM chains, the endpoint contract stores nonces, message hashes, and configuration. On Canton, VER keeps this endpoint state. A set of independent validators runs VER. A result is committed to the ledger only after a quorum of them has signed it, together with a commitment to VER's new state. The `lzowner` party commits each result. The OApp's own holdings and configuration stay in the OApp's contracts. See [Protocol Overview](https://docs.layerzero.network/v2/developers/canton/protocol-overview.md#the-virtual-endpoint-rollup-ver).

## Security & Permission Model

### Signatories, Controllers, and Observers

Canton's [authorization model](https://docs.canton.network/appdev/modules/m3-authorization) is part of each template:

* A **signatory** must authorize the creation of a contract. Signatories always see the contract.
* A **controller** can exercise a specific choice. Each choice declares its own controllers.
* An **observer** can see a contract but cannot exercise its choices unless it is also a controller.

Daml checks these rules for every action in a transaction. A choice that a party is not a controller of cannot be exercised by that party.

### OApp Admin Control

The OApp admin's parties control the OApp's own configuration, for example peers, enforced options, and rate limits. Changing these settings does not need a signature from LayerZero.

Endpoint settings work differently. To set the send and receive libraries, the DVN and executor configuration, or the delegate, the OApp admin submits a request and pays a request fee in Amulet. VER processes the request, and the `lzowner` party accepts it.

### LayerZero Parties on Every Message

LayerZero's protocol parties, including `lzowner`, take part in every send and every receive. They are signatories on the request contracts, and they act on each request. Because of this, LayerZero's participant nodes must be online for messages to move. The [Protocol Overview](https://docs.layerzero.network/v2/developers/canton/protocol-overview.md#layerzero-parties-on-every-message) describes their role.

### Package Vetting as Code Trust

A participant node runs only the Daml packages that it has [vetted](https://docs.canton.network/appdev/modules/m5-manage-daml-packages). Every participant node involved in a transaction must have vetted the package version that the transaction uses. If it has not, the transaction is rejected. Vetting a package is how a node operator agrees to run that code. When LayerZero releases a new package version, vet it on each participant node. Until every involved node has vetted it, transactions keep using the highest version that all of them have vetted, or fail if that version is no longer vetted. See Canton's guide to [smart contract upgrades](https://docs.canton.network/appdev/deep-dives/smart-contract-upgrade).

### LayerZero Packages

LayerZero provides its Canton packages as private DAR files. Upload and vet these packages on each participant node that hosts a party that uses LayerZero (`layerzero-burn-mint-adapter` only if you use a burn/mint token):

| Group | Packages |
| - | - |
| Interfaces | `layerzero-iaccess-control`, `layerzero-iledger-time-validity-period`, `layerzero-iminfee`, `layerzero-iminpayoutvalue`, `layerzero-ioapp`, `layerzero-ioapp-config`, `layerzero-irate-limiter`, `layerzero-iregistry`, `layerzero-irequest`, `layerzero-itoken`, `layerzero-request-factory-interface` |
| Shared code | `layerzero-common`, `layerzero-fee-utils`, `layerzero-oft-common` |
| OApp contracts | `layerzero-access-control`, `layerzero-discovery-registry`, `layerzero-rate-limiter`, `layerzero-registry` |
| Adapters and OFT | `layerzero-lock-unlock-adapter`, `layerzero-burn-mint-adapter`, `layerzero-oft` |
| Protocol | `layerzero-request`, `layerzero-request-factory`, `layerzero-executor`, `layerzero-state-transition` |

Canton Coin uses the lock/unlock adapter.

A command names an interface package, for example `layerzero-ioapp`, but the choice runs code in the implementation package, for example the adapter. Vetting the interface package alone is not enough.

| Role | Upload and vet | Why |
| - | - | - |
| Party that receives tokens on Canton | The full set, on every node that hosts the party | Registration exercises `SetPartyId` through `layerzero-iregistry`. The choice runs in `layerzero-registry` and pays the fee through `layerzero-fee-utils`. |
| Party that sends from Canton | The full set, on every node that hosts the party | A send exercises `OApp_LzSend` through `layerzero-ioapp`. The choice runs in the adapter, for Canton Coin `layerzero-lock-unlock-adapter`, and creates a request through `layerzero-request-factory`. |
| Wallet operator | The full set, on every node and co-host that hosts users, and each new version | Users register and send. One set covers both. |
| OApp admin (OFT deployer) | The full set, including the adapter that you deploy | Your adapter, configuration, registry, rate limiter, and access control contracts use these packages. |

#### Mainnet Package IDs

Package IDs on Canton mainnet, as of 2026-10-28. Check the vetted package IDs on your participant node against this table.

| Package | Package ID |
| - | - |
| `layerzero-iaccess-control` | `29ac76d5116252e0d077079b5669d4f65732f006ac23cc6e90bbcde4a7296397` |
| `layerzero-iledger-time-validity-period` | `c4bb1ae6fdf3d6cc82483b43117785423f48f2902570b1089e1daa2dce72cea5` |
| `layerzero-iminfee` | `09d9cb66a83dba4f5d094ead2e79d868e2e21fcbe7d46a668451cb4b00adfac7` |
| `layerzero-iminpayoutvalue` | `749ff2fca45e6d911d3b0ff9c87bac4be6e719b949084b3248a38848b066593a` |
| `layerzero-ioapp` | `6f93e131b078bac9499079c5a4fa82463da628f4adb8131a5df1dcd95996e4a0` |
| `layerzero-ioapp-config` | `1aa3780f86be355dd4b98f85642d252fed6364109c288a265f4ed415f34b0c9f` |
| `layerzero-irate-limiter` | `f7ba1d0425352b8746b3a9e6501ecedc7a0251ad5ebebc72528359d66a6360c8` |
| `layerzero-iregistry` | `f3ab3c6b7b2aa00fa80c1b75ee3ea6c6bd15e8ab40028df72a33044fbeb9a7b8` |
| `layerzero-irequest` | `b335bc85684ca6f4af74ea1fff9706038b8f0b1657500e7de046503e09567d8f` |
| `layerzero-itoken` | `925f12698ebcebbc1795033caa88d647193645a0bbb052b4982223f2f88a4808` |
| `layerzero-request-factory-interface` | `a9d61831b14d7419f05a3f53d034a17c48e1a331dd55dba898eb20e9b5ccf9b6` |
| `layerzero-common` | `c25d0a1eb1faecaa41cfc582d010fb9660584bfb89cfddda3d70d7f3d6761268` |
| `layerzero-fee-utils` | `27f783ee39fef736dd29b403cdfdb6687cbbe3528870048945343cc041389178` |
| `layerzero-oft-common` | `774343a0c5b0381d257ab3c8ae55aee0a4df45645acf034fe3685a711fd97f64` |
| `layerzero-access-control` | `4c46f160d52751c5a57dbae56faefce6a723f114bcc5d70dc4fe78ee95e96ca3` |
| `layerzero-discovery-registry` | `9ad0da1f98655f79e7306bca87e0e055c5294554c89cd0cbec5335bb14c6949f` |
| `layerzero-rate-limiter` | `1c0244b583c30e20bfa987b71a01ed509a9e3224de2753a5b9d0b2d1465e07e9` |
| `layerzero-registry` | `131e39addf65add23a49b70288cb4a6243a1e88e10a6bb96d014d1af9ae99117` |
| `layerzero-lock-unlock-adapter` | `5a22c6a1b57f8fcd8f6b0733be3e2b69f6a0a2e0bb4fbfb26599b5dc79e7865d` |
| `layerzero-burn-mint-adapter` | `1e7374cb5591c68bc3d992530a18250fb79ace22733fe913c4349034a1d9856a` |
| `layerzero-oft` | `ab44b235557b129bfbfab33d6b2571a9dc269fefe57d7984c89c6d6c5122ad92` |
| `layerzero-request` | `c6f945cfe53515a6cdbb7053b87916e6afd499a4e4befe271c07970afb263875` |
| `layerzero-request-factory` | `be15ee70c4c7de842385395fc5076cbce958514b4178f37b75db1298a1b0ec38` |
| `layerzero-executor` | `542bd964a4f279c25585ab57f76315171a10f29f109795e94b18e8fab36ecd81` |
| `layerzero-state-transition` | `a5e6eecd43badb16cc9ccaac2a092c63d69a4419e6bf4c69f90816b951a7afe0` |

## Fee Model

### Traffic

Canton has no gas in the EVM sense. Instead, every transaction on the Global Synchronizer costs [traffic](https://docs.canton.network/appdev/modules/m4-canton-coin). Every validator that sends messages for a transaction uses traffic, including validators that only confirm. Each validator has a free base rate. Traffic beyond that is bought by burning Canton Coin. The LayerZero metadata lists the native token as Amulet, with 10 decimals.

### LayerZero Fees

What the protocol fee pays for depends on the direction of the message.

**From Canton.** The protocol fee is the LayerZero messaging fee, which pays for DVN verification and execution on the destination chain, plus the LayerZero request fee: the minimum fee (`minFee`) of the send request on Canton. It is paid in Amulet, upfront when the send is submitted, from one or more Amulet holdings that the sender supplies. These holdings are separate from the token holdings being sent.

**Into Canton.** The sender pays the messaging fee on the source chain, as on any pathway. For a message into Canton, the fee covers:

* the traffic cost of each DVN's verification on Canton
* the commit of the verification in VER
* the executor's delivery
* the settlement of the pending callback
* the `minFee` of every Canton request that the message needs

## Key Canton Concepts for LayerZero

### Peers and Receivers Are 32 Bytes

LayerZero messages identify peers and receivers with 32-byte values. Canton party IDs are text identifiers longer than 32 bytes, so an OApp on Canton maps a 32-byte value to a Canton party when it delivers a message. Remote OApps set the Canton OApp as a peer with a 32-byte value, as on other chains.

### CIP-0056 Token Standard

[CIP-0056](https://github.com/global-synchronizer-foundation/cips/blob/main/cip-0056/cip-0056.md) defines the Canton token standard. It specifies the holding interface, transfer instructions, and the registry endpoints that wallets use. A token that you move with LayerZero on Canton is a CIP-0056 token.

## Key Takeaways

* Canton is private by default. Each participant node sees only its parties' contracts.
* Authorization is declared in Daml templates through signatories, controllers, and observers.
* Contracts are immutable, and contract IDs change when contracts are updated.
* Token balances are UTXO-style CIP-0056 holdings, and the sender's wallet supplies them on every send.
* Each participant node involved in a message must upload and vet the LayerZero DAR packages.
* The LayerZero endpoint on Canton is VER. Every request and result is recorded on the ledger, and LayerZero parties confirm and act on every message.
* Traffic and the protocol fee are paid in Amulet.

## Next Steps

* [Protocol Overview](https://docs.layerzero.network/v2/developers/canton/protocol-overview.md): the LayerZero message lifecycle on Canton
* [Getting Started](https://docs.layerzero.network/v2/developers/canton/getting-started.md): what a Canton integration requires
* [Canton Network documentation](https://docs.canton.network): Daml and the Canton ledger model
