Skip to main content
Available when Canton Coin launches on Stargate and the Value Transfer API.Canton Coin on BSC, HyperEVM, and Solana is bridged CC: an OFT that LayerZero Labs deployed and administers. It is minted when native CC is locked on Canton through the LayerZero CC adapter. On HyperCore, LZCC is a spot token linked to the HyperEVM OFT through the Hyperliquid asset bridge. The Global Synchronizer Foundation, Digital Asset, and the Canton Network did not deploy bridged CC and do not administer its contracts. See Canton Coin Across Chains.
The Value Transfer API builds each Canton Coin (CC) transfer for you. You sign it and submit it yourself. On Canton, you submit from your own participant node, or from a node that hosts your party. The API never submits a Canton transaction. On a send from Canton, the Value Transfer API adds no fee of its own. On a send into Canton, the quote can include the partner or base fees configured for your API key. The quote lists every fee. You also pay the protocol fee. It is the LayerZero messaging fee. On a send from Canton, it also includes the LayerZero request fee on Canton, paid in CC.
Send into Canton only through the Value Transfer API or Stargate. The supported paths into Canton are the Value Transfer API, for integrators and market makers, and Stargate, for end users. Both check that the receiving Canton party is registered before they build a send. An OFT send() that you build yourself on BSC, HyperEVM, or Solana bypasses these checks. If the receiving party is not registered in the CC adapter’s registry, including because its validator node has not vetted the LayerZero DAR packages, the funds will be stuck on Canton. A malformed to value, a fingerprint registered to a different party, or a compose message on the send cannot be fixed by registering. Recovery needs action by the node operator or by LayerZero, is not practical for end users, and LayerZero does not guarantee it. See Send from Another Chain to Canton and the pre-send checklist.

Prerequisites

  1. A participant node that hosts your party. Run your own validator node, or host your party on another operator’s node. A validator needs a Super Validator sponsor, an egress IP on the Super Validators’ allowlist, and traffic to submit transactions. See Validators, Super Validators, and the Global Synchronizer.
  2. A Value Transfer API key. Contact the LayerZero team for a key, and say that you need Canton Coin routes. Send it in the x-api-key header on every call, including /v1/register-canton-party, as in the quickstart.
  3. Access to the LayerZero Canton packages. The DAR packages are private. To get access, contact the LayerZero team with the GitHub handles of the people who will download them.
  4. The packages vetted on every node that hosts your party. If your party is hosted on more than one participant node, every co-host must upload and vet the same packages. See LayerZero Packages.
  5. Funded parties. Create and fund your Canton parties before you start. A sending party needs CC for the amount plus the protocol fee from /v1/quotes. A receiving party needs CC for the registration fee before it can receive any CC.
  6. A registered party for inbound transfers. Register each party that receives CC from another chain, on Canton, before the first inbound send. See Register a Receiving Party.
  7. Split holdings. The amount and the fee must come from different holdings, so the sending party needs at least two unlocked Amulet holdings. Their total must cover the amount plus the quoted fee, with some margin: the build re-quotes the fee. The API picks one or more holdings for the fee so that the holdings left over cover the amount. Send from an external party, because the Splice wallet merges the holdings of the parties it manages (see Holdings). To create an external party, see Canton’s external signing guide. The party’s key signs every send. Build and submit immediately after you read the holdings.
  8. Your own signing flow for Canton, and for Solana if you send from Solana. Solana sends need a signer for Solana v1 transactions. See Solana Wallets.
One receiving party per validator namespace. The registry stores the 32-byte fingerprint of a party ID, and by default all local parties on one validator share the validator’s fingerprint. If two of those parties register, the last one receives every inbound transfer for both. Register only one receiving party per namespace. The Value Transfer API refuses to re-point a namespace that another party holds (the CantonNamespaceTaken error; see Errors). A direct SetPartyId call does not have this check, so always register through the API.

Address Formats

Always pass the full Canton party ID. The API converts it to the 32-byte receiver value for the LayerZero message. This guide covers Canton mainnet only. It does not cover a testnet route, so test with a small amount on mainnet.

Endpoint Sequence

Get the Canton chain key (canton) from GET /v1/chains, and the CC token addresses from GET /v1/tokens. Use these values in /v1/quotes and /v1/register-canton-party.
1

Get a quote

Call POST /v1/quotes as on any route. Use Canton party IDs as Canton addresses. Before you quote a route into Canton, check the recipient with POST /v1/register-canton-party (no holdings). /v1/quotes does not check the recipient. On a route into Canton from BSC, HyperEVM, or Solana, /v1/build-user-steps returns the CantonPartyNotRegistered error (HTTP 422; see Errors) if the recipient is not registered. It returns the CantonNamespaceTaken error (HTTP 409) if the recipient’s fingerprint is registered to a different party. A quote from or into Canton has no userSteps. The build step returns them. Quote amounts use base units, as on any route. A transfer carries at most 6 decimal places of CC (shared decimals), so a smaller remainder is not sent.
2

Register the receiving party (once)

If the recipient is not registered, it registers once per token, on Canton, before the inbound send. POST /v1/register-canton-party prepares the registration transaction, and the recipient’s own Canton wallet signs it. See Register a Receiving Party. After the registration transaction commits, request the quote again. Skip this step if the party is already registered.
3

Build the transaction

Call POST /v1/build-user-steps with the quoteId. For a Canton source, include the sender’s holdings in data.holdings. The API does not look up holdings for you. Read them from your node. For a Canton source, the response has one TRANSACTION step with chainType: "CANTON".
4

Sign and submit

Sign and submit every user step from /v1/build-user-steps on the source chain, in order. An ERC-20 source returns an approve step and a send step. For a Canton source, see Submit on Canton.
5

Track the transfer

For every source, including Canton, poll GET /v1/status/{quoteId}?txHash=<hash>. For Canton, pass the update ID that your node or wallet returns. The API maps it to the LayerZero transaction that LayerZero Scan indexes, and links it in explorerUrl. explorerUrl is empty until the API has matched the update ID. Searching Scan for your wallet’s update ID finds nothing. To track the message in code, query the LayerZero Scan API with the transaction hash in explorerUrl. For a Canton source, that transaction can hold several messages: select the one whose sender is your party and whose amount matches. Read the warning below before you act on SUCCEEDED.
Check what SUCCEEDED means before you act on it.
  • From Canton: /v1/status can currently return the status of another transfer committed in the same LayerZero batch, and a send that LayerZero rejects can show that other transfer’s status. Confirm on LayerZero Scan that the message’s sender is your party and the amount matches.
  • Into Canton: SUCCEEDED means that LayerZero delivered the message to the CC adapter. It does not mean that the CC is in the receiver’s wallet. Confirm the CC on Canton: check for a new holding, or for a transfer instruction that the receiver must accept within 24 hours if it has no transfer preapproval.
The API rate-limits each key. On HTTP 429, wait and retry. See Status.

Acceptance, Refunds, and Rejected Sends

  • Inbound CC can wait for acceptance. Unless the receiving party has a CC transfer preapproval, the CC arrives as a transfer instruction that the receiver must accept within 24 hours. /v1/status does not track acceptance. Tell your users to turn on a preapproval (One-step Transfers in Loop) or to accept the instruction.
  • Fee change and refunds can arrive the same way. On a send from Canton, unused fee and refunds can also arrive as transfer instructions.
  • A send from Canton that VER rejects is not refunded automatically. After VER processes the request, the lzowner party rejects it on the ledger (see Protocol Overview). The CC that you sent stays with the LayerZero CC adapter until the refund is settled. Contact the LayerZero team with the party ID, the update ID, and the quoteId. The LayerZero request fee is not refunded.
LayerZero does not resend an expired instruction automatically. To have the tokens delivered again, contact the LayerZero team.

Build Request for a Canton Source

  • Pass only unlocked Amulet holdings owned by the sending party. The API drops holdings with another owner or instrument, but it cannot see locks. A locked holding that you pass can be selected, and the transaction then fails on submit.
  • Set instrumentId to the structured fee.instrumentId that POST /v1/register-canton-party returns, and pass it through unchanged. Do not derive it from the token address. That call returns fee only for an unregistered party. For a registered party, use the instrument ID that your node reports on the Amulet holding.
  • Set amount to the holding’s current amount as a decimal string in CC, as your node reports it, for example "12.5", with at most 10 decimal places. Do not use base units.
  • Pass at most 200 holdings. More returns HTTP 400 Invalid request.
  • A send uses at most 100 holdings across the amount and the fee. If you have many small holdings, merge them: send your CC to yourself from an external party, which consolidates the inputs into one holding plus change.

Register a Receiving Party

The receiving party’s own Canton wallet must register it. If you let users send CC from BSC, HyperEVM, or Solana into Canton, your integration must connect each user’s Canton wallet. Before the first inbound send, that wallet signs the registration transaction that POST /v1/register-canton-party prepares, and the fee is paid in CC from the party’s holdings on Canton. The Value Transfer API cannot register a party from the source chain, and no API call registers a party automatically as part of a send. A user whose CC is only on BSC, HyperEVM, or Solana must first get CC into a Canton wallet that can sign, and register there.Connect the wallet with a CIP-0103 provider, such as @canton-network/dapp-sdk, or with Loop’s SDK for Loop. Pass the transaction from /v1/register-canton-party to the wallet’s prepareExecute unchanged, with your own commandId, and wait for the executed event before you quote.
Registration maps your party’s fingerprint to your full party ID in the CC adapter’s registry. The party signs it with its own wallet or key, in a Canton transaction separate from any send. Register once per party per token. For parties that share a namespace, see the warning under Prerequisites.
  1. Check the status and the fee. Call POST /v1/register-canton-party with partyId and token, without holdings.
    • {"status": "registered", "partyId": "<partyId>"} means no action is needed.
    • {"status": "unregistered", "fee": {...}} gives the fee in CC. If the fee is zero, this response already includes the transaction. Go to step 3.
    This call reports only the registration status and the fee. Package vetting, an unknown party, and full registries are reported in step 2. A 409 here means the fingerprint is registered to another party.
  2. Get the registration transaction. Call the endpoint again with holdings that cover the fee. The response includes a transaction to sign.
  3. Sign and submit the transaction as the party, as in Submit on Canton.

Submit on Canton

The Canton transaction is a CIP-0103 prepareExecute request. In a build step, it is transaction.encoded. In a registration response, it is transaction. It has one command (OApp_LzSend for a send, SetPartyId for a registration), actAs, readAs, and the disclosedContracts that the command needs.
  • Internal party (your node holds the key): submit the command with its disclosed contracts through your node’s Ledger API.
  • External party (you hold the key): use Canton’s interactive submission. Prepare the transaction with the command and its disclosedContracts, sign the returned hash with the party key, then execute it. Take the update ID from the completion for your commandId. /v1/status needs it.
The request has no commandId. Set your own unique commandId when you submit, and keep the same commandId if you resubmit the same send. The request also has no synchronizerId. It is optional: set it to the Global Synchronizer ID, or leave it unset and your node picks a synchronizer that it is connected to. Do not change the command or its package ID. You do not need your own idempotency or deduplication checks on Canton. The transaction spends specific holdings, so only one submission of it can commit. Track each send with /v1/status and LayerZero Scan, as in Endpoint Sequence. While a send is pending, do not build a replacement from other holdings. Sign and submit within a few minutes of the build. The transaction carries a timestamp that the API sets when it builds the transaction. The LayerZero contracts reject it once ledger time is more than 10 minutes past that timestamp. If submission fails with a timestamp-in-the-future error, wait a few seconds and resubmit the same transaction. Contract IDs read at build time can also go stale. If submission fails with a stale or archived contract, request a new quote, read your holdings again, and build again. Calling /v1/build-user-steps again with the old quoteId returns the same transaction. A build that failed is not stored, so you can retry a failed build with the same quoteId.

One Send per Transaction

Each quote gives one command. Submit it as its own Canton transaction. Do not merge commands from several builds into one transaction.
  • The API picks holdings the same way each time and does not reserve them. Two builds from the same holdings list pick the same holdings, so the second send fails.
  • Transfer tracking expects one LayerZero send per Canton transaction.
To send in parallel from one party, give each build a different set of holdings. This works only for a party whose holdings the Splice wallet automation does not merge, such as an external party. Or send one at a time, and read your holdings again after each send commits. A spent holding returns change with a new contract ID.

No Native Drop into Canton

Do not set options.dstNativeDropAmount on a route into Canton. Leave it at the default of 0. The fee for a native drop is charged, but the drop is not delivered on Canton.

Deposit Directly to HyperCore

HyperCore is a destination of the Value Transfer API. You can deposit CC to HyperCore in one transaction from Canton, BSC, Solana, or HyperEVM. Call POST /v1/quotes as on any route, with these destination fields: An address is activated by its first incoming transfer on HyperCore, which costs 1 USDC. If the address is not activated, the API returns no route. Activate the address first. From Canton, BSC, or Solana, the API builds an OFT send to the LayerZero Hyperliquid Composer on HyperEVM, with the compose message and compose options. The executor delivers the CC to the composer on HyperEVM, and the composer moves it to the receiver on HyperCore. From HyperEVM, the API builds a transfer to the Hyperliquid asset bridge. Call /v1/build-user-steps and sign the steps as on any other route.
If you build or change the transaction yourself. LayerZero does not support Canton transactions from your own transaction builder, or changes to the encoding of a transaction that the Value Transfer API built. A self-built send must set to to the composer 0xccEE9Cad5C58Ad64113629Eb8921c9E948E10A0C, include compose gas, and carry composeMsg = abi.encode(uint256 minMsgValue, address hyperCoreReceiver), exactly 64 bytes. If the receiver is not activated on HyperCore, the composer sends the full amount as CC to the same address on HyperEVM. If the compose message does not decode, the composer holds the CC. Anyone can return it to the sender on the source chain with refundToSrc(guid), paying the LayerZero messaging fee. From Canton, the refund settles only if the sending party is registered. If compose gas is below 150,000 (200,000 with HYPE), the compose reverts and can be retried. For help, contact the LayerZero team with the source transaction hash.
To leave HyperCore, hold at least 0.001 HYPE on HyperCore for gas, and spot-send LZCC to the asset bridge 0x20000000000000000000000000000000000004E7, or quote HyperCore to HyperEVM with the same sender and receiver address. The CC appears at the same address on HyperEVM. Then bridge from HyperEVM.

Errors

Error responses carry code and message, and most also carry data. Match on the HTTP status and message. For HTTP 400 Invalid request, read data.validationErrors. The names in the first column are labels for this page only. The API does not send them. Submission errors come from your node, not the API. For a stale or archived contract, quote, read your holdings, and build again. For a package that is not vetted, vet the full package set on every hosting node. For insufficient traffic, top up your validator’s traffic. For delivery problems after submission, see Canton Coin Troubleshooting.