Skip to main content
The TokenController contract provides token configuration management for CCTP, including mapping local tokens to remote tokens across domains and setting per-message burn limits. Contract: src/roles/TokenController.sol

Key Concepts

  • Token Pairs: Links between local tokens and their corresponding remote domain tokens
  • Burn Limits: Maximum amount of each token that can be burned per message
  • Token Controller Role: Authorized address that manages token configuration

State Variables

burnLimitsPerMessage

Maps local token addresses to their maximum burn amount per message. Key: Local token address
Value: Maximum burn amount per message (in token’s smallest unit)
Usage: A burn limit of 0 means the token is not supported for burning.

remoteTokensToLocalTokens

Maps remote domain and token pairs to local token addresses. Key: keccak256(abi.encodePacked(remoteDomain, remoteToken))
Value: Local token address
Usage: Enables validation and minting when receiving messages from remote domains.

_tokenController

Private state variable storing the address of the token controller.

Functions

linkTokenPair

Links a pair of local and remote tokens to be supported by the TokenMinter. Parameters:
  • localToken: Address of the local token
  • remoteDomain: Domain ID of the remote chain
  • remoteToken: Address of the token on the remote domain (as bytes32)
Requirements:
  • Caller must be the token controller
  • Remote token must not already be linked to a local token
Emits: TokenPairLinked(address localToken, uint32 remoteDomain, bytes32 remoteToken) Source: TokenController.sol:126 Note:
  • A remote token can only map to one local token
  • Multiple remote tokens can map to the same local token
  • Linking does not enable deposits (must also call setMaxBurnAmountPerMessage)

unlinkTokenPair

Removes the link between a local token and a remote token. Parameters:
  • localToken: Address of the local token
  • remoteDomain: Domain ID of the remote chain
  • remoteToken: Address of the token on the remote domain (as bytes32)
Requirements:
  • Caller must be the token controller
  • Token pair must currently be linked
Emits: TokenPairUnlinked(address localToken, uint32 remoteDomain, bytes32 remoteToken) Source: TokenController.sol:157 Note: Unlinking does not disable burning the local token (must separately call setMaxBurnAmountPerMessage with 0)

setMaxBurnAmountPerMessage

Sets the maximum burn amount per message for a given local token. Parameters:
  • localToken: Address of the local token
  • burnLimitPerMessage: Maximum burn amount per message (0 to disable burning)
Requirements:
  • Caller must be the token controller
Emits: SetBurnLimitPerMessage(address indexed token, uint256 burnLimitPerMessage) Source: TokenController.sol:186 Important:
  • Burns exceeding this limit will revert
  • Mints do not respect this limit
  • Reducing the limit does not affect previously burned tokens (they can still be minted)
  • Setting to 0 disables burning for that token

tokenController

Returns the address of the token controller. Returns: Address of the current token controller Source: TokenController.sol:113

Internal Functions

_setTokenController

Internal function to set the token controller address. Parameters:
  • newTokenController: Address of the new token controller
Requirements:
  • New token controller must be non-zero address
Emits: SetTokenController(address tokenController) Source: TokenController.sol:202

_getLocalToken

Gets the local token associated with a remote domain and token. Parameters:
  • remoteDomain: Domain ID of the remote chain
  • remoteToken: Address of the token on the remote domain (as bytes32)
Returns: Local token address, or zero address if not linked Source: TokenController.sol:217

_hashRemoteDomainAndToken

Hashes a remote domain and token address together. Parameters:
  • remoteDomain: Domain ID of the remote chain
  • remoteToken: Address of the token on the remote domain (as bytes32)
Returns: Keccak256 hash of packed remote domain and token Source: TokenController.sol:235

Modifiers

onlyTokenController

Restricts function access to the token controller only. Reverts: “Caller is not tokenController” if caller is not the token controller Source: TokenController.sol:82

onlyWithinBurnLimit

Ensures that a burn operation does not exceed the per-message burn limit. Parameters:
  • token: Address of the token to burn
  • amount: Amount to burn
Reverts:
  • “Burn token not supported” if burn limit is 0
  • “Burn amount exceeds per tx limit” if amount exceeds limit
Source: TokenController.sol:98

Events

TokenPairLinked

Emitted when a token pair is linked. Parameters:
  • localToken: Local token address
  • remoteDomain: Remote domain ID
  • remoteToken: Remote token address as bytes32

TokenPairUnlinked

Emitted when a token pair is unlinked.

SetBurnLimitPerMessage

Emitted when a burn limit is set for a token. Parameters:
  • token: Local token address
  • burnLimitPerMessage: New burn limit per message

SetTokenController

Emitted when the token controller address is set. Parameters:
  • tokenController: New token controller address

Usage Examples

Setting Up a New Token

Updating Configuration

Checking Configuration

Security Considerations

  • Token controller should be a trusted address (e.g., multisig or DAO)
  • Burn limits should be set based on liquidity and risk assessment
  • A token pair can only be linked once (prevents conflicting mappings)
  • Minting is not limited by burn limits (asymmetric by design)
  • Setting burn limit to 0 effectively disables deposits for that token
  • Unlinking a token pair does not automatically disable burning
  • Remote tokens are stored as bytes32 to support non-EVM chains
  • Multiple remote tokens can map to the same local token (useful for wrapped tokens)

Integration with TokenMinter

The TokenMinter contract inherits from TokenController and uses these functions in:
  • depositForBurn(): Checks burn limit via onlyWithinBurnLimit modifier
  • handleReceiveMessage(): Uses _getLocalToken() to determine which token to mint
  • Administrative setup: Owner uses inherited functions to configure supported tokens

Remote Token Address Format

Remote tokens are represented as bytes32 to support both EVM and non-EVM chains:
For non-EVM chains, the bytes32 format is used directly without conversion.