Empowering Developers With The CCIP Cross-Chain Token (CCT) Standard
With the release of CCIP v1.5, token developers are equipped with powerful tools to seamlessly deploy and manage cross-chain tokens. This upgrade introduces several key features designed to accelerate cross-chain integrations across decentralized finance (DeFi) and traditional finance (TradFi):
- Self-Serve Token Onboarding: Developers can now create cross-chain tokens in minutes using a self-serve, permissionless, and secure process. This empowers projects of all sizes to leverage the Cross-Chain Token (CCT) standard, making tokens cross-chain native with ease.
- CCIP Token Manager: The new CCIP Token Manager simplifies cross-chain token deployment and management with an intuitive interface. Token developers can register, configure, launch, and oversee tokens across multiple blockchain networks, giving them full control and autonomy.
- CCIP SDK: To make integrations even easier, the CCIP SDK provides developers with pre-built functionality that enables them to integrate CCIP’s cross-chain capabilities in just a few lines of code. This reduces complexity and speeds up time-to-deployment for cross-chain applications.
- Enhanced Security and Privacy: The CCIP v1.5 upgrade also introduces critical security enhancements, including upgraded defense-in-depth measures and protections against potential exploits.
Token Standards and Requirements for Creating a Cross-Chain Token (CCT) Standard
As part of the CCIP v1.5 upgrade, developers can now create CCIP-enabled ERC-20 tokens with ease. These tokens leverage the Cross-Chain Token (CCT) standard, allowing them to become cross-chain native while maintaining security and flexibility.
Overview of the Cross-Chain Token (CCT) Standard
The Cross-Chain Token (CCT) standard introduces a streamlined, decentralized way for tokens to transfer across blockchains using Chainlink’s Cross-Chain Interoperability Protocol (CCIP). Previously, token developers had to navigate a time-intensive, manual process, including the deployment of token pools for each blockchain. CCTs now eliminate this friction by allowing developers to deploy, configure, and manage token pools through a straightforward interface. This self-serve approach accelerates deployment and enhances developers’ control over cross-chain token operations. Below, we’ll dive into the requirements for enabling an ERC20-compatible token in CCIP and explore the architectural components of CCT.
Requirements for a CCIP-Enabled ERC-20 Token
To integrate an ERC-20-compatible token with CCIP, developers must ensure the following conditions are met:
- Permissionless Token Administrator Registration:
- Supported Functions: The token contract should use one of these supported function signatures to register an administrator:
owner()
: Returns the token contract owner’s address.getCCIPAdmin()
: Returns the token administrator’s address, which is recommended for new tokens to separate the CCIP Token Administrator role from other roles likeowner()
.
- If neither of these functions is implemented, developers can register their token by submitting a form provided by Chainlink.
- Supported Functions: The token contract should use one of these supported function signatures to register an administrator:
- Burn & Mint Requirements:
- mint(address account, uint256 amount): Mints a specified amount of tokens to a given account on the destination blockchain.
- burn(uint256 amount): Burns a specified amount of tokens on the source blockchain.
- decimals(): Returns the token’s number of decimals.
- balanceOf(address account): Retrieves the current token balance of a specified account.
- Optional Function – burnFrom(address account, uint256 amount): Burns tokens from a specific account on the source blockchain. Although optional, this function is useful if a token pool requires
burnFrom
rather thanburn
.
- Granting Mint and Burn Permissions:
- Token contracts on both the source and destination blockchains must support granting mint and burn permissions. Typically, these permissions are granted by the token developer or an administrator role to the token pool.
- Lock & Mint Requirements:
- The token contract should implement:
decimals()
balanceOf(address account)
- On the destination blockchain, mint and burn permissions must be granted to the token pool.
- The token contract should implement:
- Using Chainlink’s BurnMintERC677 for New Tokens:
- If a developer does not have an existing token, they can use Chainlink’s BurnMintERC677 contract, which implements the ERC677 or ERC777 standard, to quickly deploy a token compatible with CCIP. This contract can be used as-is or extended for custom requirements.
Understanding Token Pools in CCT
In CCIP, token pools serve as intermediary vaults that securely store and manage tokens during cross-chain transfers. Developers can choose from several types of token pools based on their needs:
- Burn/Mint:
- Tokens are burned on the source blockchain and minted on the destination blockchain. This type is commonly used for ERC-20 tokens that need a fixed supply across chains.
- Lock/Mint:
- Tokens are locked in a pool on the source blockchain and minted on the destination blockchain. This type is beneficial for tokens that are non-burnable or need to maintain a specific balance on the source chain.
- Burn/Unlock:
- Tokens are burned on the source blockchain, allowing for an equivalent amount of tokens to be unlocked on the destination blockchain. This method is suitable for tokens with a fixed cross-chain supply.
- Lock/Unlock:
- Tokens are locked on the source blockchain and unlocked on the destination blockchain. This pool type is commonly used for non-transferable or utility tokens where a mirrored balance is needed across blockchains.
These token pool options allow developers to select the best mechanism for their cross-chain token strategy, enabling secure, flexible, and compliant cross-chain transactions.
Requirements for Token Pools in CCIP
All token pools, whether standard or custom, must adhere to specific guidelines to ensure seamless interactions with Chainlink’s Cross-Chain Interoperability Protocol (CCIP). The following requirements are essential for enabling reliable cross-chain token transfers.
Mandatory Functions for Source and Destination Blockchains
To facilitate token movements, the token pool must implement specific functions for both the source and destination blockchains:
- Source Blockchain Requirement:
lockOrBurn(Pool.LockOrBurnInV1 calldata lockOrBurnIn) external returns (Pool.LockOrBurnOutV1 memory)
: This function is responsible for locking or burning tokens on the source blockchain.
- Destination Blockchain Requirement:
releaseOrMint(Pool.ReleaseOrMintInV1 calldata releaseOrMintIn) external returns (Pool.ReleaseOrMintOutV1 memory)
: This function releases or mints tokens on the destination blockchain.
Gas Limit on the Destination Blockchain
When tokens are released or minted on the destination blockchain, the CCIP OffRamp contract performs three essential calls to ensure accurate balance adjustments:
- Pre-minting/releasing balance check: The
balanceOf
function checks the token balance of the recipient before any minting or releasing operation. - Executing releaseOrMint: The main
releaseOrMint
function call executes the release or mint operation, with both the token pool’s logic and the token’s execution logic (e.g., gas costs associated with minting) included in this process. - Post-minting/releasing balance check: Another
balanceOf
function call verifies the recipient’s balance after the release or minting operation.
These three calls together should not exceed the destination blockchain’s default gas limit of 90,000 gas. To confirm that this limit is maintained, it is recommended to test a token transfer on a testnet. If the combined execution requires more than 90,000 gas, and no custom gas limit has been configured, the CCIP execution will fail on the destination blockchain. In this case, manual intervention will be necessary.
Minting a New CCT-Enabled Token
Example Contract Code for CCIP Integration
Below is an example of how you can implement a CCIP-enabled ERC-20 token based on OpenZeppelin’s libraries. To allow for more granular control over roles like the Minter, we recommend using OpenZeppelin’s AccessControl instead of the more traditional Ownable pattern.
- CCIP Admin Management: A variable to store the CCIPAdmin address must be exposed via a publicly scoped getter function getCCIPAdmin (address CCIPadmin). Additionally, setCCIPAdmin (address CCIPAdmin) must be callable by the token owner or admin, with an event emitted when the admin is set.
Below is an example of how you can implement a CCIP-enabled ERC-20 token based on OpenZeppelin’s libraries:
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {IBurnMintERC20} from "@chainlink/contracts-ccip/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol"; import { ERC20, ERC20Burnable } from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import {AccessControl} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/access/AccessControl.sol"; contract MyToken is IBurnMintERC20, ERC20Burnable, AccessControl { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); address internal s_CCIPAdmin; event CCIPAdminSet(address indexed ccipAdmin); constructor() ERC20("MyToken", "MTK") { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(MINTER_ROLE, msg.sender); _grantRole(BURNER_ROLE, msg.sender); s_CCIPAdmin = msg.sender; } function mint(address account, uint256 amount) public onlyRole(MINTER_ROLE) { _mint(account, amount); } function burn(uint256 amount) public override(IBurnMintERC20, ERC20Burnable) onlyRole(BURNER_ROLE) { super.burn(amount); } function burnFrom(address account, uint256 amount) public override(IBurnMintERC20, ERC20Burnable) onlyRole(BURNER_ROLE) { super.burnFrom(account, amount); } function burn(address account, uint256 amount) public virtual override { burnFrom(account, amount); } function getCCIPAdmin() external view returns (address) { return s_CCIPAdmin; } function setCCIPAdmin(address ccipAdmin) external onlyRole(DEFAULT_ADMIN_ROLE) { s_CCIPAdmin = ccipAdmin; emit CCIPAdminSet(ccipAdmin); } }
Cross-Chain Token (CCT) Tutorial
We have created a new tutorial to help you get up to speed on CCT. The tutorial covers the key actions required to enable a token for cross-chain transfers, serving as the foundation you will need moving forward. Regardless of whether you’re using an Externally Owned Account (EOA) or a Smart Account (such as a multisig setup), the overall process remains consistent. You’ll follow the same steps to enable cross-chain token transfers, configure token pools, and assign administrative roles.
Here is a video tutorial that will walk you step-by-step through this process with me:
This tutorial is designed to work seamlessly with both EOAs and Smart Accounts, ensuring a flexible, comprehensive approach to enabling your token for cross-chain functionality.