Using CCIP Local Simulator in Your Hardhat Project - Forked Environments
You can use Chainlink Local to run CCIP in forked environments within your Hardhat project. To get started quickly, you will use the CCIP Hardhat Starter Kit.
This project includes the Example1.spec.tst file located in the ./test/fork directory, demonstrating how to set up and run token transfer tests between two accounts using CCIP in forked environments.
Forked environments allow you to simulate real-world blockchain networks by forking the state of existing chains. In this example, we will fork Arbitrum Sepolia and Ethereum Sepolia.
Prerequisites
This guide assumes that you are familiar with the guide Using CCIP Local Simulator in Your Hardhat Project. If not, please get familiar with it and run all the prerequisites.
Set an environment variable file. For higher security, the examples repository imports @chainlink/env-enc. Use this tool to encrypt your environment variables at rest.
-
Set an encryption password for your environment variables.
npx env-enc set-pw -
Run
npx env-enc setto configure a.env.encfile with:ARBITRUM_SEPOLIA_RPC_URL: The Remote Procedure Call (RPC) URL for the Arbitrum Sepolia network. You can obtain one by creating an account on Alchemy or Infura and setting up an Arbitrum Sepolia project.ETHEREUM_SEPOLIA_RPC_URL: The RPC URL for the Ethereum Sepolia testnet. You can sign up for a personal endpoint from Alchemy, Infura, or another node provider service.
Test tokens transfers
You will run a test to transfer tokens between two accounts in a forked environment. The test file Example1.spec.ts is located in the ./test/fork directory.
This file contains one test case:
Transfer with LINK fees: This test case transfers tokens from the sender account to the receiver account, paying fees in LINK. At the end of the test, it verifies that the sender account was debited and the receiver account was credited.
In this test, we simulate a transfer of tokens from an Externally Owned Account (EOA) on a source blockchain (which is a fork of Arbitrum Sepolia) to an EOA on a destination blockchain (which is a fork of Ethereum Sepolia). Forked environments allow you to simulate real-world blockchain networks by forking the state of existing chains, providing a realistic testing scenario.
For a detailed explanation of the test file, refer to the Examine the code section.
In your terminal, run the following command to execute the test:
npx hardhat test test/fork/Example1.spec.ts
Example output:
$ npx hardhat test test/fork/Example1.spec.ts
Example 1 - Fork
✔ Should transfer CCIP test tokens from EOA to EOA (10889ms)
1 passing (11s)
Examine the code
To transfer tokens using CCIP in a forked environment, we need the following:
- Destination chain selector
- Source CCIP router
- LINK token for paying CCIP fees
- A test token contract (such as CCIP-BnM) on both source and destination chains
- A sender account (Alice)
- A receiver account (Bob)
The it("Should transfer CCIP test tokens from EOA to EOA") function sets up the necessary environment and runs the test. Here are the steps involved:
-
Initialize the sender and receiver accounts:
const [alice, bob] = await hre.ethers.getSigners() -
Set the source and destination chains:
const [source, destination] = ["ethereumSepolia", "arbitrumSepolia"] -
Retrieve the necessary addresses and configurations (such as the LINK token address, source router address, and so on).
-
Fork the source network:
await hre.network.provider.request({ method: "hardhat_reset", params: [ { forking: { jsonRpcUrl: getProviderRpcUrl(source), }, }, ], }) -
Connect to the source router and CCIP-BnM contracts.
-
Call
sourceCCIPBnM.dripto request CCIP-BnM tokens for Alice (sender). -
Approve the source router to spend tokens on behalf of Alice (sender).
-
Construct the
Client.EVM2AnyMessagestructure. This step is similar to the non fork example. -
Call the source router to estimate the fees.
-
Call the
requestLinkFromFaucetfunction to request LINK tokens for Alice (sender). -
Connect to the LINK contract and approve the LINK token for paying CCIP fees.
-
Estimate Alice (sender) balance before the transfer.
-
Call the source router to send the CCIP request.
-
Wait for the transaction to be included in a block.
-
Call the
getEvm2EvmMessagefunction to parse the transaction receipt and extract theCCIPSendRequestedevent and then decodes it to an object. -
Verify that Alice's balance has decreased by the amount sent.
-
Fork and switch to the destination network:
await hre.network.provider.request({ method: "hardhat_reset", params: [ { forking: { jsonRpcUrl: getProviderRpcUrl(destination), }, }, ], }) -
Call the
routeMessagefunction to route the message to the destination router. -
Connect to the CCIP-BnM contract using the CCIP-BnM destination address.
-
Verify that Bob's balance has increased by the amount sent.