Skip to main content
This page lists every step to make an existing CIP-0056 token on Canton an Omnichain Fungible Token (OFT) with an OFT adapter, and to wire it to an EVM chain. It covers the Canton side, the EVM side, and a first transfer in each direction.
LayerZero on Canton is not yet open for self-service integrations. The Canton contract packages and deployment tooling are not public. Steps that depend on LayerZero start with “Work with LayerZero Labs” and say what happens in that step. To start an integration, contact the LayerZero team.
Read Getting Started and Technical Overview first. This page uses their terms: parties, participant nodes, signatories, disclosed contracts, and holdings.

How an OFT Works on Canton

On Canton, LayerZero connects an existing CIP-0056 token, for example a token issued through Digital Asset’s registry, with an OFT adapter. The adapter is a set of Daml contracts. The OApp admin signs them. It is usually the asset issuer. For Canton Coin, it is the ccadapter party that LayerZero Labs administers (see Parties Involved). The adapter reaches the LayerZero Virtual Endpoint Rollup (VER) through request contracts on the ledger.

Adapter Models

The LayerZero Canton packages support two adapters: With both adapters, the receiver gets a CIP-0056 transfer instruction. If the token’s transfer rules require it, the receiver accepts the instruction in a separate transaction, usually through its wallet. The instruction expires after 24 hours, and the tokens return to the custody or mint escrow party. LayerZero does not resend an expired instruction automatically. To have the tokens delivered again, contact the LayerZero team. For Canton Coin, a receiver with a transfer preapproval gets the tokens without a separate accept. The burn/mint adapter has a second signatory, a mint escrow party. The lock/unlock adapter has a second signatory, the custody party that holds locked assets. Both must be different from the OApp admin. A burn/mint adapter can burn and mint only through a burn/mint factory that the OApp admin controls. If the token’s instrument admin is a different party, the instrument admin creates a BurnMintDelegate contract for the token. This contract gives the OApp admin burn and mint rights for that one instrument. The instrument admin can revoke these rights by archiving the contract.

Parties Involved

Decimals

The adapter’s configuration contract (AdapterConfig for lock/unlock, BurnMintAdapterConfig for burn/mint) stores localDecimals (at most 10 on Canton) and sharedDecimals. As on other chains, the amount in a LayerZero message uses shared decimals. Pick a sharedDecimals value that every chain in your mesh supports. The OFT standard explains shared decimals.

Prerequisites

  1. A participant node for your parties. Run your own validator node, or use a party hosted on another operator’s node. A validator node needs a Super Validator sponsor to join the Global Synchronizer.
  2. Ledger API access to that participant node, with rights to submit commands as your parties.
  3. Parties. Allocate the OApp admin party and a fee deposit party on your participant node. Allocate the custody or mint escrow party too, unless you agree in Step 1 that another operator runs it. See Manage Daml Parties.
  4. An existing CIP-0056 token. Choose the lock/unlock or the burn/mint adapter. For the burn/mint adapter, the token must have a CIP-0056 burn/mint factory.
  5. Amulet (Canton Coin) on your parties, to pay for traffic, endpoint request fees, and registry fees.
  6. A Canton 3.x environment. The LayerZero packages target Canton 3.x and the Daml 3.x SDK.
  7. An EVM deployer for the remote side, set up as in the EVM OFT quickstart.

Step 1: Agree on the Integration

Work with LayerZero Labs to start the integration. In this step, you and LayerZero agree on:
  • The adapter model and the networks: LayerZero testnet (Canton DevNet) first, then mainnet
  • The EVM chains to connect
  • Who operates which parties, including the mint escrow or custody party for an adapter
  • The send and receive security settings (DVNs and confirmations)

Step 2: Get the LayerZero Package Set and Protocol References

Work with LayerZero Labs to receive these items:
  • The LayerZero DAR packages. See LayerZero Packages.
  • The protocol references that your contracts point to. These are the lzowner party, which observes your OApp and handles its requests, the request factory contract, and the endpoint address that the adapter configuration stores.
  • The disclosures for the LayerZero-signed contracts that your transactions use, such as the request factory. Your participant node cannot see these contracts, so each submission must attach them as disclosed contracts.

Step 3: Upload and Vet the Packages

Upload the DAR packages to your participant node. Canton vets a package as part of the upload by default. See Manage Daml Packages. Every participant node that takes part in a transaction with your token must vet the same packages. This includes the participant nodes of your users’ wallets. Plan this with your wallet providers before launch.

Step 4: Create the Adapter Contracts on Canton

Submit these commands as the OApp admin. None of these contracts needs a signature from LayerZero. Each contract lists lzowner (from Step 2) as an observer when you create it. This lets LayerZero see your contracts, so it can process your requests and provide the disclosures that senders and the executor need.
  1. Burn/mint delegate (burn/mint adapter only). If the token’s instrument admin is not the OApp admin, the instrument admin creates a BurnMintDelegate for the token. It names the OApp admin as the delegate, the token’s burn/mint factory, and lzowner as oappGateway.
  2. Adapter. Create LockUnlockAdapter or BurnMintAdapter with the OApp ID and lzowner as oappGateway. The OApp ID is the OApp admin party and an ID string that you choose. The custody party (lock/unlock) or the mint escrow party (burn/mint) is the second signatory, so it must also authorize this command.
  3. Configuration. Create AdapterConfig (lock/unlock) or BurnMintAdapterConfig (burn/mint). It holds:
    • the OApp ID and the token’s instrument ID
    • localDecimals and sharedDecimals
    • the fee deposit party and the fee in basis points per destination EID
    • the request factory and the endpoint address from Step 2
    • the rate limiter configuration (rate limiting can be disabled)
    • empty maps for peers and enforced options, which you set in Step 6
    • the observers of the configuration, including lzowner
  4. Receiver registry. Create a Registry and its RegistryConfig. On receive, the adapter uses the registry to turn the 32-byte receiver in the message into a Canton party. The configuration sets an optional registration fee and a maximum number of entries.
  5. Access control and rate limiter state. Create an AccessControl contract if you want to give roles to other parties, for example a manager or a fee manager. Without it, only the OApp admin can run administrative choices. Create a RateLimiterState contract if you use rate limits.
Contract IDs change when you update a contract. Each configuration choice in Step 6 archives the adapter configuration and creates a new one. Read the current contract ID from the ledger after each change.

Step 5: Set the Endpoint Configuration

On Canton, endpoint settings are requests, not direct calls. For each setting, the OApp admin exercises OApp_CreateRequest on the adapter. The request carries the endpoint call and pays the request fee in Amulet. VER processes the request, and the lzowner party accepts it. Submit one request for each of these endpoint calls: Work with LayerZero Labs to get the current request fee and to agree on the values. Hold enough Amulet on the OApp admin for every request. Do not submit a second request for the same setting while the first one is still pending. DVNs on Canton. Canton DVNs are deployed once per DVN provider, at the protocol level, as on other chains. You do not deploy DVN contracts for your OApp. You choose DVNs in setSendConfig and setReceiveConfig. For the DVNs that verify Canton Coin messages on Canton mainnet, see DVN Providers. Use the DVN addresses that LayerZero Labs confirms in Step 1.

Step 6: Configure the Adapter on Canton

Exercise these choices on the adapter configuration as the OApp admin, or as a party with the required role. They do not go through VER and do not need a LayerZero signature. Set rate limits with the rate limiter choices on the adapter configuration (rate limiter manager role). The RateLimiterState contract tracks usage.

Step 7: Deploy and Wire the EVM Side

Deploy an OFT on the EVM chain with the standard tooling, as in the EVM OFT quickstart. Then wire it to Canton:
  1. Set the peer. Call setPeer on the EVM OFT with the Canton EID (30567 for mainnet, 40567 for LayerZero testnet on Canton DevNet) and the adapter’s 32-byte OApp address. The address is derived from the adapter’s OApp ID: the OApp admin party ID and the ID string from Step 4:
    Use the ID string of the OApp ID, which can differ from the token’s instrument ID. Work with LayerZero Labs to confirm the address before you set it.
  2. Set the DVNs. Configure the same DVN set on the EVM send and receive configuration as on the Canton side. See DVN and Executor Configuration.
  3. Set the confirmations for each direction to the values agreed in Step 1.
  4. Set enforced options for sends to Canton.
  5. Match decimals. Use the same sharedDecimals on the EVM OFT as in the adapter configuration.

Step 8: Register Receivers

A LayerZero message names its receiver with 32 bytes. A Canton party ID is hint::1220<64 hex characters>. Canton calls everything after :: the fingerprint: 1220 is a multihash header for SHA-256, followed by a 32-byte digest. LayerZero calls the 64 hex characters of that digest the fingerprint, and this page uses that meaning. The message carries only those 32 bytes, and the adapter’s registry maps them back to the full party. Canton treats party IDs as opaque, so this mapping is a LayerZero convention. For Canton Coin, use the Value Transfer API or Stargate to derive it. Before a party can receive tokens from another chain, it must register in the adapter’s registry. The party exercises SetPartyId on the Registry with its hint, and pays the registration fee from its own holdings if the configuration sets one. The registry key is the fingerprint of the party that exercises SetPartyId, and the hint is a value that party supplies. A party with its own namespace, such as an external party created with its own key, is the only party that can register its fingerprint. Parties that share a namespace can each set or replace the entry for that shared fingerprint. For Canton Coin, use the Value Transfer API or Stargate to register: they derive the hint from the full party ID and refuse to re-point a namespace that another party holds. A wallet can ask its user to sign the registration during onboarding. If a party is not registered, delivery to it waits. The tokens stay burned or locked on the source chain. The lzReceive request is accepted, but its settlement cannot complete. The LayerZero executor can retry the settlement after the party registers, and the delivery can then complete.
The registry key is the 32-byte fingerprint alone. All parties in one namespace, for example the local parties on one validator, share a fingerprint. If a second party in the namespace registers, it replaces the first, and every later inbound transfer for that fingerprint goes to the second party. Register only one receiving party per namespace.

Send from Canton to the EVM Chain

  1. Get the fee. The fee is quoted from VER off the ledger, through the read API of LayerZero’s VER sequencer (see VER). Get an access token from LayerZero Labs.
  2. Submit. The sender’s wallet exercises OApp_LzSend on the adapter. The command includes:
    • the send parameters: destination EID, receiver (the 32-byte EVM address), amount, minimum amount, extra options, and an optional compose message
    • the sender’s token holdings to spend
    • Amulet holdings and the fee amount for the protocol fee
    • the configuration, rate limiter, and request factory references
    • the disclosed LayerZero contracts from Step 2
  3. Settle. The lock/unlock adapter moves the amount to the custody party, and the burn/mint adapter burns it. The send also creates a request. VER processes it, and the lzowner party accepts or rejects it. On accept, VER emits the packet and the DVNs verify it. The LayerZero executor settles an accepted send separately. For rejected sends and refunds, see Step 5.
  4. Receive on EVM. The DVNs verify the packet, and the executor calls lzReceive on the EVM OFT, which credits the receiver as on any EVM pathway.

Send from Another Chain to Canton

A send into Canton is different from a send between EVM chains. The source chain cannot check the receiver, so the receiving party must be ready on Canton before anyone sends.
  • The message carries only part of the receiver’s address. The to field holds the 32-byte fingerprint of the party ID. The adapter’s registry on Canton maps it back to the full party ID (see Step 8).
  • The receiver must register first, on Canton. A party registers by signing SetPartyId with its own Canton wallet or key and paying the fee from its own holdings on Canton. No source-chain transaction can register it. Parties that share a namespace can each set the entry for that namespace (see Step 8).
  • The receiver’s participant node must run the LayerZero packages. To register, the node that hosts the party must have uploaded and vetted the LayerZero packages. Many Canton wallets and custodians do not run them.
For Canton Coin, use Stargate or the Value Transfer API. They take the full Canton party ID and convert it to the 32-byte receiver value. They refuse to build a transfer if the party is not registered or if its fingerprint is registered to a different party. The Value Transfer API also prepares the registration transaction for the receiving party, which signs it on Canton with its own wallet. Neither can register the party from the source chain. For other tokens on Canton, work with the token issuer or LayerZero Labs before you build sends, and check every item in the pre-send checklist.

Build the Send Yourself

If you build an OFT send into Canton yourself and the receiving party is not registered in the adapter’s registry, your funds will be stuck. This includes a receiving party whose hosting participant node has not uploaded and vetted the LayerZero DAR packages, because that party cannot register until its node runs them. The source chain burns or locks the tokens, but the delivery on Canton cannot complete. It stays pending until the receiving party registers.A call to send() on an OFT on an EVM chain, HyperEVM, or Solana does not check the Canton receiver. Stargate and the Value Transfer API do. We strongly recommend that you do not build OFT sends into Canton yourself. If funds are stuck, recovery needs action from the receiver’s node operator, a move of the party to a different node, or intervention by LayerZero. It is not practical for an end user to do this alone. LayerZero does not guarantee recovery or a recovery time. To ask for help, contact the LayerZero team.Three mistakes cannot be fixed by registering the receiver:
  • A to value that is not a party fingerprint, for example a hashed party ID, an EVM address, or a Solana address. No party can register that value, so the delivery never completes.
  • A fingerprint that is registered to a different party, for example another local party on the same validator. The tokens are delivered to that party, and only that party can send them back.
  • A compose message on the send. The Canton OApp refuses the delivery, whoever the receiver is.
If you build sends yourself, you are responsible for every item in the pre-send checklist.
  1. Encode the receiver. Set to in the SendParam to the receiver party’s fingerprint: the 64 hex characters after 1220 in its party ID, as 32 bytes. Do not hash the party ID. Do not use an EVM address, a Solana address, or the hint.
  2. Send. Call quoteSend and send on the OFT, as on any pathway. See the EVM OFT quickstart. Leave composeMsg empty. A Canton OApp rejects any inbound message that carries a compose message, so the delivery never completes and the tokens stay burned or locked on the source chain. Registering the receiver does not fix this. Do not set a native drop. The fee is charged, but nothing is delivered on Canton.
  3. Automatic delivery. After the DVNs verify the packet, the LayerZero executor delivers it into Canton. VER processes the request, and the executor settles it. The adapter’s callback resolves the receiver through the registry. The receiver then gets a CIP-0056 transfer instruction from the custody party or the mint escrow party. The receiver may need to accept it in a separate transaction, depending on the token’s transfer rules. The instruction expires after 24 hours. LayerZero does not resend an expired instruction automatically. To have the tokens delivered again, contact the LayerZero team.
  4. Rate limits. If an inbound rate limit is reached, the lzReceive request is still accepted, and the settlement waits. The LayerZero executor retries the settlement after the limit’s capacity recovers.

If the Receiver Is Not Ready

In every case below, the tokens are already burned or locked on the source chain. The sender cannot cancel or reverse the transfer from the source chain. The adapter stores the amount in local decimals as a 64-bit integer, so the per-message cap is (2^63 − 1) / 10^localDecimals tokens. This is not a Canton or holding limit. For Canton Coin, the cap is about 922 million CC per message.

Pre-Send Checklist

Check every item before the first send to a Canton party, and again if the party or its node changes.
  1. The receiving party exists and is funded. It holds Canton Coin on Canton for the registration fee, and its own Canton wallet or key can sign the registration.
  2. Its node runs the LayerZero packages. Every node that hosts the party has vetted the LayerZero packages. The node operator vets each new LayerZero package version.
  3. The party is registered for this token. For Canton Coin, POST /v1/register-canton-party on the Value Transfer API without holdings returns {"status": "registered", "partyId": "<partyId>"}.
  4. The fingerprint is not taken by another party. If the fingerprint is registered to a different party, the same call returns HTTP 409 (…its namespace is already registered to a different party). Do not send: the tokens would go to that party.
  5. The receiver value is the fingerprint. to is the 64 hex characters after 1220 in that party ID, as 32 bytes.
  6. The party can accept the transfer. For Canton Coin, the party has a transfer preapproval (One-step Transfers in Loop), or someone accepts the transfer instruction within 24 hours.
  7. No compose message and no native drop are set on the send. A compose message makes the delivery fail (see If the Receiver Is Not Ready).
  8. Send a small test amount first, and confirm that it arrives on Canton before you send more.

Verify the Transfer

  • On Canton, look up the transaction or the receiver’s holdings in Lighthouse, or read the holdings from the receiver’s participant node.
  • On the EVM chain, check the transaction in the chain’s block explorer.
  • Trace a message end to end by its GUID or source transaction hash with the LayerZero Scan API. For a send from Canton, Scan indexes the transaction in which lzowner commits the VER result, not the update ID that your wallet returns.

Next Steps