BurnMint Token Pool Deployment (Canton)

Deploy a BurnMint token pool on Canton, register it on the Token Admin Registry (TAR), and enable a cross-chain lane. Use the ccip-starter-kit-canton for pool deployment and TAR registration, or submit Ledger API commands directly.

This guide assumes you already have a token instrument live on Canton with supply minted on-ledger — for example a Registry instrument from Digital Asset's Registry utility, or any instrument supporting CIP-56 with the BurnMintFactory interface.

Scope: On-ledger pool connection — deploy, TAR registration, and lane enablement. Does not cover EDS setup or end-to-end transfer tests.

Prerequisites

  • Instrument on Canton with known InstrumentId = { admin, id }. The admin party must be the same party that will own the token pool.
  • Burn/mint authorityinstrumentId.admin == poolOwner (enforced by the BurnMint template and by the starter kit).
  • CCIP DAR packages on your participant from contracts/dars/v2_0_0.
  • CCIP contract references from Chainlink ops: CCIP owner party, Token Admin Registry, RMNRemote, FeeQuoter.
  • Starter kit setup (if using cct:* scripts) — complete Canton as Source prerequisites and the CCT overview.

Deployment overview

StepActionStarter kitManual
1Deploy BurnMintTokenPoolcct:deploy-burn-mintLedger API CreateCommand
2Register on TARcct:set-tokenTAR choices via Ledger API
3Enable laneRate limiters + ApplyChainUpdates
4Go liveEDS + verification

Step 1 — Deploy the BurnMint Token Pool

Create one BurnMintTokenPool. poolOwner + instanceId uniquely identifies the pool. Signatory: poolOwner.

From the starter kit root:

Terminal
npm run cct:deploy-burn-mint -- \
  --instanceId acme-eur-bm-pool \
  --instrument 'yourParty::1220…::token-id'

Flags:

FlagRequiredDescription
--instanceIdYesUnique pool instance ID for your party (e.g. acme-eur-bm-pool)
--instrumentYesCanton instrument ID: party::fingerprint::tokenId
--poolOwnerNoDefaults to party in canton-config.json (must equal instrument admin)
--ccipOwnerNoDefaults to ccipParty in canton-config.json
--decimalsNoToken decimals on Canton (default 10)

Expected output:

Deploying BurnMint token pool on Canton
   instanceId: acme-eur-bm-pool
   instrument: yourParty::1220…::token-id
   poolOwner:  yourParty::1220…
Canton JWT obtained via client credentials.
✅ BurnMintTokenPool contract ID: 00abc…
   Pool address: acme-eur-bm-pool@yourParty::1220…
   Update: https://lighthouse.testnet.cantonloop.com/transactions/…

Next step: register the pool on the Token Admin Registry:
   npm run cct:set-token -- --instrument "yourParty::1220…::token-id" --poolInstanceId "acme-eur-bm-pool" --poolOwner "yourParty::1220…"

Save the pool address ({instanceId}@{poolOwner}) and contract ID for verification.

Transfer-fee fields

When setting tokenTransferFeeConfigs (map keyed by destination chain selector):

FieldMeaning
isEnabledMust be true to apply the config
feeUSDCentsFlat fee in USD cents (non-negative)
destGasOverheadGas for destination execution (> 0 when set via choice)
destBytesOverheadData-availability bytes overhead (≥ 32)
feeBpsProportional fee in basis points (< 10000)

CCIP hosted addresses

FieldTestnetMainnet
Token Admin Registrytokenadminregistry-lzrnd@ccipOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551tokenadminregistry-ckswd@ccipOwner::122012714685760dc1927c4cfe119ce2126c48756154e95c06f5c181da05a5519093
RMNRemotermn_remote-nzvtd@rmnOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551rmn_remote-mnprd@rmnOwner::122012714685760dc1927c4cfe119ce2126c48756154e95c06f5c181da05a5519093
FeeQuoterfeequoter-scxln@ccipOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551feequoter-fpxih@ccipOwner::122012714685760dc1927c4cfe119ce2126c48756154e95c06f5c181da05a5519093
CCIP owner partyccipOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551ccipOwner::122012714685760dc1927c4cfe119ce2126c48756154e95c06f5c181da05a5519093
Global EDShttps://eds.testnet.ccip.chain.linkhttps://eds.ccip.chain.link
Indexerhttps://indexer-1.testnet.ccip.chain.linkhttps://indexer-1.ccip.chain.link (backup: indexer-2)
Canton chain selector92687312186494980742308837218439511688
Default CommitteeVerifiercommitteeverifier-tqkny@ccvOwner::1220e382f4e57b0815e6be737006e381e6b7de448e06bd033ece6df498017879f551committeeverifier-mnprd@ccvOwner::122096accf0a84fc7d80d5fce5ea3135317a03eb22e62e0d8cdd7548865f984f11ff

Step 2 — Register on the Token Admin Registry

TAR maps each instrument to a single token pool. Registration: propose → accept → set pool.

Terminal
npm run cct:set-token -- \
  --instrument 'yourParty::1220…::token-id' \
  --poolInstanceId acme-eur-bm-pool

Flags:

FlagRequiredDescription
--instrumentYesSame instrument ID used in Step 1
--poolInstanceIdYesinstanceId from Step 1
--poolOwnerNoParty that owns the pool contract (defaults to party in canton-config.json)
--poolAdminNoTAR admin party (defaults to poolOwner)
--proposeCallerNoParty that calls ProposeAdministrator (defaults to instrument admin)
--tokenConfigCidNoResume at AcceptAdminRole if ProposeAdministrator already succeeded
--skipAcceptNoResume at SetPool only (skip propose and accept)

The script fetches TAR disclosures from Global EDS, then submits all three TAR choices in sequence.

Expected output:

Registering token pool on the Token Admin Registry (TAR)
   instrument:     yourParty::1220…::token-id
   poolInstanceId: acme-eur-bm-pool
   pool address:   acme-eur-bm-pool@yourParty::1220…
Step 1/3 — ProposeAdministrator on Token Admin Registry
   tokenConfigCid: 00def…
Step 2/3 — AcceptAdminRole on Token Admin Registry
Step 3/3 — SetPool on Token Admin Registry
✅ Token pool registered for yourParty::1220…::token-id
   tokenConfigCid: 00ghi…

Next step: enable a cross-chain lane (rate limiters + ApplyChainUpdates on the pool).

Step 3 — Enable a lane

Lane enablement is not automated by the starter kit. Deploy rate limiters and call ApplyChainUpdates on your pool via the Ledger API.

1. Deploy three rate limiters

Each lane requires RateLimiter contracts (for testing, deploy disabled with zero capacity/rate):

  • Inbound (default finality)
  • Inbound (custom finality)
  • Outbound

Created by poolOwner with matching poolInstanceId, poolOwner, and remoteChainSelector.

2. Apply the chain update

Call ApplyChainUpdates on the pool:

ChainUpdate fieldGuidance
remoteChainSelectorDestination chain selector — see CCIP Directory
remotePoolsRemote token pool address(es)
remoteTokenAddressRemote token address
inboundCCVs / outboundCCVsDefault CommitteeVerifier (testnet address above) unless custom
finalityConfigWaitForFinality for default finalized source tx
inboundRateLimiterInbound default limiter CID
inboundCustomBlockConfirmationsRateLimiterInbound custom limiter CID
outboundRateLimiterOutbound limiter CID

Once applied, the on-ledger lane is complete.

Step 4 — Go live

Verification checklist

  1. InstrumentId.admin equals pool owner.
  2. TAR maps your instrument to your pool with your party as admin.
  3. Pool exists at {instanceId}@{poolOwner} with matching InstrumentId.
  4. Lane rate limiters are in place.

Stand up EDS for your pool

Users need explicit disclosures for your pool to send and execute transfers. The reference EDS Docker image supports BurnMint out of the box.

Test transactions

See CCIP on Canton — Overview for user flows. Track messages in CCIP Explorer.

For Registry-issued tokens, also complete SetBurnMintFactory with your AllocationFactory contract ID.

What's next

Get the latest Chainlink content straight to your inbox.