This repository represents an example of using a Chainlink product or service. It is provided to help you understand how to interact with Chainlink’s systems so that you can integrate them into your own. This template is provided "AS IS" without warranties of any kind, has not been audited, and may be missing key checks or error handling to make the usage of the product more clear. Take everything in this repository as an example and not something to be copy pasted into a production ready service.
Demonstrating how to use Chainlink Cross-Chain Interoperobility Protocol (CCIP) leveraging the Cross Chain Token (CCT) Standard to send a token from Ethereum Sepolia to Base Sepolia. This includes the creation of a CCT on the source chain, and the redemption of that CCT on the destination chain and management of the CCT via the sdk and token manager.
This repository provides a step-by-step guide for implementing the Cross Chain Token (CCT) Standard using Chainlink's Cross-Chain Interoperability Protocol (CCIP). The implementation demonstrates token deployment and transfer between Ethereum Sepolia and Base Sepolia networks, including token creation, pool management, and cross-chain transfers.
-
Initial Setup
-
Testing
-
Token Deployment
-
Pool Management
-
Administrative Setup
-
Token Operations
-
Environment Setup
- Configuration of environment variables
- Wallet creation and management
- Network RPC endpoints setup
-
Smart Contracts
- Token contracts
- Pool contracts
- Management contracts
- Transfer contracts
-
Network Support
- Ethereum Sepolia testnet
- Base Sepolia testnet
-
Administrative Functions
- Role management
- Pool configuration
- Chain setup
- Token minting and transfer capabilities
yarn && make
Run the command below, then update the .env PRIVATE_KEY
and ETHERSCAN_API_KEY
variables.
if [ -f .env ]; then
echo "We will use the .env your have already created."
else
if [ -z "${DOTENV}" ]; then
echo "Creating and setting .env"
cp .env.example .env && source .env
echo "Set your PRIVATE_KEY and ETHERSCAN_API_KEY in .env"
fi
fi
To create a new wallet that is stored in a keystore, issue the following command, which will prompt you to secure the private key with a password.
# Grabs the PRIVATE_KEY from the .env file.
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
if [ -f keystore/secret ]; then
echo "Found keystore in workspace"
else
if [ -z "${DOTENV}" ]; then
echo "Creating and setting keystore"
mkdir keystore
cast wallet import --private-key $PRIVATE_KEY -k keystore secret
echo "keystore/secret created"
fi
fi
For ease use of the keystore we already configured a environment variable called KEYSTORE
pointing to the keystore
file in the working directory.
You can use the wallet stored in the keystore by adding the --keystore
flag instead of the --private-key
flag. Run the command below to confirm your wallet address is stored accurately.
KEYSTORE=$(grep KEYSTORE .env | cut -d '=' -f2)
cast wallet address --keystore $KEYSTORE
export WALLET_ADDRESS=$(cast wallet address --keystore $KEYSTORE)
Step 4 | Test Contracts
Before we proceed with deployment, it is best practice to run tests, which can be executed as follows:
forge test -vvv
Ran 1 test for test/BnM.t.sol:BurnMintPoolFork
[PASS] test_cctDeployment() (gas: 7404858)
Logs:
[1] mockERC20TokenEthSepolia deployed
[2] mockERC20TokenBaseSepolia deployed
[3] burnMintTokenPoolEthSepolia deployed
[4] burnMintTokenPoolBaseSepolia deployed
[5] mint and burn roles granted to burnMintTokenPoolEthSepolia
[6] mint and burn roles granted to burnMintTokenPoolBaseSepolia
[7] Claim Admin role on Ethereum Sepolia
[8] Claim Admin role on Base Sepolia
[9] Accept Admin role on Ethereum Sepolia
[10] Accept Admin role on Base Sepolia
[11] Link token to pool on Ethereum Sepolia
[12] Link token to pool on Base Sepolia
[13] Configured Token Pool on Ethereum Sepolia
[14] Configured Token Pool on Base Sepolia
[15] minted and sent tokens from Ethereum Sepolia to Base Sepolia
[16] received tokens in Base Sepolia
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.56s (7.21s CPU time)
Step 5 | Deploy Tokens
In order to interact with our contracts, we first need to deploy them, which is simplified in the script/deploy
smart contracts, so let's deploy each contract applying the deployment script for each of the following commands.
5 | Deploy Tokens | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
if ! [ -f .env ]; then
echo "Missing necessary environment variables for deployment"
echo "Please set the following environment variables:"
echo "ETH_SEPOLIA_RPC, KEYSTORE, and WALLET_ADDRESS"
else
if [ -z "${DOTENV}" ]; then
echo "Deploying Token on Sepolia"
rm -rf keystore
cast wallet import --private-key $PRIVATE_KEY -k keystore secret
forge script ./script/deploy/DeployTokens.s.sol:DeployToken -vvv --broadcast --rpc-url $ETH_SEPOLIA_RPC --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
echo "Token deployed on Sepolia"
fi
fi
5 | Deploy Tokens | Base Sepolia
BASE_SEPOLIA_RPC=$(grep BASE_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
if ! [ -f .env ]; then
echo "Missing necessary environment variables for deployment"
echo "Please set the following environment variables:"
echo "BASE_SEPOLIA_RPC, KEYSTORE, and WALLET_ADDRESS"
else
if [ -z "${DOTENV}" ]; then
echo "Deploying Token on Base Sepolia"
rm -rf keystore
cast wallet import --private-key $PRIVATE_KEY -k keystore secret
forge script ./script/deploy/DeployTokens.s.sol:DeployToken -vvv --broadcast --rpc-url $BASE_SEPOLIA_RPC --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
echo "Token deployed on Base Sepolia"
fi
fi
Deploy Pools | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/deploy/DeployPool.s.sol:DeployPool -vvv --broadcast --rpc-url $ETH_SEPOLIA_RPC --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Deploy Pools | Base Sepolia
BASE_SEPOLIA_RPC=$(grep BASE_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/deploy/DeployPool.s.sol:DeployPool -vvv --broadcast --rpc-url $BASE_SEPOLIA_RPC --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Step 7 | Claim Admin Role
7 | Claim Admin Role | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/admin/ClaimAdmin.s.sol:ClaimAdmin --rpc-url $ETH_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
7 | Claim Admin Role | Base Sepolia
BASE_SEPOLIA_RPC=$(grep BASE_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/admin/ClaimAdmin.s.sol:ClaimAdmin --rpc-url $BASE_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Step 8 | Accept Admin Role
8 | Accept Admin Role | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/admin/AcceptAdmin.s.sol:AcceptAdmin --rpc-url $ETH_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
8 | Accept Admin Role | Base Sepolia
BASE_SEPOLIA_RPC=$(grep BASE_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/admin/AcceptAdmin.s.sol:AcceptAdmin --rpc-url $BASE_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Step 9 | Set Pool
9 | Set Pool | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/manage/SetPool.s.sol:SetPool --rpc-url $ETH_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
9 | Set Pool | Base Sepolia
BASE_SEPOLIA_RPC=$(grep BASE_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/manage/SetPool.s.sol:SetPool --rpc-url $BASE_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Step 10 | Apply Chain
10 | Apply Chain | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/manage/ApplyChain.s.sol:ApplyChain --rpc-url $ETH_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
10 | Apply Chain | Base Sepolia
BASE_SEPOLIA_RPC=$(grep BASE_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/manage/ApplyChain.s.sol:ApplyChain --rpc-url $BASE_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Step 11 | Mint Tokens
11 | Mint Tokens | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/mint/MintTokens.s.sol:MintTokens --rpc-url $ETH_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY
Step 12 | Transfer Tokens
12 | Transfer Tokens | Ethereum Sepolia
ETH_SEPOLIA_RPC=$(grep ETH_SEPOLIA_RPC .env | cut -d '=' -f2)
WALLET_ADDRESS=$(grep WALLET_ADDRESS .env | cut -d '=' -f2)
PRIVATE_KEY=$(grep PRIVATE_KEY .env | cut -d '=' -f2)
forge script ./script/transfer/TransferTokens.s.sol:TransferTokens --rpc-url $ETH_SEPOLIA_RPC --broadcast --sender $WALLET_ADDRESS --private-key $PRIVATE_KEY