Enable your tokens in CCIP (Burn & Mint): Register from Safe multisig using Hardhat

This tutorial will guide you through enabling your tokens in CCIP using Hardhat and Safe Multisig smart accounts. You will learn how to deploy tokens and set up Burn & Mint token pools using a 2-of-3 multi-signature Safe. After that, you will register the tokens in CCIP and configure them using multisig transactions without needing manual intervention. Finally, you will test the Burn & Mint token handling mechanism, where tokens are burned on the source blockchain, and an equivalent amount is minted on the destination blockchain.

Introduction to Smart Accounts and Safe Multisig

Introduction

A smart account (also known as a smart contract account) leverages the programmability of smart contracts to extend their functionality and improve their security compared to externally owned accounts (EOAs). Smart accounts are controlled by one or multiple EOAs or other smart accounts, and all transactions must be initiated by one of these controllers.

Some common features of smart accounts include:

  • Multi-signature schemes: Require multiple approvals for a transaction to be executed, enhancing security.
  • Transaction batching: Combine multiple actions into a single transaction, reducing costs and improving efficiency.
  • Account recovery: Allow for recovery mechanisms in case of lost access.
  • Gasless transactions: Enable transaction fees to be paid by a third party or relayer.

Safe is one of the most trusted implementations of a smart account, offering a robust multi-signature mechanism. In this tutorial, we will use a Safe Multisig account, specifically a 2-of-3 multi-signature setup, where two out of three owners must approve a transaction to execute it. This setup ensures enhanced security and decentralized control over the assets.

The Protocol Kit from Safe allows developers to interact with Safe, smart accounts through a TypeScript interface. This kit can be used to create new Safe accounts, update configurations, propose transactions, and execute them, making it an ideal choice for blockchain projects.

How Safe Multisig Works

Before we proceed, let's take a moment to understand how the multisig transaction process works with a Safe smart account.

In this tutorial, we'll use a 2-of-3 multi-signature setup as an example, where at least two out of the three Smart Account Owners must approve each transaction. Depending on your project's needs, this multisig process is valid for any scheme (e.g., 3-of-5, 4-of-7). In Safe smart accounts, the Smart Account Owners are responsible for approving actions. Transactions are signed off-chain by the required number of owners, which improves efficiency and reduces gas costs. Once the necessary threshold is met, the signed transactions are submitted to the blockchain for execution. The Safe smart account (a smart contract) verifies the signatures and enforces the required number of approvals before executing the transaction. This process enhances security, particularly for sensitive tasks such as registering a token administrator or configuring a token pool. Multi-signature ensures that no single owner can act alone.

The steps for enabling your tokens in CCIP follow the same flow as the previous tutorials that used externally owned accounts (EOA). The key difference here is that for sensitive actions like token administrator registration or token pool configuration, we assume your project uses a Safe multisig. Therefore, multiple signatures are required off-chain; in some cases, a batch of transactions will be submitted to save on gas costs.

The following sequence diagram illustrates the multisig transaction flow in a Safe smart account, from off-chain signature collection to on-chain execution:

How Safe Multisig works

In this diagram, the process is as follows:

  1. Signer 1 initiates the batch of transactions (one or multiple transactions) and signs off-chain.
  2. Signer 1 shares the batch with Signer 2, who signs it off-chain.
  3. Once the required number of signatures is collected (in this case, two), the batch is submitted for execution.
  4. The Smart Account verifies the signatures and checks that the signature threshold has been met.
  5. The Smart Account then executes each transaction in the batch, interacting with the Target Smart Contract(s).
  6. If any transaction fails, the entire batch is reverted. If all transactions succeed, they are confirmed on-chain.

By following this process, we maintain the security of multisig transactions while improving efficiency through the off-chain signature collection and gas savings from transaction batching.

Steps covered in this tutorial

We will cover the following key steps:

  1. Creating a Safe Account: You will create a 2-of-3 multi-signature Safe that will serve as the owner of the token and token pool contracts. This Safe will also manage administrative tasks, such as configuring token pools and registering as the token admin in the token admin registry.

  2. Deploying Tokens: You will deploy your BurnMintERC20 tokens on the Ethereum Sepolia and BASE Sepolia testnets and transfer ownership to the Safe account.

  3. Deploying Token Pools: Once your tokens are deployed, you will deploy BurnMintTokenPool token pools on Ethereum Sepolia and BASE Sepolia. The Safe account will own each token pool.

  4. Claiming and Accepting the Admin Role: This is a two-step process that will be managed using the Safe multi-signature account. It involves creating and signing multiple meta-transactions off-chain before executing them on-chain to register Safe as the token admin and accept the admin role for managing the tokens and token pools.

    1. You will call the RegistryModuleOwnerCustom contract's registerAdminViaGetCCIPAdmin function to register the Safe as the token admin. This role is required to enable your token in CCIP.

    2. Once claimed, you will call the TokenAdminRegistry contract's acceptAdminRole function to complete the registration process.

    Meta-transactions are used here to batch these two actions, allowing both steps to be executed efficiently. The meta-transactions are created off-chain and signed by each of the two required Safe owners. This off-chain signing process reduces gas costs and enhances security, as the transactions are only broadcasted to the blockchain once all required signatures are collected.

  5. Linking Tokens to Pools: You will use the Safe account to call the TokenAdminRegistry contract's setPool function to associate each token with its respective token pool.

  6. Configuring Token Pools: You will configure each token pool by setting cross-chain transfer parameters, such as token pool rate limits and enabled destination chains, using multisig transactions through the Safe account.

  7. Granting Mint and Burn Roles: You will grant the mint and burn roles to the token pools on each linked token using the Safe account. These roles are required for the token pools to mint and burn tokens during cross-chain transfers.

  8. Minting Tokens: You will mint tokens on Ethereum Sepolia. These tokens will later be used to test cross-chain transfers to BASE Sepolia.

  9. Transferring Tokens: Finally, you will transfer tokens from Ethereum Sepolia to BASE Sepolia using CCIP. You can pay CCIP fees using either LINK tokens or native gas tokens.

By the end of this tutorial, you will have successfully deployed, registered, configured, and enabled your tokens and token pools for use in CCIP. All are managed securely through a multi-signature Safe account.

Before You Begin

  1. Make sure you have Node.js v18 or above installed. If not, install Node.js v18:
    Download Node.js 18 if you don't have it installed. Optionally, you can use the nvm package to switch between Node.js versions:

    nvm use 18
    

    Verify that the correct version of Node.js is installed:

    node -v
    

    Example output:

    $ node -v
    v18.7.0
    
  2. Clone the repository and navigate to the project directory:

    git clone https://github.com/smartcontractkit/smart-contract-examples.git
    cd smart-contract-examples/ccip/cct/hardhat
    
  3. Install dependencies for the project:

    npm install
    
  4. Compile the project:

    npm run compile
    
  5. Encrypt your environment variables for higher security:
    The project uses @chainlink/env-enc to encrypt your environment variables at rest. Follow the steps below to configure your environment securely:

    1. Set an encryption password for your environment variables:

      npx env-enc set-pw
      
    2. Set up a .env.enc file with the necessary variables for Ethereum Sepolia and BASE Sepolia testnets. Use the following command to add the variables:

      npx env-enc set
      

      Variables to configure:

      • ETHEREUM_SEPOLIA_RPC_URL: A URL for the Ethereum Sepolia testnet. You can get a personal endpoint from services like Alchemy or Infura.
      • BASE_SEPOLIA_RPC_URL: A URL for the BASE Sepolia testnet. You can sign up for a personal endpoint from Alchemy or Infura.
      • PRIVATE_KEY: The private key for the first signer of the Safe multisig account. If you use MetaMask, you can follow this guide to export your private key. Note: This key is used to create and sign the transaction of the first signer.
      • PRIVATE_KEY_2: The private key for the second signer of the Safe multisig account. If you use MetaMask, you can follow this guide to export your private key. Note: This key is used to create and sign the transaction of the second signer.
      • ETHERSCAN_API_KEY: An API key from Etherscan to verify your contracts. You can obtain one from Etherscan.
      • BASESCAN_API_KEY: An API key from Basescan to verify your contracts on BASE. See this guide to get one from Basescan.
  6. Fund the EOA linked to the first private key with LINK and native gas tokens:
    Make sure your EOA has enough LINK and native gas tokens on Ethereum Sepolia and BASE Sepolia to cover transaction fees. You can use the Chainlink faucets to get testnet tokens. Important clarifications:

    • Off-chain signatures are collected for this tutorial. The first EOA is responsible for sending the transactions to the Safe smart account, meaning only the first EOA requires enough native gas tokens for these transactions.
    • When transferring the deployed tokens from Ethereum Sepolia to BASE Sepolia, the first EOA will be used to pay the CCIP fees in LINK. Therefore, it is crucial that the first EOA has sufficient LINK tokens to cover these fees. If a different EOA were to initiate the CCIP transfer, that EOA would need to hold enough LINK tokens.

Tutorial

Deploy Safe Smart Accounts

In this step, you will deploy a Safe smart account on both Ethereum Sepolia and BASE Sepolia using the deploySafe task. The Safe smart account will serve as the multi-signature account, requiring approvals from multiple owners to authorize transactions. You can customize the number of owners and the required threshold for signatures.

Below is an explanation of the parameters used during deployment:

ParameterDescriptionDefaultRequired
ownersA comma-separated list of owner addresses. These are the Ethereum addresses that will control the Safe smart account and authorize transactions.N/AYes
thresholdThe number of required signatures to authorize a transaction. This must be at least 1 and cannot exceed the number of owners provided.1Yes
networkThe blockchain on which the Safe smart account will be deployed. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.N/AYes
  1. Deploy a Safe on Ethereum Sepolia (Replace 0xOwnerAddress1, 0xOwnerAddress2, and 0xOwnerAddress3 with your Ethereum addresses):

    npx hardhat deploySafe --owners "0xOwnerAddress1,0xOwnerAddress2,0xOwnerAddress3" --threshold 2 --network sepolia
    

    Example output:

    $ npx hardhat deploySafe --owners 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA,0xA028Cedc47485aB2F1230551E4f3a6871B764263,0x27d7A69C878F9c8f51f4e53703abCE9bAcd2D9bf --threshold 2 --network sepolia
    
    2025-05-15T15:32:48.235Z info: Initializing Safe Protocol Kit...
    2025-05-15T15:32:50.056Z info: Deploying Safe with the following configuration:
    2025-05-15T15:32:50.056Z info: Owners: 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA, 0xA028Cedc47485aB2F1230551E4f3a6871B764263, 0x27d7A69C878F9c8f51f4e53703abCE9bAcd2D9bf
    2025-05-15T15:32:50.056Z info: Threshold: 2
    2025-05-15T15:32:50.056Z info: Salt nonce: 38379367611322498636143726637956939262525535210366890252487188019703321761416
    2025-05-15T15:33:03.680Z info: Safe deployed successfully at address: 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    
  2. Deploy a Safe on BASE Sepolia (Replace 0xOwnerAddress1, 0xOwnerAddress2, and 0xOwnerAddress3 with your Ethereum addresses):

    npx hardhat deploySafe --owners "0xOwnerAddress1,0xOwnerAddress2,0xOwnerAddress3" --threshold 2 --network baseSepolia
    

    Example output:

    $ npx hardhat deploySafe --owners 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA,0xA028Cedc47485aB2F1230551E4f3a6871B764263,0x27d7A69C878F9c8f51f4e53703abCE9bAcd2D9bf --threshold 2 --network baseSepolia
    
    2025-05-15T15:38:47.857Z info: Initializing Safe Protocol Kit...
    2025-05-15T15:38:49.395Z info: Deploying Safe with the following configuration:
    2025-05-15T15:38:49.395Z info: Owners: 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA, 0xA028Cedc47485aB2F1230551E4f3a6871B764263, 0x27d7A69C878F9c8f51f4e53703abCE9bAcd2D9bf
    2025-05-15T15:38:49.395Z info: Threshold: 2
    2025-05-15T15:38:49.395Z info: Salt nonce: 96228125351478622478825173646808876872832528152252275965961638646626126249894
    2025-05-15T15:38:58.503Z info: Safe deployed successfully at address: 0xcba922b26e7382955576014b7F1313ED6eb28c05
    

Deploy Tokens

In this step, you will deploy a token on both Ethereum Sepolia and BASE Sepolia using the deployTokenWithSafe task, then transfer ownership of the token to the Safe multisig account. This ensures that the Safe smart account controls the token, requiring multiple signatures to authorize any future administrative actions.

Below is an explanation of the parameters used during deployment:

ParameterDescriptionDefaultRequired
safeaddressThe address of the Safe smart account that will own the deployed token.N/AYes
nameThe full name of the token.N/AYes
symbolThe shorthand symbol representing the token.N/AYes
decimalsThe number of decimal places the token supports (e.g., 18 means 1 token is represented as 1e18 smallest units).18No
maxsupplyThe maximum supply of tokens. Set to 0 for unlimited supply.0No
premintThe amount of tokens to be minted to the owner at the time of deployment. If set to 0, no tokens will be minted to the owner during deployment.0No
verifycontractWhether to verify the contract on Etherscan or a similar blockchain explorer.falseNo
networkThe blockchain on which the token will be deployed. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.N/AYes
  1. Deploy a token on Ethereum Sepolia (Replace 0xSafeAddress with the address of the Safe smart account. You can also adapt the token name and symbol as needed):

    npx hardhat deployTokenWithSafe \
      --name "BnM aem" \
      --symbol BnMaem \
      --decimals 18 \
      --maxsupply 0 \
      --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 \
      --verifycontract true \
      --network sepolia
    

    Example output:

    2025-05-15T15:47:29.124Z info: Deploying BurnMintERC20 contract to sepolia
    2025-05-15T15:47:29.125Z info: Waiting 2 blocks for transaction 0xbf6814a50f884b3397b0ff5355339e6c0822c07483d70208f692f3bee2cb9170 to be confirmed...
    2025-05-15T15:47:55.976Z info: Token deployed to: 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21
    2025-05-15T15:47:55.977Z info: Verifying contract on Etherscan...
    The contract 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 has already been verified on the block explorer. If you're trying to verify a partially verified contract, please use the --force flag.
    https://sepolia.etherscan.io/address/0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21#code
    
    2025-05-15T15:47:57.606Z info: Token contract deployed and verified
    2025-05-15T15:47:57.607Z info: Transferring ownership of token to Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T15:48:25.529Z info: Ownership of token transferred to Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T15:48:25.529Z info: Setting Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 as the CCIP admin
    2025-05-15T15:48:48.744Z info: Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 has been set as the CCIP admin
    
  2. Deploy a token on BASE Sepolia (Replace 0xSafeAddress with the address of the Safe smart account. You can also adapt the token name and symbol as needed):

    npx hardhat deployTokenWithSafe \
      --name "BnM aem" \
      --symbol BnMaem \
      --decimals 18 \
      --maxsupply 0 \
      --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 \
      --verifycontract true \
      --network baseSepolia
    

    Example output:

    2025-05-15T16:02:17.414Z info: Deploying BurnMintERC20 contract to baseSepolia
    2025-05-15T16:02:17.415Z info: Waiting 2 blocks for transaction 0xf1d1846148b3c0553f9c804d094ee2f815c4260563159bb6043c9e48c414decd to be confirmed...
    2025-05-15T16:02:18.967Z info: Token deployed to: 0xa284Ec8049A6360B9A5990f12d472fd385680f50
    2025-05-15T16:02:18.968Z info: Verifying contract on Etherscan...
    Successfully submitted source code for contract
    @chainlink/contracts/src/v0.8/shared/token/ERC20/BurnMintERC20.sol:BurnMintERC20 at 0xa284Ec8049A6360B9A5990f12d472fd385680f50
    for verification on the block explorer. Waiting for verification result...
    
    Successfully verified contract BurnMintERC20 on the block explorer.
    https://sepolia.basescan.org/address/0xa284Ec8049A6360B9A5990f12d472fd385680f50#code
    
    2025-05-15T16:02:26.553Z info: Token contract deployed and verified
    2025-05-15T16:02:26.554Z info: Transferring ownership of token to Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T16:02:36.461Z info: Ownership of token transferred to Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T16:02:36.461Z info: Setting Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05 as the CCIP admin
    2025-05-15T16:02:45.474Z info: Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05 has been set as the CCIP admin
    

Deploy Token Pools

In this step, you will deploy a token pool on both Ethereum Sepolia and BASE Sepolia using the deployTokenPoolWithSafe task, then transfer ownership of the token pool to the Safe smart account. This ensures that the Safe smart account controls the token pool, providing a secure, multisig setup for managing the token pool operations.

Below is an explanation of the parameters used during deployment:

ParameterDescriptionDefaultRequired
tokenaddressThe address of the token that the pool will manage.N/AYes
safeaddressThe address of the Safe smart account that will own the token pool.N/AYes
localtokendecimalsThe number of decimals for the token on this chain.18No
verifycontractWhether to verify the contract on Etherscan or a similar blockchain explorer.falseNo
networkThe blockchain on which the token pool will be deployed. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.N/AYes
  1. Deploy a Burn and Mint token pool on Ethereum Sepolia (Replace 0xTokenAddress and 0xSafeAddress with the token address and Safe smart account address, respectively):

    npx hardhat deployTokenPoolWithSafe \
      --tokenaddress 0xTokenAddress \
      --safeaddress 0xSafeAddress \
      --localtokendecimals 18 \
      --verifycontract true \
      --network sepolia
    

    Example output:

    $ npx hardhat deployTokenPoolWithSafe \
       --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 \
       --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 \
       --localtokendecimals 18 \
       --verifycontract true \
       --network sepolia
    
    2025-05-15T16:37:55.334Z info: Waiting 2 blocks for transaction 0xdac1450c299a28e4ef285a5d13a4f5e060f8f59d0954cc7aacfd8980f8273880 to be confirmed...
    2025-05-15T16:38:12.821Z info: Token pool deployed to: 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084
    2025-05-15T16:38:12.821Z info: Verifying contract on Etherscan...
    2025-05-15T16:38:15.864Z error: Failed to send contract verification request.
    2025-05-15T15:47:55.977Z info: Verifying contract on Etherscan...
    The contract 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 has already been verified on the block explorer. If you're trying to verify a partially verified contract, please use the --force flag.
    https://sepolia.etherscan.io/address/0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084#code
    
    2025-05-15T16:38:15.865Z info: Transferring ownership of Token Pool to Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T16:38:40.846Z info: Ownership of Token Pool transferred to Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    
  2. Deploy a Burn and Mint token pool on BASE Sepolia (Replace 0xTokenAddress and 0xSafeAddress with the token address and Safe smart account address, respectively):

    npx hardhat deployTokenPoolWithSafe \
      --tokenaddress 0xTokenAddress \
      --safeaddress 0xSafeAddress \
      --localtokendecimals 18 \
      --verifycontract true \
      --network baseSepolia
    

    Example output:

    $ npx hardhat deployTokenPoolWithSafe \
       --tokenaddress 0xa284Ec8049A6360B9A5990f12d472fd385680f50 \
       --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 \
       --localtokendecimals 18 \
       --verifycontract true \
       --network baseSepolia
    
    2025-05-15T16:44:20.542Z info: Waiting 2 blocks for transaction 0x5947e2b45c36ec7c963f047591d32d9c9465a09ff5d36f3337740a0af0e11b33 to be confirmed...
    2025-05-15T16:44:21.290Z info: Token pool deployed to: 0xa5FAe80F8986d7000610E1ea857ce2981C44140a
    2025-05-15T16:44:21.291Z info: Verifying contract on Etherscan...
    Successfully submitted source code for contract
    @chainlink/contracts-ccip/contracts/pools/BurnMintTokenPool.sol:BurnMintTokenPool at 0xa5FAe80F8986d7000610E1ea857ce2981C44140a
    for verification on the block explorer. Waiting for verification result...
    
    Successfully verified contract BurnMintTokenPool on the block explorer.
    https://sepolia.basescan.org/address/0xa5FAe80F8986d7000610E1ea857ce2981C44140a#code
    
    2025-05-15T16:44:38.767Z info: Token pool contract deployed and verified
    2025-05-15T16:44:38.768Z info: Transferring ownership of Token Pool to Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T16:44:48.457Z info: Ownership of Token Pool transferred to Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05
    

Accept Ownership of Token Pools

After deploying the token pools and transferring ownership to the Safe smart account, the Safe smart account must formally accept ownership of the token pools. This ensures that all administrative actions for the token pools will require multisig approval, ensuring a secure and decentralized management process.

The process will use the Safe smart account to sign the transaction off-chain, collect the required signatures from multiple owners, and then execute it on-chain.

Below is an explanation of the parameters used during this task:

ParameterDescriptionRequired
contractaddressThe address of the contract whose ownership the Safe smart account is accepting.Yes
safeaddressThe address of the Safe smart account that will accept ownership of the contract.Yes
networkThe blockchain network where the transaction will be executed. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.Yes
  1. Accept ownership of the token pool on Ethereum Sepolia (Replace 0xContractAddress and 0xSafeAddress with the token pool contract address and Safe smart account address, respectively):

    npx hardhat acceptOwnershipFromSafe --contractaddress 0xContractAddress --safeaddress 0xSafeAddress --network sepolia
    

    Example output:

    $ npx hardhat acceptOwnershipFromSafe \
        --contractaddress 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 \
        --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 \
        --network sepolia
    
    2025-05-15T17:00:39.803Z info: Encoding acceptOwnership call for contract at 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084...
    2025-05-15T17:00:39.981Z info: Setting up Safe transaction...
    2025-05-15T17:00:44.376Z info: Safe transaction created
    2025-05-15T17:00:45.665Z info: Safe transaction signed by owner 1
    2025-05-15T17:00:47.630Z info: Safe transaction signed by owner 2
    2025-05-15T17:00:47.632Z info: Executing Safe transaction to accept ownership...
    2025-05-15T17:00:50.534Z info: Executed Safe transaction
    2025-05-15T17:00:50.535Z info: Waiting for 2 blocks for transaction 0x15dde64c9147342c9065304b8a21645727e4d25dc11f8ec7c3e4fc6cd880503f to be confirmed...
    2025-05-15T17:01:16.426Z info: Transaction confirmed after 2 blocks.
    
  2. Accept ownership of the token pool on BASE Sepolia (Replace 0xContractAddress and 0xSafeAddress with the token pool contract address and Safe smart account address, respectively):

    npx hardhat acceptOwnershipFromSafe \
      --contractaddress 0xa5FAe80F8986d7000610E1ea857ce2981C44140a \
      --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 \
      --network baseSepolia
    

    Example output:

    2025-05-15T17:02:53.631Z info: Encoding acceptOwnership call for contract at 0xa5FAe80F8986d7000610E1ea857ce2981C44140a...
    2025-05-15T17:02:53.790Z info: Setting up Safe transaction...
    2025-05-15T17:02:59.263Z info: Safe transaction created
    2025-05-15T17:03:01.147Z info: Safe transaction signed by owner 1
    2025-05-15T17:03:02.416Z info: Safe transaction signed by owner 2
    2025-05-15T17:03:02.417Z info: Executing Safe transaction to accept ownership...
    2025-05-15T17:03:06.223Z info: Executed Safe transaction
    2025-05-15T17:03:06.223Z info: Waiting for 2 blocks for transaction 0xe600bdd13d77dc266b6adf34f7c6d1baf491e20ceae9e93140700c22e481abaa to be confirmed...
    2025-05-15T17:03:11.594Z info: Transaction confirmed after 2 blocks.
    

Claim and Accept Token Admin Role using Safe

In this step, you will use the claimAndAcceptAdminRoleFromSafe task to claim and accept the admin role for the deployed tokens in a single Ethereum transaction. By leveraging Safe's batching feature, we can efficiently combine the two operations—claiming the admin role and accepting the admin role—into one on-chain interaction. This reduces gas costs and improves efficiency.

The process will use the Safe smart account to sign the transaction off-chain, collect the required signatures from multiple owners, and then execute it on-chain.

Below is an explanation of the parameters used during this task:

ParameterDescriptionDefaultRequired
tokenaddressThe address of the token for which the admin role will be claimed and accepted.N/AYes
safeaddressThe address of the Safe smart account that will execute the transactions and become the token admin.N/AYes
networkThe blockchain on which the transaction will be executed. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.N/AYes
  1. Claim and accept the admin role for the token on Ethereum Sepolia (Replace 0xTokenAddress and 0xSafeAddress with the token address and Safe smart account address, respectively):

    npx hardhat claimAndAcceptAdminRoleFromSafe --tokenaddress 0xTokenAddress --safeaddress 0xSafeAddress --network sepolia
    

    Example output:

    $ npx hardhat claimAndAcceptAdminRoleFromSafe --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 --network sepolia
    
    2025-05-15T17:58:57.327Z info: Current token CCIP admin: 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T17:58:57.330Z info: Claiming admin of 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 via getCCIPAdmin() for Safe at 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T17:58:57.335Z info: Adding second MetaTransaction to accept admin role for token 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21
    2025-05-15T17:59:01.740Z info: Safe transaction with two meta transactions created
    2025-05-15T17:59:02.996Z info: Safe transaction signed by owner 1
    2025-05-15T17:59:04.586Z info: Safe transaction signed by owner 2
    2025-05-15T17:59:04.586Z info: Executing Safe transaction to claim and accept admin role...
    2025-05-15T17:59:07.040Z info: Executed Safe transaction
    2025-05-15T17:59:07.040Z info: Waiting for 2 blocks for transaction 0x28f3138030f65acf35948a597ded0393441bf802e07bccd9b5e8c74cba4f0486 to be confirmed...
    2025-05-15T17:59:25.649Z info: Transaction confirmed after 2 blocks.
    2025-05-15T17:59:25.650Z info: Claiming admin of token 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 via Safe and accepted admin role successfully.
    
  2. Claim and accept the admin role for the token on BASE Sepolia (Replace 0xTokenAddress and 0xSafeAddress with the token address and Safe smart account address, respectively):

    npx hardhat claimAndAcceptAdminRoleFromSafe --tokenaddress 0xTokenAddress --safeaddress 0xSafeAddress --network baseSepolia
    

    Example output:

    $ npx hardhat claimAndAcceptAdminRoleFromSafe --tokenaddress 0xa284Ec8049A6360B9A5990f12d472fd385680f50 --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 --network baseSepolia
    
    2025-05-15T18:00:42.653Z info: Current token CCIP admin: 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T18:00:42.656Z info: Claiming admin of 0xa284Ec8049A6360B9A5990f12d472fd385680f50 via getCCIPAdmin() for Safe at 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T18:00:42.659Z info: Adding second MetaTransaction to accept admin role for token 0xa284Ec8049A6360B9A5990f12d472fd385680f50
    2025-05-15T18:00:46.780Z info: Safe transaction with two meta transactions created
    2025-05-15T18:00:48.415Z info: Safe transaction signed by owner 1
    2025-05-15T18:00:49.675Z info: Safe transaction signed by owner 2
    2025-05-15T18:00:49.675Z info: Executing Safe transaction to claim and accept admin role...
    2025-05-15T18:00:52.315Z info: Executed Safe transaction
    2025-05-15T18:00:52.315Z info: Waiting for 2 blocks for transaction 0x2fa1994a0bf73cd2a642933562bbee8b24bbf94dc7b5c648ad375e767f41b532 to be confirmed...
    2025-05-15T18:00:57.717Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:00:57.717Z info: Claiming admin of token 0xa284Ec8049A6360B9A5990f12d472fd385680f50 via Safe and accepted admin role successfully.
    

Grant Mint and Burn Roles using Safe

In this step, you will use the grantMintBurnRoleFromSafe task to grant mint and burn roles to both the token pool and the Safe smart account on Ethereum Sepolia and BASE Sepolia. The Safe smart account will handle the transaction to securely assign these roles, ensuring that multiple owners sign off on the operation. Granting mint and burn roles is essential to allow the token pool and the Safe account to manage token issuance and burning, and to prepare for future cross-chain transfers.

This process will grant:

  • Mint and burn roles to the token pool for handling cross-chain operations.
  • Mint and burn roles to the Safe smart account for minting tokens to EOAs for testing purposes.

Below is an explanation of the parameters used during this task:

ParameterDescriptionRequired
tokenaddressThe address of the deployed token contract for which mint and burn roles will be granted.Yes
burnermintersA comma-separated list of addresses (token pools and Safe smart account) to which mint and burn roles will be granted.Yes
safeaddressThe address of the Safe smart account that will execute the transaction to grant mint and burn roles.Yes
networkThe blockchain on which the mint and burn roles will be granted. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.Yes
  1. Grant mint and burn roles on Ethereum Sepolia (Replace 0xTokenAddress, 0xPoolAddress, and 0xSafeAddress with the token address, token pool address, and Safe smart account address, respectively):

    npx hardhat grantMintBurnRoleFromSafe \
      --tokenaddress 0xTokenAddress \
      --burnerminters 0xPoolAddress,0xSafeAddress \
      --safeaddress 0xSafeAddress \
      --network sepolia
    

    Example output:

    $ npx hardhat grantMintBurnRoleFromSafe --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --burnerminters 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084,0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 --network sepolia
    
    2025-05-15T18:09:40.787Z info: Connecting to token contract at 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21...
    2025-05-15T18:09:44.862Z info: Setting up Safe transactions to grant mint and burn roles to: 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084, 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T18:09:45.423Z info: Safe transaction created
    2025-05-15T18:09:46.690Z info: Safe transaction signed by owner 1
    2025-05-15T18:09:48.556Z info: Safe transaction signed by owner 2
    2025-05-15T18:09:48.557Z info: Executing Safe transaction to grant mint and burn roles...
    2025-05-15T18:09:51.051Z info: Executed Safe transaction
    2025-05-15T18:09:51.051Z info: Waiting for 2 blocks for transaction 0xddac86c3a9d7a954a21bad60e1687363d553471615ff04d05736de05bcababca to be confirmed...
    2025-05-15T18:10:18.680Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:10:18.681Z info: Mint and burn roles granted to 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084, 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    
  2. Grant mint and burn roles on BASE Sepolia (Replace 0xTokenAddress, 0xPoolAddress, and 0xSafeAddress with the token address, token pool address, and Safe smart account address, respectively):

    npx hardhat grantMintBurnRoleFromSafe \
      --tokenaddress 0xTokenAddress \
      --burnerminters 0xPoolAddress,0xSafeAddress \
      --safeaddress 0xSafeAddress \
      --network baseSepolia
    

    Example output:

    $ npx hardhat grantMintBurnRoleFromSafe --tokenaddress 0xa284Ec8049A6360B9A5990f12d472fd385680f50 --burnerminters 0xa5FAe80F8986d7000610E1ea857ce2981C44140a,0xcba922b26e7382955576014b7F1313ED6eb28c05 --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 --network baseSepolia
    
    2025-05-15T18:11:36.277Z info: Connecting to token contract at 0xa284Ec8049A6360B9A5990f12d472fd385680f50...
    2025-05-15T18:11:40.533Z info: Setting up Safe transactions to grant mint and burn roles to: 0xa5FAe80F8986d7000610E1ea857ce2981C44140a, 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T18:11:41.099Z info: Safe transaction created
    2025-05-15T18:11:42.751Z info: Safe transaction signed by owner 1
    2025-05-15T18:11:44.363Z info: Safe transaction signed by owner 2
    2025-05-15T18:11:44.364Z info: Executing Safe transaction to grant mint and burn roles...
    2025-05-15T18:11:48.063Z info: Executed Safe transaction
    2025-05-15T18:11:48.063Z info: Waiting for 2 blocks for transaction 0xbfe5e20537e790f23de7b7520f92cee781057fa300b718615cd0f9ee74f4db9d to be confirmed...
    2025-05-15T18:11:53.175Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:11:53.176Z info: Mint and burn roles granted to 0xa5FAe80F8986d7000610E1ea857ce2981C44140a, 0xcba922b26e7382955576014b7F1313ED6eb28c05
    

Set Pool using Safe

In this step, you will use the setPoolFromSafe task to link a token to a token pool on both Ethereum Sepolia and BASE Sepolia. The Safe smart account will be used to execute the transaction, ensuring that the pool is set securely with multisig approval. Multiple owners will sign the transaction off-chain before it is executed on-chain.

Below is an explanation of the parameters used during this task:

ParameterDescriptionRequired
tokenaddressThe address of the token for which the pool will be set.Yes
pooladdressThe address of the token pool to be linked to the token.Yes
safeaddressThe address of the Safe smart account that will execute the transaction.Yes
networkThe blockchain on which the transaction will be executed. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.Yes
  1. Set the token pool on Ethereum Sepolia (Replace 0xTokenAddress, 0xPoolAddress, and 0xSafeAddress with the token address, token pool address, and Safe smart account address, respectively):

    npx hardhat setPoolFromSafe --tokenaddress 0xTokenAddress --pooladdress 0xPoolAddress --safeaddress 0xSafeAddress --network sepolia
    

    Example output:

    $ npx hardhat setPoolFromSafe --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --pooladdress 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 --network sepolia
    
    2025-05-15T18:16:06.087Z info: Connecting to TokenAdminRegistry contract at 0x95F29FEE11c5C55d26cCcf1DB6772DE953B37B82...
    2025-05-15T18:16:07.338Z info: Setting pool for token 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 to 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 by 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2
    2025-05-15T18:16:11.919Z info: Safe transaction created for setting the pool
    2025-05-15T18:16:13.816Z info: Safe transaction signed by owner 1
    2025-05-15T18:16:15.095Z info: Safe transaction signed by owner 2
    2025-05-15T18:16:15.096Z info: Executing Safe transaction to set pool for token 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21...
    2025-05-15T18:16:17.825Z info: Executed Safe transaction
    2025-05-15T18:16:17.825Z info: Waiting for 2 blocks for transaction 0x35ce15719711eeda8dcd76a3d3c95576052789d94e0e2897b5caf828b468f79c to be confirmed...
    2025-05-15T18:16:50.361Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:16:50.362Z info: Pool set for token 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 to 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 successfully.
    
  2. Set the token pool on BASE Sepolia (Replace 0xTokenAddress, 0xPoolAddress, and 0xSafeAddress with the token address, token pool address, and Safe smart account address, respectively):

    npx hardhat setPoolFromSafe --tokenaddress 0xTokenAddress --pooladdress 0xPoolAddress --safeaddress 0xSafeAddress --network baseSepolia
    

    Example output:

    $ npx hardhat setPoolFromSafe --tokenaddress 0xa284Ec8049A6360B9A5990f12d472fd385680f50 --pooladdress 0xa5FAe80F8986d7000610E1ea857ce2981C44140a --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 --network baseSepolia
    
    2025-05-15T18:17:42.868Z info: Connecting to TokenAdminRegistry contract at 0x736D0bBb318c1B27Ff686cd19804094E66250e17...
    2025-05-15T18:17:44.056Z info: Setting pool for token 0xa284Ec8049A6360B9A5990f12d472fd385680f50 to 0xa5FAe80F8986d7000610E1ea857ce2981C44140a by 0xcba922b26e7382955576014b7F1313ED6eb28c05
    2025-05-15T18:17:48.127Z info: Safe transaction created for setting the pool
    2025-05-15T18:17:49.778Z info: Safe transaction signed by owner 1
    2025-05-15T18:17:51.342Z info: Safe transaction signed by owner 2
    2025-05-15T18:17:51.343Z info: Executing Safe transaction to set pool for token 0xa284Ec8049A6360B9A5990f12d472fd385680f50...
    2025-05-15T18:17:53.847Z info: Executed Safe transaction
    2025-05-15T18:17:53.847Z info: Waiting for 2 blocks for transaction 0x6731ae6ebbd61680552a2bfd54394b48072efbfb1453cc43a935954fe18b5884 to be confirmed...
    2025-05-15T18:17:59.199Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:17:59.199Z info: Pool set for token 0xa284Ec8049A6360B9A5990f12d472fd385680f50 to 0xa5FAe80F8986d7000610E1ea857ce2981C44140a successfully.
    

Configure Token Pools using Safe

In this step, you will use the applyChainUpdatesFromSafe task to configure a token pool for cross-chain interactions. By leveraging the Safe smart account, you can securely update the configuration of the token pool to support remote chains, including setting rate limits and linking it to remote pools and tokens.

The task handles complex cross-chain setups, allowing you to define rate limits for both inbound and outbound token transfers. The transaction is signed by the Safe owners off-chain and then executed on-chain, ensuring secure multi-signature control over the pool configuration.

Below is an explanation of the parameters used during this task:

ParameterDescriptionDefaultRequired
pooladdressThe address of the token pool to be configured.N/AYes
remotechainThe identifier of the remote blockchain (e.g., sepolia for Ethereum Sepolia or baseSepolia for BASE Sepolia).N/AYes
remotepooladdressesComma-separated list of remote pool addresses.N/AYes
remotetokenaddressThe address of the token on the remote chain.N/AYes
outboundratelimitenabledWhether the outbound rate limit for the token pool is enabled.falseNo
outboundratelimitcapacityThe maximum number of tokens that can be transferred outbound in a single burst (bucket capacity for the outbound rate limiter).0No
outboundratelimitrateThe rate at which tokens are refilled in the outbound bucket (tokens per second).0No
inboundratelimitenabledWhether the inbound rate limit for the token pool is enabled.falseNo
inboundratelimitcapacityThe maximum number of tokens that can be transferred inbound in a single burst (bucket capacity for the inbound rate limiter).0No
inboundratelimitrateThe rate at which tokens are refilled in the inbound bucket (tokens per second).0No
safeaddressThe address of the Safe smart account that will execute the transaction to configure the pool.N/AYes
networkThe blockchain on which the pool is being configured. Examples include sepolia for Ethereum Sepolia and baseSepolia for BASE Sepolia.N/AYes
  1. Configure the token pool on Ethereum Sepolia (Replace 0xPoolAddress, 0xRemotePoolAddress, 0xRemoteTokenAddress, and 0xSafeAddress with the token pool address, remote token pool address, remote token address, and Safe smart account address, respectively):

    npx hardhat applyChainUpdatesFromSafe --pooladdress 0xPoolAddress --remotechain baseSepolia  --remotepooladdresses 0xRemotePoolAddress --remotetokenaddress 0xRemoteTokenAddress --safeaddress 0xSafeAddress --network sepolia
    

    Example output:

    $ npx hardhat applyChainUpdatesFromSafe --pooladdress 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 --remotechain baseSepolia --remotepooladdresses 0xa5FAe80F8986d7000610E1ea857ce2981C44140a --remotetokenaddress 0xa284Ec8049A6360B9A5990f12d472fd385680f50 --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 --network sepolia
    
    2025-05-15T18:26:24.859Z info: Configuring pool at address: 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 for remote chain baseSepolia
    2025-05-15T18:26:24.860Z info: Remote chain selector: 10344971235874465080
    2025-05-15T18:26:24.860Z info: Remote pool addresses: 0xa5FAe80F8986d7000610E1ea857ce2981C44140a
    2025-05-15T18:26:24.860Z info: Remote token address: 0xa284Ec8049A6360B9A5990f12d472fd385680f50
    2025-05-15T18:26:28.628Z info: Safe transaction created for configuring the pool
    2025-05-15T18:26:29.890Z info: Safe transaction signed by owner 1
    2025-05-15T18:26:31.439Z info: Safe transaction signed by owner 2
    2025-05-15T18:26:31.440Z info: Executing Safe transaction to configure pool 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084...
    2025-05-15T18:26:34.351Z info: Executed Safe transaction
    2025-05-15T18:26:34.352Z info: Waiting for 2 blocks for transaction 0xc0236bd7b1332731acc0c380918cb47b052e787f3cdf168f8b910ace8baa2ccc to be confirmed...
    2025-05-15T18:26:52.574Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:26:52.574Z info: Pool configured successfully for remote chain baseSepolia
    
  2. Configure the token pool on BASE Sepolia (Replace 0xPoolAddress, 0xRemotePoolAddress, 0xRemoteTokenAddress, and 0xSafeAddress with the token pool address, remote token pool address, remote token address, and Safe smart account address, respectively):

    npx hardhat applyChainUpdatesFromSafe --pooladdress 0xPoolAddress --remotechain sepolia  --remotepooladdresses 0xRemotePoolAddress --remotetokenaddress 0xRemoteTokenAddress --safeaddress 0xSafeAddress --network baseSepolia
    

    Example output:

    $ npx hardhat applyChainUpdatesFromSafe --pooladdress 0xa5FAe80F8986d7000610E1ea857ce2981C44140a --remotechain sepolia --remotepooladdresses 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084 --remotetokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --safeaddress 0xcba922b26e7382955576014b7F1313ED6eb28c05 --network baseSepolia
    
    2025-05-15T18:27:55.354Z info: Configuring pool at address: 0xa5FAe80F8986d7000610E1ea857ce2981C44140a for remote chain sepolia
    2025-05-15T18:27:55.355Z info: Remote chain selector: 16015286601757825753
    2025-05-15T18:27:55.355Z info: Remote pool addresses: 0xfc21fe4Afef7fe0041BddaA1d0A794bfc7409084
    2025-05-15T18:27:55.355Z info: Remote token address: 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21
    2025-05-15T18:27:59.636Z info: Safe transaction created for configuring the pool
    2025-05-15T18:28:01.411Z info: Safe transaction signed by owner 1
    2025-05-15T18:28:03.668Z info: Safe transaction signed by owner 2
    2025-05-15T18:28:03.668Z info: Executing Safe transaction to configure pool 0xa5FAe80F8986d7000610E1ea857ce2981C44140a...
    2025-05-15T18:28:07.675Z info: Executed Safe transaction
    2025-05-15T18:28:07.675Z info: Waiting for 2 blocks for transaction 0x0934a608774c5a34f89f286992fb5536b533b6541787196d05701db987200876 to be confirmed...
    2025-05-15T18:28:12.741Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:28:12.741Z info: Pool configured successfully for remote chain sepolia
    

Mint Tokens to an EOA using Safe

In this step, you will use the mintTokensFromSafe task to mint tokens to an EOA on Ethereum Sepolia. This process uses a Safe smart account to securely manage the minting process, ensuring that the transaction is signed by multiple owners off-chain before being executed on-chain. These tokens will be used for transfers through CCIP from Ethereum Sepolia to BASE Sepolia.

Below is an explanation of the parameters used during this task:

ParameterDescriptionRequired
tokenaddressThe address of the token contract from which the tokens will be minted.Yes
amountThe amount of tokens to mint for each recipient address.Yes
receiveraddressesA comma-separated list of recipient addresses (EOAs) that will receive the minted tokens.Yes
safeaddressThe address of the Safe smart account that will execute the transaction to mint tokens.Yes
networkThe blockchain on which the minting transaction will be executed. For example, sepolia for Ethereum Sepolia.Yes
  1. Mint tokens to an EOA on Ethereum Sepolia (Replace 0xTokenAddress, 0xSafeAddress, and 0xReceiverAddress with the token address, Safe smart account address, and recipient address, respectively):

    npx hardhat mintTokensFromSafe \
      --tokenaddress 0xTokenAddress \
      --receiveraddresses 0xReceiverAddress \
      --amount 100000000000000000000 \
      --safeaddress 0xSafeAddress \
      --network sepolia
    

    Example output:

    $ npx hardhat mintTokensFromSafe --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --receiveraddresses 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA --amount 100000000000000000000 --safeaddress 0xcF30Ec01eA1d0240c92Dd5F81f2432D62BA6d4c2 --network sepolia
    
    2025-05-15T18:34:28.118Z info: Connecting to token contract at 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21...
    2025-05-15T18:34:28.477Z info: Minting 100000000000000000000 of BnMaem tokens to multiple addresses: 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA
    2025-05-15T18:34:32.658Z info: Safe transaction created
    2025-05-15T18:34:33.914Z info: Safe transaction signed by owner 1
    2025-05-15T18:34:35.471Z info: Safe transaction signed by owner 2
    2025-05-15T18:34:35.472Z info: Executing Safe transaction to mint tokens to multiple addresses...
    2025-05-15T18:34:38.059Z info: Executed Safe transaction
    2025-05-15T18:34:38.059Z info: Waiting for 2 blocks for transaction 0x7574289482a21a61db609aed7976e62b7c8f4ae315248444795b47bf004fb1db to be confirmed...
    2025-05-15T18:35:02.406Z info: Transaction confirmed after 2 blocks.
    2025-05-15T18:35:02.915Z info: Minted 100000000000000000000 of BnMaem tokens to 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA
    2025-05-15T18:35:03.588Z info: Current balance of 0x8C244f0B2164E6A3BED74ab429B0ebd661Bb14CA is 200000000000000000000 BnMaem
    

Transfer Tokens

In this step, you will use the transferTokens task to transfer tokens from Ethereum Sepolia to BASE Sepolia using CCIP. You have two options for paying CCIP fees: using LINK tokens or native gas tokens.

You will interact with the IRouterClient contract, specifically calling the ccipSend() function to initiate the token transfer.

Below is an explanation of the parameters used during the token transfer process:

ParameterDescriptionDefaultRequired
tokenaddressThe address of the token being transferred.N/AYes
amountThe amount of tokens to transfer.N/AYes
destinationchainThe blockchain to which the tokens will be transferred. Examples include baseSepolia, and sepolia.N/AYes
receiveraddressThe address of the receiver on the destination blockchain.N/AYes
feeThe type of fee used for the transfer, either LINK or native.LINKNo
networkThe blockchain on which the token transfer will be initiated. Examples include baseSepolia, and sepolia.N/AYes

Call the CCIP Router to transfer tokens from Ethereum Sepolia to BASE Sepolia, paying the CCIP fees in LINK tokens. Replace the token address, amount, receiver address, and blockchain with the appropriate values:

npx hardhat transferTokens --tokenaddress 0xTokenAddress --amount 2000000000000000000 --destinationchain baseSepolia --receiveraddress 0xReceiverAddress --fee LINK --network sepolia

Example output:

$ npx hardhat transferTokens --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --amount 2000000000000000000 --destinationchain baseSepolia --receiveraddress 0xA028Cedc47485aB2F1230551E4f3a6871B764263 --fee LINK --network sepolia

2025-05-15T18:39:49.001Z info: Estimated fees: 42779379610026734
2025-05-15T18:39:49.004Z info: Approving 2000000000000000000 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 to 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59
2025-05-15T18:40:19.643Z info: Approving 42779379610026734 LINK to 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59
2025-05-15T18:40:36.645Z info: Transferring 2000000000000000000 of 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 to 0xA028Cedc47485aB2F1230551E4f3a6871B764263 on chain baseSepolia with 42779379610026734 of LINK as fees
2025-05-15T18:41:07.957Z info: Transaction hash: 0xa3dfe6cdd6793793ff8b954907eeba0e74c3083f0531f53172d4923ce90f1206
2025-05-15T18:41:07.976Z warn: Unable to parse the event logs corresponding to the transaction 0xa3dfe6cdd6793793ff8b954907eeba0e74c3083f0531f53172d4923ce90f1206
2025-05-15T18:41:07.977Z info: Check status of message on https://ccip.chain.link/tx/0xa3dfe6cdd6793793ff8b954907eeba0e74c3083f0531f53172d4923ce90f1206

You can check the status of the message on the Chainlink CCIP Explorer by visiting the provided URL. In this example, the message ID is 0x706c9057d25c69c9e1191a5a86b07a2156ef78bc7eaff6334c02dad5e905a9fb.

Pay fees in native gas tokens

Call the CCIP Router to transfer tokens from Ethereum Sepolia to BASE Sepolia, paying the CCIP fees in native gas tokens. Replace the token address, amount, receiver address, and blockchain with the appropriate values:

npx hardhat transferTokens --tokenaddress 0xTokenAddress --amount 2000000000000000000 --destinationchain baseSepolia --receiveraddress 0xReceiverAddress --fee native --network sepolia

Example output:

$ npx hardhat transferTokens --tokenaddress 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 --amount 2000000000000000000 --destinationchain baseSepolia --receiveraddress 0xA028Cedc47485aB2F1230551E4f3a6871B764263 --fee native --network sepolia

2025-05-15T18:43:46.493Z info: Estimated fees: 289689740371171
2025-05-15T18:43:46.495Z info: Approving 2000000000000000000 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 to 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59
2025-05-15T18:44:17.706Z info: Transferring 2000000000000000000 of 0x6BfD1AD6b17Fd27Fb1c7eAcc0cB4Aa023411fF21 to 0xA028Cedc47485aB2F1230551E4f3a6871B764263 on chain baseSepolia with 289689740371171 of native token as fees
2025-05-15T18:44:37.382Z info: Transaction hash: 0xbca99a8f7e052bb1486e6323be17cca7081bd16dc8dbcf168a2aa6c32a48f6a5
2025-05-15T18:44:37.396Z warn: Unable to parse the event logs corresponding to the transaction 0xbca99a8f7e052bb1486e6323be17cca7081bd16dc8dbcf168a2aa6c32a48f6a5
2025-05-15T18:44:37.397Z info: Check status of message on https://ccip.chain.link/tx/0xbca99a8f7e052bb1486e6323be17cca7081bd16dc8dbcf168a2aa6c32a48f6a5

You can check the status of the message on the Chainlink CCIP Explorer by visiting the provided URL. In this example, the message ID is 0xac612ebbbdd36bbfb7f577ddb0258d4642fa7e0ad1063b40143875fc59555396.

Get the latest Chainlink content straight to your inbox.