> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/circlefin/evm-cctp-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# TokenController

> Manages token pair mappings and burn limits for cross-chain transfers

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

```solidity theme={null}
mapping(address => uint256) public 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

```solidity theme={null}
mapping(bytes32 => address) public 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

```solidity theme={null}
address private _tokenController
```

Private state variable storing the address of the token controller.

## Functions

### linkTokenPair

```solidity theme={null}
function linkTokenPair(
    address localToken,
    uint32 remoteDomain,
    bytes32 remoteToken
) external onlyTokenController
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L126)

**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

```solidity theme={null}
function unlinkTokenPair(
    address localToken,
    uint32 remoteDomain,
    bytes32 remoteToken
) external onlyTokenController
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L157)

**Note:** Unlinking does not disable burning the local token (must separately call `setMaxBurnAmountPerMessage` with 0)

### setMaxBurnAmountPerMessage

```solidity theme={null}
function setMaxBurnAmountPerMessage(
    address localToken,
    uint256 burnLimitPerMessage
) external onlyTokenController
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L186)

**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

```solidity theme={null}
function tokenController() external view returns (address)
```

Returns the address of the token controller.

**Returns:** Address of the current token controller

**Source:** [TokenController.sol:113](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L113)

## Internal Functions

### \_setTokenController

```solidity theme={null}
function _setTokenController(address newTokenController) internal
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L202)

### \_getLocalToken

```solidity theme={null}
function _getLocalToken(
    uint32 remoteDomain,
    bytes32 remoteToken
) internal view returns (address)
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L217)

### \_hashRemoteDomainAndToken

```solidity theme={null}
function _hashRemoteDomainAndToken(
    uint32 remoteDomain,
    bytes32 remoteToken
) internal pure returns (bytes32)
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L235)

## Modifiers

### onlyTokenController

```solidity theme={null}
modifier 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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L82)

### onlyWithinBurnLimit

```solidity theme={null}
modifier onlyWithinBurnLimit(address token, uint256 amount)
```

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](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/roles/TokenController.sol#L98)

## Events

### TokenPairLinked

```solidity theme={null}
event TokenPairLinked(
    address localToken,
    uint32 remoteDomain,
    bytes32 remoteToken
)
```

Emitted when a token pair is linked.

**Parameters:**

* `localToken`: Local token address
* `remoteDomain`: Remote domain ID
* `remoteToken`: Remote token address as bytes32

### TokenPairUnlinked

```solidity theme={null}
event TokenPairUnlinked(
    address localToken,
    uint32 remoteDomain,
    bytes32 remoteToken
)
```

Emitted when a token pair is unlinked.

### SetBurnLimitPerMessage

```solidity theme={null}
event SetBurnLimitPerMessage(
    address indexed token,
    uint256 burnLimitPerMessage
)
```

Emitted when a burn limit is set for a token.

**Parameters:**

* `token`: Local token address
* `burnLimitPerMessage`: New burn limit per message

### SetTokenController

```solidity theme={null}
event SetTokenController(address tokenController)
```

Emitted when the token controller address is set.

**Parameters:**

* `tokenController`: New token controller address

## Usage Examples

### Setting Up a New Token

```solidity theme={null}
// Step 1: Link token pair (as token controller)
// Link local USDC to Ethereum USDC
tokenController.linkTokenPair(
    0xLOCAL_USDC_ADDRESS,
    0, // Ethereum domain ID
    0x000000000000000000000000ETHEREUM_USDC_ADDRESS
);

// Step 2: Set burn limit to enable deposits
// Allow burning up to 1 million USDC per message
tokenController.setMaxBurnAmountPerMessage(
    0xLOCAL_USDC_ADDRESS,
    1_000_000 * 10**6 // 1M USDC (6 decimals)
);
```

### Updating Configuration

```solidity theme={null}
// Reduce burn limit
tokenController.setMaxBurnAmountPerMessage(
    0xLOCAL_USDC_ADDRESS,
    100_000 * 10**6 // 100K USDC
);

// Temporarily disable burning (set to 0)
tokenController.setMaxBurnAmountPerMessage(
    0xLOCAL_USDC_ADDRESS,
    0
);

// Unlink token pair
tokenController.unlinkTokenPair(
    0xLOCAL_USDC_ADDRESS,
    0, // Ethereum domain ID
    0x000000000000000000000000ETHEREUM_USDC_ADDRESS
);
```

### Checking Configuration

```solidity theme={null}
// Check burn limit
uint256 limit = tokenMinter.burnLimitsPerMessage(0xLOCAL_USDC_ADDRESS);

// Check if remote token is linked
bytes32 key = keccak256(abi.encodePacked(
    uint32(0), // Ethereum domain
    bytes32(uint256(uint160(0xETHEREUM_USDC_ADDRESS)))
));
address localToken = tokenMinter.remoteTokensToLocalTokens(key);
```

## 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:

```solidity theme={null}
// Convert EVM address to bytes32
bytes32 remoteToken = bytes32(uint256(uint160(evmAddress)));

// Extract EVM address from bytes32
address evmAddress = address(uint160(uint256(remoteToken)));
```

For non-EVM chains, the bytes32 format is used directly without conversion.
