CCIP Modules 
Overview 
The CCIP Modules are designed to facilitate cross-chain asset transfers of LST tokens between EVM chains and synchronize exchange rates.
Compatibility 
- Supports all EVM chains supported by ChainLink
 - Enables automated cross-chain exchange rate synchronization
 - Supports token cross-chain transfers (requires collaboration with Chainlink for TokenPool deployment before CCIP 1.5 update)
 
Introduction 
The CCIP Modules comprise two main components:
- Exchange Rate Cross-Chain Module
 - Token Cross-Chain Module
 
These modules are independent and can be deployed and used separately.
Repository: Evm Module Contracts
Rate Cross-Chain 
Includes four main contracts:
RateSender: Deployed on the source chain, periodically checks for rate updates and sends rate messages. Supports Proxy deployment for upgradability.RateReceiver: Deployed on the destination chain, receives LST rates.CCIPRateProvider: Deployed on the destination chain, provides LST rates for third-party use.RegisterUpkeep: Integrates Automation service, enables automatic triggering upon deployment.
Token Cross-Chain 
Includes one main contract:
TokenTransferor: Deployed using Proxy pattern, supports upgrades and permission control.
Note: Token cross-chain functionality requires whitelisting with Chainlink
Installation 
Prerequisites 
- Node.js
 - Yarn
 
Steps 
Clone the repository:
bashgit clone https://github.com/stafiprotocol/evm-module-contracts.git cd evm-module-contractsgit clone https://github.com/stafiprotocol/evm-module-contracts.git cd evm-module-contractsInstall dependencies:
bashyarn installyarn installConfigure the network in
hardhat.config.js,Add or replace the original chain and target chain configuration for your intended deployment:javascriptnetworks: { hardhat: { allowUnlimitedContractSize: true, }, local: { allowUnlimitedContractSize: true, url: "http://127.0.0.1:8545/" }, fuji: { url: process.env. AVALANCHE_FUJI_RPC, accounts: [process.env. PRIVATEKEY] }, ethSepolia: { url: process.env. SEPOLIA_RPC, accounts: [process.env. PRIVATEKEY] }, }networks: { hardhat: { allowUnlimitedContractSize: true, }, local: { allowUnlimitedContractSize: true, url: "http://127.0.0.1:8545/" }, fuji: { url: process.env. AVALANCHE_FUJI_RPC, accounts: [process.env. PRIVATEKEY] }, ethSepolia: { url: process.env. SEPOLIA_RPC, accounts: [process.env. PRIVATEKEY] }, }Set up environment variables in
.envfile,Add or replace the original chain and target chain configuration for your intended deployment:bashAVALANCHE_FUJI_RPC=https://ava-testnet.public.blastapi.io/ext/bc/C/rpc SEPOLIA_RPC=https://rpc.ankr.com/eth_sepolia PRIVATEKEY= EHTERSCAN_API_KEY=AVALANCHE_FUJI_RPC=https://ava-testnet.public.blastapi.io/ext/bc/C/rpc SEPOLIA_RPC=https://rpc.ankr.com/eth_sepolia PRIVATEKEY= EHTERSCAN_API_KEY=
Deployment 
Rate Cross-Chain Deployment 
The Router and Link addresses for each chain, as well as the chain selector, are available from networks supported by Chainlink
Option 1: Deploy contracts individually 
Deploy
RateSenderon the source chain:bashROUTER_ADDRESS=0x.. LINK_ADDRESS=0x.. npx hardhat run ./scripts/RateMsg/deploy_sender.js --network source_chainROUTER_ADDRESS=0x.. LINK_ADDRESS=0x.. npx hardhat run ./scripts/RateMsg/deploy_sender.js --network source_chainDeploy
RateReceiveron the destination chain:bashROUTER_ADDRESS=0x.. ALLOWED_SENDER_ADDRESS=0x.. npx hardhat run ./scripts/RateMsg/deploy_receiver.js --network dest_chainROUTER_ADDRESS=0x.. ALLOWED_SENDER_ADDRESS=0x.. npx hardhat run ./scripts/RateMsg/deploy_receiver.js --network dest_chainDeploy
CCIPRateProvideron the destination chain:bashINITIAL_RATE=1000000000000000000 RECEIVER_ADDRESS=0x.. npx hardhat run ./scripts/RateMsg/deploy_rate_provider.js --network dest_chainINITIAL_RATE=1000000000000000000 RECEIVER_ADDRESS=0x.. npx hardhat run ./scripts/RateMsg/deploy_rate_provider.js --network dest_chain
Option 2: Deploy all contracts at once 
Copy and modify the configuration file:
bashcp ./scripts/RateMsg/config.example.json ./scripts/RateMsg/config.jsoncp ./scripts/RateMsg/config.example.json ./scripts/RateMsg/config.jsonModify
config.json:json{ "routerAddressSource": "0x..", // Source chain router address "routerAddressDestination": "0x..", // Destination chain router address "linkAddressSource": "0x..", // Source chain LINK address "linkAddressDestination": "0x..", // Destination chain LINK address "initialRate": "1000000000000000000", // Initial rate for the destination chain "tokens": [] // Use when testing, leave blank when deploying to a production environment }{ "routerAddressSource": "0x..", // Source chain router address "routerAddressDestination": "0x..", // Destination chain router address "linkAddressSource": "0x..", // Source chain LINK address "linkAddressDestination": "0x..", // Destination chain LINK address "initialRate": "1000000000000000000", // Initial rate for the destination chain "tokens": [] // Use when testing, leave blank when deploying to a production environment }Run the deployment script:
bashNETWORK_SOURCE=source_chain NETWORK_DESTINATION=dst_chain ./scripts/RateMsg/deploy_all.shNETWORK_SOURCE=source_chain NETWORK_DESTINATION=dst_chain ./scripts/RateMsg/deploy_all.sh
RegisterUpkeep Deployment 
REGISTRAR_ADDRESS from Automation Documents
LINK_ADDRESS=0x.. REGISTRAR_ADDRESS=0x..  npx hardhat run ./scripts/RateMsg/deploy_register_upkeep.js --network source_chainLINK_ADDRESS=0x.. REGISTRAR_ADDRESS=0x..  npx hardhat run ./scripts/RateMsg/deploy_register_upkeep.js --network source_chainToken Cross-Chain Deployment 
Deploy the TokenTransferor contract:
ROUTER_ADDRESS=0x.. npx hardhat run ./scripts/TokenTransfer/deploy.js --network source_chainROUTER_ADDRESS=0x.. npx hardhat run ./scripts/TokenTransfer/deploy.js --network source_chainConfiguration 
Configure
RateSendercontract:- Add token information using 
addTokenInfomethod:solidity/// @notice Adds a new token rate and its associated information for a specific chain /// @dev This function combines the functionality of adding a token rate and its chain-specific information /// @param tokenName The name of the token to add /// @param _rateSource The address of the contract providing the rate for this token /// @param _receiver The address of the receiver contract on the destination chain /// @param _dstRateProvider The address of the rate provider contract on the destination chain /// @param _selector The chain selector for the destination chain function addTokenInfo( string memory tokenName, address _rateSource, address _receiver, address _dstRateProvider, uint64 _selector ) external onlyRole(ADMIN_ROLE)/// @notice Adds a new token rate and its associated information for a specific chain /// @dev This function combines the functionality of adding a token rate and its chain-specific information /// @param tokenName The name of the token to add /// @param _rateSource The address of the contract providing the rate for this token /// @param _receiver The address of the receiver contract on the destination chain /// @param _dstRateProvider The address of the rate provider contract on the destination chain /// @param _selector The chain selector for the destination chain function addTokenInfo( string memory tokenName, address _rateSource, address _receiver, address _dstRateProvider, uint64 _selector ) external onlyRole(ADMIN_ROLE) - Add new destination chains using 
addTokenDstInfomethod:solidity/// @notice Adds destination information for a token /// @param tokenName Name of the token /// @param _receiver Receiver address on the destination chain /// @param _dstRateProvider Rate provider address on the destination chain /// @param _selector Chain selector for the destination chain function addTokenDstInfo( string memory tokenName, address _receiver, address _dstRateProvider, uint64 _selector ) external onlyRole(ADMIN_ROLE)/// @notice Adds destination information for a token /// @param tokenName Name of the token /// @param _receiver Receiver address on the destination chain /// @param _dstRateProvider Rate provider address on the destination chain /// @param _selector Chain selector for the destination chain function addTokenDstInfo( string memory tokenName, address _receiver, address _dstRateProvider, uint64 _selector ) external onlyRole(ADMIN_ROLE) 
- Add token information using 
 Fund
RateSendercontract with LINK tokens for cross-chain message feesRegister Upkeep contract with Chainlink Automation:
Option 1: Use Chainlink Automation web interface



Option 2: Deploy
RegisterUpkeepcontract and callregisterAndPredictIDmethod:- Authorize a certain amount of LINK tokens to the 
RegisterUpkeepcontract for paying fees to Chainlink when registering Upkeep. 
solidityfunction registerAndPredictID( Params calldata params ) external payable nonReentrant whenNotPaused returns (uint256) struct Params { string name; // upkeep name string email; // optional Chainlink Automation is not currently enabled address upkeepContract; // address of the RateSender contract uint32 gasLimit; // gas limit for the upkeep address adminAddress; // address of the admin uint96 amount; // amount of LINK tokens }function registerAndPredictID( Params calldata params ) external payable nonReentrant whenNotPaused returns (uint256) struct Params { string name; // upkeep name string email; // optional Chainlink Automation is not currently enabled address upkeepContract; // address of the RateSender contract uint32 gasLimit; // gas limit for the upkeep address adminAddress; // address of the admin uint96 amount; // amount of LINK tokens }After a successful call to registerAndPredictID, you can see something like this on the web interface

- Authorize a certain amount of LINK tokens to the 
 
Usage 
Token Cross-Chain Transfer 
- Authorize tokens for cross-chain transfer to the 
TokenTransferorcontract - Call 
updateChainAllowlistandupdateTokenAllowlistmethods on theTokenTransferorcontract:solidityfunction updateChainAllowlist(uint64 _destinationChainSelector, bool _allowed) external onlyRole(ADMIN_ROLE) function updateTokenAllowlist(address _token, bool _allowed) external onlyRole(ADMIN_ROLE)function updateChainAllowlist(uint64 _destinationChainSelector, bool _allowed) external onlyRole(ADMIN_ROLE) function updateTokenAllowlist(address _token, bool _allowed) external onlyRole(ADMIN_ROLE) - Call 
transferTokensmethod on theTokenTransferorcontract:solidity/// @notice Calculate the required fee for transferring tokens to another chain. /// @dev This function simulates the token transfer process to calculate the fee, /// without actually performing the transfer. It uses the same logic as the /// transferTokens function to ensure fee consistency. /// @param _destinationChainSelector The identifier (selector) for the destination blockchain. /// @param _receiver The address of the recipient on the destination blockchain. /// @param _token The address of the token to be transferred. /// @param _amount The amount of tokens to be transferred. /// @return fees The amount of native currency required as fee for the cross-chain transfer. function transferTokens( uint64 _destinationChainSelector, address _receiver, address _token, uint256 _amount ) external payable nonReentrant validateReceiver(_receiver) onlyAllowlistedToken(_token) onlyAllowlistedChain(_destinationChainSelector) returns (bytes32 messageId)/// @notice Calculate the required fee for transferring tokens to another chain. /// @dev This function simulates the token transfer process to calculate the fee, /// without actually performing the transfer. It uses the same logic as the /// transferTokens function to ensure fee consistency. /// @param _destinationChainSelector The identifier (selector) for the destination blockchain. /// @param _receiver The address of the recipient on the destination blockchain. /// @param _token The address of the token to be transferred. /// @param _amount The amount of tokens to be transferred. /// @return fees The amount of native currency required as fee for the cross-chain transfer. function transferTokens( uint64 _destinationChainSelector, address _receiver, address _token, uint256 _amount ) external payable nonReentrant validateReceiver(_receiver) onlyAllowlistedToken(_token) onlyAllowlistedChain(_destinationChainSelector) returns (bytes32 messageId) 
Notes 
- All CCIP cross-chain transactions can be viewed on CCIP Explorer using the source chain transaction hash.
 - Token cross-chain functionality requires whitelisting with Chainlink.
 - Router addresses and chain selectors can be found in the ChainLink supported networks documentation.