# Chainlink Local API Reference
Source: https://docs.chain.link/chainlink-local/api-reference
## Available Versions
### Latest Release
- **[Chainlink Local v0.2.3](/chainlink-local/api-reference/v0.2.3)** (Current Version)
- Added support for `EVMExtraArgsV2` and OZ's `AccessControl` in CCIPLocalSimulator
- Added `supportNewTokenViaAccessControlDefaultAdmin` function
- Bumped `@chainlink/contracts-ccip` to `1.5.1-beta.0`
- Enhanced token pool support and EVMExtraArgsV2 compatibility
### Previous Versions
- **[Chainlink Local v0.2.2](/chainlink-local/api-reference/v0.2.2)** (Legacy Version)
- Added support for CCIP v1.5
- Enhanced token support functions
- Updated network configurations
- Improved testing utilities
- **[Chainlink Local v0.2.1](/chainlink-local/api-reference/v0.2.1)** (Legacy Version)
- Added support for Chainlink Data Feeds
- Introduced mock aggregator contracts
- Basic testing framework setup
## Documentation Structure
Each version includes detailed documentation for:
- CCIP Components (Router, Simulator)
- Data Feed Contracts
- Token Implementations
- JavaScript Utilities
- Testing Helpers
---
# AggregatorInterface v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/aggregator-interface
## AggregatorInterface
[`AggregatorInterface`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/data-feeds/interfaces/AggregatorInterface.sol) defines the standard interface for Chainlink Data Feed aggregators. This interface provides methods to access price feed data and timestamps, as well as historical values.
## Events
### AnswerUpdated
```solidity
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
```
Emitted when a new answer is available for a price feed.
| Parameter | Type | Description |
| --------- | --------- | -------------------------------------- |
| current | `int256` | The new price value (indexed) |
| roundId | `uint256` | The round ID for this update (indexed) |
| updatedAt | `uint256` | Timestamp when this answer was updated |
### NewRound
```solidity
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt)
```
Emitted when a new round of price updates begins.
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------------ |
| roundId | `uint256` | The round ID that started (indexed) |
| startedBy | `address` | Address that initiated the round (indexed) |
| startedAt | `uint256` | Timestamp when the round started |
## Functions
### getAnswer
```solidity
function getAnswer(uint256 roundId) external view returns (int256)
```
Retrieves the answer for a specific round.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | --------------------- |
| roundId | `uint256` | The round ID to query |
**Returns:**
| Type | Description |
| -------- | ---------------------------------------- |
| `int256` | The price answer for the specified round |
### getTimestamp
```solidity
function getTimestamp(uint256 roundId) external view returns (uint256)
```
Retrieves the timestamp for a specific round.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | --------------------- |
| roundId | `uint256` | The round ID to query |
**Returns:**
| Type | Description |
| --------- | ---------------------------------------- |
| `uint256` | The timestamp when the round was updated |
### latestAnswer
```solidity
function latestAnswer() external view returns (int256)
```
Retrieves the most recent answer.
**Returns:**
| Type | Description |
| -------- | ---------------------------- |
| `int256` | The latest price feed answer |
### latestRound
```solidity
function latestRound() external view returns (uint256)
```
Retrieves the most recent round ID.
**Returns:**
| Type | Description |
| --------- | ------------------- |
| `uint256` | The latest round ID |
### latestTimestamp
```solidity
function latestTimestamp() external view returns (uint256)
```
Retrieves the timestamp of the latest answer.
**Returns:**
| Type | Description |
| --------- | ------------------------------------------------ |
| `uint256` | The timestamp when the latest answer was updated |
---
# AggregatorV2V3Interface v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/aggregator-v2-v3-interface
## AggregatorV2V3Interface
[`AggregatorV2V3Interface`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/data-feeds/interfaces/AggregatorV2V3Interface.sol) combines the functionality of both AggregatorInterface and AggregatorV3Interface, providing backward compatibility while supporting newer Data Feed features.
## Interface Inheritance
```solidity
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
```
This interface provides access to all functions and events from both parent interfaces.
---
# AggregatorV3Interface v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/aggregator-v3-interface
## AggregatorV3Interface
[`AggregatorV3Interface`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/data-feeds/interfaces/AggregatorV3Interface.sol) defines the extended interface for Chainlink Data Feed aggregators, providing comprehensive round data and metadata about the feed.
## Functions
### decimals
```solidity
function decimals() external view returns (uint8)
```
Retrieves the number of decimals used to format the answer.
**Returns:**
| Type | Description |
| ------- | -------------------------------------- |
| `uint8` | The number of decimals in the response |
### description
```solidity
function description() external view returns (string memory)
```
Retrieves the description of the price feed.
**Returns:**
| Type | Description |
| -------- | --------------------------------- |
| `string` | The description of the price feed |
### getRoundData
```solidity
function getRoundData(uint80 _roundId) external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
Retrieves the complete round data for a specific round ID.
**Parameters:**
| Parameter | Type | Description |
| --------- | -------- | --------------------- |
| _roundId | `uint80` | The round ID to query |
**Returns:**
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------- |
| roundId | `uint80` | The round ID from the aggregator for this round |
| answer | `int256` | The price answer for this round |
| startedAt | `uint256` | Timestamp when the round started |
| updatedAt | `uint256` | Timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### latestRoundData
```solidity
function latestRoundData() external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
Retrieves the latest round data from the aggregator.
**Returns:**
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------- |
| roundId | `uint80` | The round ID from the aggregator for this round |
| answer | `int256` | The latest price answer |
| startedAt | `uint256` | Timestamp when the round started |
| updatedAt | `uint256` | Timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### version
```solidity
function version() external view returns (uint256)
```
Retrieves the version number of the aggregator implementation.
**Returns:**
| Type | Description |
| --------- | ------------------------------------ |
| `uint256` | The version number of the aggregator |
---
# BurnMintERC677Helper v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/burn-mint-erc677-helper
## BurnMintERC677Helper
[`BurnMintERC677Helper`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/ccip/BurnMintERC677Helper.sol) is a helper contract that extends the BurnMintERC677 token implementation. It provides simplified functionality for minting tokens, primarily intended for testing and development environments.
## Functions
### constructor
Initializes a new BurnMintERC677Helper token with specified name and symbol.
| Parameter | Type | Description |
| --------- | -------- | ------------------------------ |
| name | `string` | The name of the token |
| symbol | `string` | The symbol/ticker of the token |
### drip
Mints exactly one full token (1e18 base units) to the specified address.
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------------------- |
| to | `address` | The recipient address to receive the minted token |
---
# CCIPLocalSimulatorFork v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/ccip-local-simulator-fork-js
## CCIPLocalSimulatorFork
[`CCIPLocalSimulatorFork`](https://github.com/smartcontractkit/chainlink-local/blob/main/scripts/CCIPLocalSimulatorFork.js) provides utilities for simulating Cross-Chain Interoperability Protocol (CCIP) operations in a forked environment. It enables testing of cross-chain messaging and token transfers within a Hardhat project.
## Types
### EVM2EVMMessage
An object representing a cross-chain message with the following structure:
| Property | Type | Description |
| ------------------- | ---------------------------------------- | -------------------------------------- |
| sourceChainSelector | `bigint` | The chain selector of the source chain |
| sender | `string` | The address of the message sender |
| receiver | `string` | The address of the message receiver |
| sequenceNumber | `bigint` | Message sequence number |
| gasLimit | `bigint` | Gas limit for message execution |
| strict | `boolean` | Whether strict mode is enabled |
| nonce | `bigint` | Message nonce |
| feeToken | `string` | Address of the token used for fees |
| feeTokenAmount | `bigint` | Amount of fee token |
| data | `string` | Message payload data |
| tokenAmounts | `Array<{token: string, amount: bigint}>` | Array of token transfers |
| sourceTokenData | `Array` | Source token metadata |
| messageId | `string` | Unique message identifier |
## Functions
### requestLinkFromTheFaucet
Requests LINK tokens from a faucet contract for testing purposes.
| Parameter | Type | Description |
| ----------- | -------- | ------------------------------------------------------- |
| linkAddress | `string` | The address of the LINK contract on the current network |
| to | `string` | The address to receive the LINK tokens |
| amount | `bigint` | The amount of LINK tokens to request |
**Returns**: `Promise` - The transaction hash of the fund transfer
### getEvm2EvmMessage
Parses a transaction receipt to extract CCIP message details from the `CCIPSendRequested` event.
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------ |
| receipt | `object` | The transaction receipt from the `ccipSend` call |
**Returns**: `object | null` - The parsed EVM2EVMMessage object or null if no valid message is found
### routeMessage
Routes a sent message from the source network to the destination network in the forked environment.
| Parameter | Type | Description |
| -------------- | -------- | ------------------------------------------ |
| routerAddress | `string` | Address of the destination Router contract |
| evm2EvmMessage | `object` | The cross-chain message to be routed |
**Returns**: `Promise` - Resolves when the message is successfully routed
---
# CCIPLocalSimulatorFork v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/ccip-local-simulator-fork
## CCIPLocalSimulatorFork
[`CCIPLocalSimulatorFork`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/ccip/CCIPLocalSimulatorFork.sol) enables testing of CCIP cross-chain messaging in forked network environments. This contract provides essential utilities for configuring networks and simulating message routing between chains in a Foundry test environment.
## Events
### CCIPSendRequested
```solidity
event CCIPSendRequested(Internal.EVM2EVMMessage message)
```
Emitted when a cross-chain message is requested to be sent through CCIP.
## Variables
### i_register
```solidity
Register immutable i_register
```
Stores and manages network configuration details required for CCIP operations.
### LINK_FAUCET
```solidity
address constant LINK_FAUCET = 0x4281eCF07378Ee595C564a59048801330f3084eE
```
Provides test LINK tokens to addresses during testing.
### s_processedMessages
```solidity
mapping(bytes32 messageId => bool isProcessed) internal s_processedMessages
```
Prevents duplicate processing by tracking which messages have already been handled.
## Functions
### constructor
```solidity
constructor() public
```
Initializes the simulator environment by deploying and configuring a persistent Register contract.
### getNetworkDetails
```solidity
function getNetworkDetails(uint256 chainId) external view returns (Register.NetworkDetails memory)
```
Retrieves network-specific CCIP configuration details. Use this function to access default settings or custom configurations for a given network.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------------------------------------------------------------------------- |
| chainId | `uint256` | The blockchain network chain ID. For example 11155111 for Ethereum Sepolia. Not CCIP chain selector. |
**Returns:**
| Parameter | Type | Description |
| -------------- | ------------------------- | -------------------- |
| networkDetails | `Register.NetworkDetails` | A struct containing: |
• chainSelector - The unique CCIP Chain Selector
• routerAddress - The address of the CCIP Router contract
• linkAddress - The address of the LINK token
• wrappedNativeAddress - The address of the wrapped native token for CCIP fees
• ccipBnMAddress - The address of the CCIP BnM token
• ccipLnMAddress - The address of the CCIP LnM token |
### requestLinkFromFaucet
```solidity
function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
```
Transfers LINK tokens from the faucet to a specified address for testing purposes.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ----------------------------------------------- |
| to | `address` | The address to which LINK tokens are to be sent |
| amount | `uint256` | The amount of LINK tokens to send |
**Returns:**
| Parameter | Type | Description |
| --------- | ------ | -------------------------------------------------------------------------- |
| success | `bool` | Returns `true` if the transfer of tokens was successful, otherwise `false` |
### setNetworkDetails
```solidity
function setNetworkDetails(uint256 chainId, Register.NetworkDetails memory networkDetails) external
```
Updates or adds CCIP configuration details for a specific blockchain network.
**Parameters:**
| Parameter | Type | Description |
| -------------- | ------------------------- | ---------------------------------------------------------------------------------------------------- |
| chainId | `uint256` | The blockchain network chain ID. For example 11155111 for Ethereum Sepolia. Not CCIP chain selector. |
| networkDetails | `Register.NetworkDetails` | A struct containing: |
• chainSelector - The unique CCIP Chain Selector
• routerAddress - The address of the CCIP Router contract
• linkAddress - The address of the LINK token
• wrappedNativeAddress - The address of the wrapped native token for CCIP fees
• ccipBnMAddress - The address of the CCIP BnM token
• ccipLnMAddress - The address of the CCIP LnM token |
### switchChainAndRouteMessage
```solidity
function switchChainAndRouteMessage(uint256 forkId) external
```
Processes a cross-chain message by switching to the destination fork and executing the message.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------------------------------------------------------------------------------ |
| forkId | `uint256` | The ID of the destination network fork. This is the returned value of `createFork()` or `createSelectFork()` |
---
# CCIPLocalSimulator v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/ccip-local-simulator
## CCIPLocalSimulator
[`CCIPLocalSimulator`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/ccip/CCIPLocalSimulator.sol) is a contract that provides local simulation capabilities for Cross-Chain Interoperability Protocol (CCIP) operations. It manages token deployments and routing configurations for testing CCIP functionality.
## Variables
### CHAIN_SELECTOR
```solidity
uint64 constant CHAIN_SELECTOR = 16015286601757825753
```
The unique identifier for the simulated chain in CCIP operations.
### i_ccipBnM
```solidity
BurnMintERC677Helper internal immutable i_ccipBnM
```
The CCIP BnM token contract instance used for cross-chain token transfers.
### i_ccipLnM
```solidity
BurnMintERC677Helper internal immutable i_ccipLnM
```
The CCIP LnM token contract instance used for cross-chain token transfers.
### i_linkToken
```solidity
LinkToken internal immutable i_linkToken
```
The LINK token contract instance used for paying CCIP fees.
### i_mockRouter
```solidity
MockCCIPRouter internal immutable i_mockRouter
```
The mock CCIP router contract instance that simulates cross-chain message routing.
### i_wrappedNative
```solidity
WETH9 internal immutable i_wrappedNative
```
The wrapped native token contract instance used for fee payments.
## Functions
### constructor
Initializes the simulator by deploying necessary token contracts and configuring supported tokens.
### configuration
Returns the configuration details for pre-deployed contracts and services needed for local CCIP simulations.
**Returns:**
| Parameter | Type | Description |
| ------------------- | ---------------------- | ------------------------------------- |
| chainSelector_ | `uint64` | The unique CCIP Chain Selector |
| sourceRouter_ | `IRouterClient` | The source chain Router contract |
| destinationRouter_ | `IRouterClient` | The destination chain Router contract |
| wrappedNative_ | `WETH9` | The wrapped native token contract |
| linkToken_ | `LinkToken` | The LINK token contract |
| ccipBnM_ | `BurnMintERC677Helper` | The CCIP-BnM token contract |
| ccipLnM_ | `BurnMintERC677Helper` | The CCIP-LnM token contract |
### getSupportedTokens
Gets a list of token addresses that are supported for cross-chain transfers by the simulator.
**Parameters:**
| Parameter | Type | Description |
| ------------- | -------- | ------------------------------ |
| chainSelector | `uint64` | The unique CCIP Chain Selector |
**Returns:**
| Parameter | Type | Description |
| --------- | ------------------ | --------------------------------- |
| tokens | `address[] memory` | List of supported token addresses |
### isChainSupported
Checks whether the provided chain selector is supported by the simulator.
**Parameters:**
| Parameter | Type | Description |
| ------------- | -------- | ------------------------------ |
| chainSelector | `uint64` | The unique CCIP Chain Selector |
**Returns:**
| Parameter | Type | Description |
| --------- | ------ | --------------------------------------- |
| supported | `bool` | True if the chain selector is supported |
### requestLinkFromFaucet
Requests LINK tokens from the faucet for a specified address.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------- |
| to | `address` | The address to receive LINK tokens |
| amount | `uint256` | The amount of LINK tokens to send |
**Returns:**
| Parameter | Type | Description |
| --------- | ------ | ----------------------------------- |
| success | `bool` | True if the transfer was successful |
### supportNewToken
Allows users to add support for new tokens besides CCIP-BnM and CCIP-LnM for cross-chain transfers.
**Parameters:**
| Parameter | Type | Description |
| ------------ | --------- | --------------------------------------------------- |
| tokenAddress | `address` | The address of the token to add to supported tokens |
---
# Chainlink Local v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1
## CCIP
These contracts provide Cross-Chain Interoperability Protocol (CCIP) functionality in a local testing environment:
- [CCIPLocalSimulator](/chainlink-local/api-reference/v0.2.1/ccip-local-simulator) - Local CCIP message routing simulator
- [CCIPLocalSimulatorFork](/chainlink-local/api-reference/v0.2.1/ccip-local-simulator-fork) - CCIP simulator for forked networks
- [CCIPLocalSimulatorForkJS](/chainlink-local/api-reference/v0.2.1/ccip-local-simulator-fork-js) - JavaScript utilities for CCIP simulation
- [MockEvm2EvmOffRamp](/chainlink-local/api-reference/v0.2.1/mock-evm2evm-offramp) - Mock CCIP off-ramp contract
- [Register](/chainlink-local/api-reference/v0.2.1/register) - CCIP network configuration registry
## Data Feeds
Contracts for simulating Chainlink Data Feeds:
- [AggregatorInterface](/chainlink-local/api-reference/v0.2.1/aggregator-interface) - Basic price feed interface
- [AggregatorV2V3Interface](/chainlink-local/api-reference/v0.2.1/aggregator-v2-v3-interface) - Combined V2/V3 price feed interface
- [AggregatorV3Interface](/chainlink-local/api-reference/v0.2.1/aggregator-v3-interface) - Extended price feed interface
- [MockOffchainAggregator](/chainlink-local/api-reference/v0.2.1/mock-offchain-aggregator) - Mock implementation of off-chain aggregator
- [MockV3Aggregator](/chainlink-local/api-reference/v0.2.1/mock-v3-aggregator) - Mock implementation of V3 aggregator
## Token Contracts
Standard token implementations for testing:
- [BurnMintERC677Helper](/chainlink-local/api-reference/v0.2.1/burn-mint-erc677-helper) - Helper contract for ERC677 token operations
- [LinkToken](/chainlink-local/api-reference/v0.2.1/link-token) - LINK token implementation
- [WETH9](/chainlink-local/api-reference/v0.2.1/weth9) - Wrapped Ether implementation
---
# LinkToken v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/link-token
## LinkToken
[`LinkToken`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/shared/LinkToken.sol) implements the ERC677 token standard for LINK tokens. This contract provides the basic functionality of the Chainlink token with a fixed total supply.
## Variables
### NAME
```solidity
string private constant NAME = "ChainLink Token"
```
The name of the token.
### SYMBOL
```solidity
string private constant SYMBOL = "LINK"
```
The symbol of the token.
### TOTAL_SUPPLY
```solidity
uint private constant TOTAL_SUPPLY = 10 ** 27
```
The total supply of LINK tokens (1 billion LINK with 18 decimals).
## Functions
### constructor
```solidity
constructor() ERC677(NAME, SYMBOL)
```
Initializes the contract with the token name and symbol, then calls `_onCreate()`.
### _onCreate
```solidity
function _onCreate() internal virtual
```
Hook that is called when this contract is created.
---
# MockEvm2EvmOffRamp v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/mock-evm2evm-offramp
## MockEvm2EvmOffRamp
[`MockEvm2EvmOffRamp`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/ccip/MockEvm2EvmOffRamp.sol) simulates the destination chain component of CCIP's cross-chain message delivery system. This contract handles token releases and message execution in a test environment.
## Errors
### CanOnlySimulatorCall
```solidity
error CanOnlySimulatorCall()
```
Thrown when a non-simulator address attempts to call restricted functions.
### ReceiverError
```solidity
error ReceiverError(bytes error)
```
Thrown when the CCIP receiver execution fails.
### TokenHandlingError
```solidity
error TokenHandlingError(bytes error)
```
Thrown when token release or minting operations encounter non-rate-limiting errors.
### UnsupportedToken
```solidity
error UnsupportedToken(IERC20 token)
```
Thrown when attempting to interact with a token that doesn't have an associated pool.
## Structs
### DynamicConfig
```solidity
struct DynamicConfig {
uint32 permissionLessExecutionThresholdSeconds;
address router;
address priceRegistry;
uint16 maxNumberOfTokensPerMsg;
uint32 maxDataBytes;
uint32 maxPoolReleaseOrMintGas;
}
```
Configuration parameters that can be updated during contract operation:
• permissionLessExecutionThresholdSeconds - Waiting time before manual execution is enabled
• router - Router address
• priceRegistry - Price registry address
• maxNumberOfTokensPerMsg - Maximum number of ERC20 token transfers per message
• maxDataBytes - Maximum payload data size in bytes
• maxPoolReleaseOrMintGas - Maximum gas for token pool releaseOrMint calls
## Variables
### i_sourceChainSelector
```solidity
uint64 internal immutable i_sourceChainSelector
```
The chain selector identifying the source chain for this off-ramp.
### s_ccipLocalSimulator
```solidity
address internal s_ccipLocalSimulator
```
The address of the local CCIP simulator controlling this off-ramp.
### s_dynamicConfig
```solidity
DynamicConfig internal s_dynamicConfig
```
Current dynamic configuration settings for the off-ramp.
### s_poolsBySourceToken
```solidity
EnumerableMapAddresses.AddressToAddressMap private s_poolsBySourceToken
```
Maps source token addresses to their corresponding token pool addresses.
## Functions
### constructor
```solidity
constructor(
address ccipLocalSimulator,
DynamicConfig memory dynamicConfig,
RateLimiter.Config memory config,
uint64 sourceChainSelector,
address[] memory sourceTokens,
address[] memory pools
) AggregateRateLimiter(config)
```
Initializes the off-ramp with simulator settings, configuration parameters, and token pool mappings.
**Parameters:**
| Parameter | Type | Description |
| ------------------- | -------------------- | ------------------------------------------- |
| ccipLocalSimulator | `address` | Address of the CCIP simulator |
| dynamicConfig | `DynamicConfig` | Initial dynamic configuration settings |
| config | `RateLimiter.Config` | Rate limiter configuration |
| sourceChainSelector | `uint64` | Chain selector for the source chain |
| sourceTokens | `address[]` | Array of source token addresses |
| pools | `address[]` | Array of corresponding token pool addresses |
### executeSingleMessage
```solidity
function executeSingleMessage(
Internal.EVM2EVMMessage memory message,
bytes[] memory offchainTokenData
) external
```
Processes a single cross-chain message by releasing tokens and executing the receiver's callback.
**Parameters:**
| Parameter | Type | Description |
| ----------------- | ------------------------- | ------------------------------------ |
| message | `Internal.EVM2EVMMessage` | The cross-chain message to process |
| offchainTokenData | `bytes[]` | Token data provided by off-chain DON |
### getPoolBySourceToken
```solidity
function getPoolBySourceToken(IERC20 sourceToken) public view returns (IPool)
```
Retrieves the token pool associated with a source token.
**Parameters:**
| Parameter | Type | Description |
| ----------- | -------- | ----------------------------------- |
| sourceToken | `IERC20` | The source token address to look up |
**Returns:**
| Type | Description |
| ------- | -------------------------------------------- |
| `IPool` | The token pool contract for the source token |
### _releaseOrMintTokens
```solidity
function _releaseOrMintTokens(
Client.EVMTokenAmount[] memory sourceTokenAmounts,
bytes memory originalSender,
address receiver,
bytes[] memory sourceTokenData,
bytes[] memory offchainTokenData
) internal returns (Client.EVMTokenAmount[] memory)
```
Handles the release or minting of tokens through token pools. This function includes safety measures against malicious tokens and rate limiting.
**Parameters:**
| Parameter | Type | Description |
| ------------------ | ------------------------- | --------------------------------------------------------------- |
| sourceTokenAmounts | `Client.EVMTokenAmount[]` | List of tokens and amount values to be released/minted |
| originalSender | `bytes` | The message sender |
| receiver | `address` | The address that will receive the tokens |
| sourceTokenData | `bytes[]` | Array of token data returned by token pools on the source chain |
| offchainTokenData | `bytes[]` | Array of token data fetched offchain by the DON |
**Returns:**
| Type | Description |
| ------------------------- | ---------------------------------------------- |
| `Client.EVMTokenAmount[]` | Array of processed token amounts and addresses |
---
# MockOffchainAggregator v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/mock-offchain-aggregator
## MockOffchainAggregator
[`MockOffchainAggregator`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/data-feeds/MockOffchainAggregator.sol) simulates a Chainlink Data Feed aggregator for testing purposes. This contract maintains price feed data and allows manual updates of answers and round data.
## Variables
### decimals
```solidity
uint8 public decimals
```
The number of decimal places in the answer values.
### getAnswer
```solidity
mapping(uint256 => int256) public getAnswer
```
Maps round IDs to their corresponding price answers.
### getTimestamp
```solidity
mapping(uint256 => uint256) public getTimestamp
```
Maps round IDs to their update timestamps.
### latestAnswer
```solidity
int256 public latestAnswer
```
The most recent price answer.
### latestRound
```solidity
uint256 public latestRound
```
The most recent round ID.
### latestTimestamp
```solidity
uint256 public latestTimestamp
```
The timestamp of the most recent update.
### maxAnswer
```solidity
int192 public maxAnswer
```
The highest answer the system can report. Not exposed from the Proxy contract.
### minAnswer
```solidity
int192 public minAnswer
```
The lowest answer the system can report. Not exposed from the Proxy contract.
## Functions
### constructor
```solidity
constructor(uint8 _decimals, int256 _initialAnswer)
```
Initializes the aggregator with decimal precision and an initial answer.
**Parameters:**
| Parameter | Type | Description |
| --------------- | -------- | --------------------------------------- |
| _decimals | `uint8` | The number of decimal places in answers |
| _initialAnswer | `int256` | The initial price answer |
### getRoundData
```solidity
function getRoundData(uint80 _roundId) external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
Retrieves the complete round data for a specific round ID.
**Parameters:**
| Parameter | Type | Description |
| --------- | -------- | --------------------- |
| _roundId | `uint80` | The round ID to query |
**Returns:**
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------- |
| roundId | `uint80` | The round ID from the aggregator for this round |
| answer | `int256` | The price answer for this round |
| startedAt | `uint256` | Timestamp when the round started |
| updatedAt | `uint256` | Timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### latestRoundData
```solidity
function latestRoundData() external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
Retrieves the latest round data from the aggregator.
**Returns:**
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------- |
| roundId | `uint80` | The round ID from the aggregator for this round |
| answer | `int256` | The latest price answer |
| startedAt | `uint256` | Timestamp when the round started |
| updatedAt | `uint256` | Timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### updateAnswer
```solidity
function updateAnswer(int256 _answer) public
```
Updates the latest answer and associated round data.
**Parameters:**
| Parameter | Type | Description |
| --------- | -------- | -------------------- |
| _answer | `int256` | The new price answer |
### updateMinAndMaxAnswers
```solidity
function updateMinAndMaxAnswers(int192 _minAnswer, int192 _maxAnswer) external
```
Updates the minimum and maximum allowed answer values.
**Parameters:**
| Parameter | Type | Description |
| ----------- | -------- | ------------------------------ |
| _minAnswer | `int192` | The new minimum allowed answer |
| _maxAnswer | `int192` | The new maximum allowed answer |
### updateRoundData
```solidity
function updateRoundData(
uint80 _roundId,
int256 _answer,
uint256 _timestamp,
uint256 _startedAt
) public
```
Updates all data for a specific round.
**Parameters:**
| Parameter | Type | Description |
| ----------- | --------- | ----------------------------- |
| _roundId | `uint80` | The round ID to update |
| _answer | `int256` | The new price answer |
| _timestamp | `uint256` | The new update timestamp |
| _startedAt | `uint256` | The new round start timestamp |
---
# MockV3Aggregator v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/mock-v3-aggregator
## MockV3Aggregator
[`MockV3Aggregator`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/data-feeds/MockV3Aggregator.sol) implements the AggregatorV2V3Interface for testing purposes. This contract acts as a proxy to a MockOffchainAggregator instance and supports aggregator upgrades.
## Variables
### aggregator
```solidity
address public aggregator
```
The address of the current MockOffchainAggregator implementation.
### proposedAggregator
```solidity
address public proposedAggregator
```
The address of the proposed new aggregator implementation.
### version
```solidity
uint256 public constant override version = 0
```
The version number of this aggregator implementation.
## Functions
### confirmAggregator
```solidity
function confirmAggregator(address _aggregator) external
```
Confirms a proposed aggregator upgrade.
**Parameters:**
| Parameter | Type | Description |
| ------------ | --------- | ------------------------------------------ |
| _aggregator | `address` | The proposed aggregator address to confirm |
### constructor
```solidity
constructor(uint8 _decimals, int256 _initialAnswer)
```
Initializes the contract by deploying a new MockOffchainAggregator instance.
**Parameters:**
| Parameter | Type | Description |
| --------------- | -------- | --------------------------------------- |
| _decimals | `uint8` | The number of decimal places in answers |
| _initialAnswer | `int256` | The initial price answer |
### decimals
```solidity
function decimals() external view returns (uint8)
```
Retrieves the number of decimal places in the answer values.
**Returns:**
| Type | Description |
| ------- | -------------------------------------- |
| `uint8` | The number of decimals in the response |
### description
```solidity
function description() external pure returns (string memory)
```
Returns the source file path of this contract.
**Returns:**
| Type | Description |
| -------- | --------------------------------- |
| `string` | The description of the price feed |
### getAnswer
```solidity
function getAnswer(uint256 roundId) external view returns (int256)
```
Retrieves the answer for a specific round.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | --------------------- |
| roundId | `uint256` | The round ID to query |
**Returns:**
| Type | Description |
| -------- | ---------------------------------------- |
| `int256` | The price answer for the specified round |
### getRoundData
```solidity
function getRoundData(uint80 _roundId) external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
Retrieves the complete round data for a specific round ID.
**Parameters:**
| Parameter | Type | Description |
| --------- | -------- | --------------------- |
| _roundId | `uint80` | The round ID to query |
**Returns:**
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------- |
| roundId | `uint80` | The round ID from the aggregator for this round |
| answer | `int256` | The price answer for this round |
| startedAt | `uint256` | Timestamp when the round started |
| updatedAt | `uint256` | Timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### getTimestamp
```solidity
function getTimestamp(uint256 roundId) external view returns (uint256)
```
Retrieves the timestamp for a specific round.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | --------------------- |
| roundId | `uint256` | The round ID to query |
**Returns:**
| Type | Description |
| --------- | ---------------------------------------- |
| `uint256` | The timestamp when the round was updated |
### latestAnswer
```solidity
function latestAnswer() external view returns (int256)
```
Retrieves the most recent answer.
**Returns:**
| Type | Description |
| -------- | ---------------------------- |
| `int256` | The latest price feed answer |
### latestRound
```solidity
function latestRound() external view returns (uint256)
```
Retrieves the most recent round ID.
**Returns:**
| Type | Description |
| --------- | ------------------- |
| `uint256` | The latest round ID |
### latestRoundData
```solidity
function latestRoundData() external view returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
Retrieves the latest round data from the aggregator.
**Returns:**
| Parameter | Type | Description |
| --------------- | --------- | ----------------------------------------------- |
| roundId | `uint80` | The round ID from the aggregator for this round |
| answer | `int256` | The latest price answer |
| startedAt | `uint256` | Timestamp when the round started |
| updatedAt | `uint256` | Timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### latestTimestamp
```solidity
function latestTimestamp() external view returns (uint256)
```
Retrieves the timestamp of the latest answer.
**Returns:**
| Type | Description |
| --------- | ------------------------------------------------ |
| `uint256` | The timestamp when the latest answer was updated |
### proposeAggregator
```solidity
function proposeAggregator(AggregatorV2V3Interface _aggregator) external
```
Proposes a new aggregator implementation.
**Parameters:**
| Parameter | Type | Description |
| ------------ | ------------------------- | --------------------------------- |
| _aggregator | `AggregatorV2V3Interface` | The new aggregator implementation |
### updateAnswer
```solidity
function updateAnswer(int256 _answer) public
```
Updates the latest answer and associated round data.
**Parameters:**
| Parameter | Type | Description |
| --------- | -------- | -------------------- |
| _answer | `int256` | The new price answer |
### updateRoundData
```solidity
function updateRoundData(
uint80 _roundId,
int256 _answer,
uint256 _timestamp,
uint256 _startedAt
) public
```
Updates all data for a specific round.
**Parameters:**
| Parameter | Type | Description |
| ----------- | --------- | ----------------------------- |
| _roundId | `uint80` | The round ID to update |
| _answer | `int256` | The new price answer |
| _timestamp | `uint256` | The new update timestamp |
| _startedAt | `uint256` | The new round start timestamp |
---
# Register v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/register
## Register
[`Register`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/ccip/Register.sol) maintains a registry of CCIP network configurations for different blockchain networks. This contract provides pre-configured settings for various test networks and allows custom network configurations.
## Structs
### NetworkDetails
```solidity
struct NetworkDetails {
uint64 chainSelector;
address routerAddress;
address linkAddress;
address wrappedNativeAddress;
address ccipBnMAddress;
address ccipLnMAddress;
}
```
Configuration details for a CCIP-enabled network:
• chainSelector - Unique identifier for the blockchain network in CCIP
• routerAddress - Address of the CCIP Router contract
• linkAddress - Address of the LINK token contract
• wrappedNativeAddress - Address of the wrapped native token
• ccipBnMAddress - Address of the CCIP BnM token
• ccipLnMAddress - Address of the CCIP LnM token
## Variables
### s_networkDetails
```solidity
mapping(uint256 chainId => NetworkDetails) internal s_networkDetails
```
Stores network configurations indexed by chain ID. Contains pre-configured settings for test networks including Sepolia, Mumbai, Fuji, and others.
## Functions
### constructor
```solidity
constructor() public
```
Initializes the contract with pre-configured network details for various test networks including:
- Ethereum Sepolia (11155111)
- OP Sepolia (11155420)
- Polygon Mumbai (80001)
- Avalanche Fuji (43113)
- BNB Chain Testnet (97)
- Arbitrum Sepolia (421614)
- Base Sepolia (84532)
- Wemix Testnet (1112)
- Kroma Sepolia (2358)
- Gnosis Chiado (10200)
### getNetworkDetails
```solidity
function getNetworkDetails(uint256 chainId) external view returns (NetworkDetails memory)
```
Retrieves the CCIP configuration for a specific blockchain network.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------ |
| chainId | `uint256` | The blockchain network ID to look up |
**Returns:**
| Type | Description |
| ---------------- | ----------------------------------------------- |
| `NetworkDetails` | The network configuration details for the chain |
### setNetworkDetails
```solidity
function setNetworkDetails(uint256 chainId, NetworkDetails memory networkDetails) external
```
Updates or adds CCIP configuration for a blockchain network.
**Parameters:**
| Parameter | Type | Description |
| -------------- | ---------------- | -------------------------------------- |
| chainId | `uint256` | The blockchain network ID to configure |
| networkDetails | `NetworkDetails` | The network configuration to set |
---
# WETH9 v0.2.1 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.1/weth9
## WETH9
[`WETH9`](https://github.com/smartcontractkit/chainlink-local/blob/ba1f4636e657f161df634379a5057a5a394e2fbb/src/shared/WETH9.sol) implements the Wrapped Ether (WETH) token standard. This contract allows users to wrap ETH into an ERC20-compatible token and unwrap it back to ETH.
## Events
### Approval
```solidity
event Approval(address indexed src, address indexed guy, uint256 wad)
```
Emitted when an approval is set.
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------------- |
| src | `address` | The address giving approval (indexed) |
| guy | `address` | The address receiving approval (indexed) |
| wad | `uint256` | The amount of tokens approved |
### Deposit
```solidity
event Deposit(address indexed dst, uint256 wad)
```
Emitted when ETH is wrapped to WETH.
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------ |
| dst | `address` | The address receiving WETH (indexed) |
| wad | `uint256` | The amount of ETH wrapped |
### Transfer
```solidity
event Transfer(address indexed src, address indexed dst, uint256 wad)
```
Emitted when tokens are transferred.
| Parameter | Type | Description |
| --------- | --------- | --------------------------------- |
| src | `address` | The sender's address (indexed) |
| dst | `address` | The recipient's address (indexed) |
| wad | `uint256` | The amount of tokens transferred |
### Withdrawal
```solidity
event Withdrawal(address indexed src, uint256 wad)
```
Emitted when WETH is unwrapped back to ETH.
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------- |
| src | `address` | The address unwrapping WETH (indexed) |
| wad | `uint256` | The amount of WETH unwrapped |
## Variables
### allowance
```solidity
mapping(address => mapping(address => uint256)) public allowance
```
Maps owner addresses to spender addresses to approved amounts.
### balanceOf
```solidity
mapping(address => uint256) public balanceOf
```
Maps addresses to their token balances.
### decimals
```solidity
uint8 public decimals = 18
```
The number of decimal places used by the token.
### name
```solidity
string public name = "Wrapped Ether"
```
The name of the token.
### symbol
```solidity
string public symbol = "WETH"
```
The symbol of the token.
## Functions
### _deposit
```solidity
function _deposit() internal
```
Internal function to handle ETH deposits and mint corresponding WETH tokens.
### approve
```solidity
function approve(address guy, uint256 wad) public returns (bool)
```
Approves another address to spend tokens.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ------------------------------- |
| guy | `address` | The address to approve |
| wad | `uint256` | The amount of tokens to approve |
**Returns:**
| Type | Description |
| ------ | ------------------- |
| `bool` | Always returns true |
### deposit
```solidity
function deposit() external payable
```
Deposits ETH and mints WETH tokens.
### receive
```solidity
receive() external payable
```
Fallback function to handle direct ETH transfers. Calls `_deposit()` to wrap received ETH into WETH.
### totalSupply
```solidity
function totalSupply() public view returns (uint256)
```
Returns the total supply of WETH tokens.
**Returns:**
| Type | Description |
| --------- | ---------------------------------------- |
| `uint256` | The total amount of WETH in the contract |
### transfer
```solidity
function transfer(address dst, uint256 wad) public returns (bool)
```
Transfers tokens to another address.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | -------------------------------- |
| dst | `address` | The recipient's address |
| wad | `uint256` | The amount of tokens to transfer |
**Returns:**
| Type | Description |
| ------ | ------------------------------ |
| `bool` | True if the transfer succeeded |
### transferFrom
```solidity
function transferFrom(address src, address dst, uint256 wad) public returns (bool)
```
Transfers tokens from one address to another.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | -------------------------------- |
| src | `address` | The source address |
| dst | `address` | The destination address |
| wad | `uint256` | The amount of tokens to transfer |
**Returns:**
| Type | Description |
| ------ | ------------------------------ |
| `bool` | True if the transfer succeeded |
### withdraw
```solidity
function withdraw(uint256 wad) external
```
Withdraws ETH by burning WETH tokens.
**Parameters:**
| Parameter | Type | Description |
| --------- | --------- | ---------------------- |
| wad | `uint256` | The amount to withdraw |
---
# AggregatorInterface v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/aggregator-interface
## AggregatorInterface
An interface that defines the standard methods for accessing price feed data from an aggregator contract.
[`AggregatorInterface`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/data-feeds/interfaces/AggregatorInterface.sol)
## Events
### AnswerUpdated
```solidity
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------------- |
| current | int256 | The updated answer |
| roundId | uint256 | The round ID for which the answer was updated |
| updatedAt | uint256 | The timestamp when the answer was updated |
### NewRound
```solidity
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------------------- |
| roundId | uint256 | The round ID of the new round |
| startedBy | address | The address of the account that started the round |
| startedAt | uint256 | The timestamp when the round was started |
## Functions
### getAnswer
Retrieves the price answer for a specific round.
```solidity
function getAnswer(uint256 roundId) external view returns (int256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ---------------------------------- |
| roundId | uint256 | The round ID to get the answer for |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | --------------------------------- |
| (unnamed) | int256 | The answer for the given round ID |
### getTimestamp
Retrieves the timestamp for a specific round.
```solidity
function getTimestamp(uint256 roundId) external view returns (uint256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------- |
| roundId | uint256 | The round ID to get the timestamp for |
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------ |
| (unnamed) | uint256 | The timestamp for the given round ID |
### latestAnswer
Retrieves the most recent price answer.
```solidity
function latestAnswer() external view returns (int256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ----------------- |
| (unnamed) | int256 | The latest answer |
### latestRound
Retrieves the most recent round ID.
```solidity
function latestRound() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ------------------- |
| (unnamed) | uint256 | The latest round ID |
### latestTimestamp
Retrieves the timestamp of the most recent answer.
```solidity
function latestTimestamp() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ---------------------------------- |
| (unnamed) | uint256 | The timestamp of the latest answer |
---
# AggregatorV2V3Interface v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/aggregator-v2-v3-interface
## AggregatorV2V3Interface
A combined interface that inherits functionality from both AggregatorInterface and AggregatorV3Interface, providing a complete set of methods for accessing price feed data.
[`AggregatorV2V3Interface`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/data-feeds/interfaces/AggregatorV2V3Interface.sol)
## Inheritance
This interface inherits from:
- [`AggregatorInterface`](/chainlink-local/api-reference/v0.2.2/aggregator-interface) - Provides basic price feed functionality
- [`AggregatorV3Interface`](/chainlink-local/api-reference/v0.2.2/aggregator-v3-interface) - Provides extended price feed functionality
---
# AggregatorV3Interface v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/aggregator-v3-interface
## AggregatorV3Interface
An interface for accessing detailed price feed data and metadata from an aggregator contract, providing enhanced functionality for retrieving round data and contract information.
[`AggregatorV3Interface`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/data-feeds/interfaces/AggregatorV3Interface.sol)
## Functions
### decimals
Retrieves the number of decimal places used by the aggregator.
```solidity
function decimals() external view returns (uint8)
```
#### Returns
| Parameter | Type | Description |
| --------- | ----- | ---------------------- |
| (unnamed) | uint8 | The number of decimals |
### description
Retrieves the description of the aggregator.
```solidity
function description() external view returns (string memory)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------ | --------------------------------- |
| (unnamed) | string | The description of the aggregator |
### getRoundData
Retrieves the complete round data for a specific round ID.
```solidity
function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------ | -------------------------------- |
| _roundId | uint80 | The round ID to get the data for |
#### Returns
| Parameter | Type | Description |
| --------------- | ------- | --------------------------------------------- |
| roundId | uint80 | The round ID |
| answer | int256 | The answer for the round |
| startedAt | uint256 | The timestamp when the round started |
| updatedAt | uint256 | The timestamp when the round was updated |
| answeredInRound | uint80 | The round ID in which the answer was computed |
### latestRoundData
Retrieves the complete round data for the most recent round.
```solidity
function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
```
#### Returns
| Parameter | Type | Description |
| --------------- | ------- | ---------------------------------------------------- |
| roundId | uint80 | The latest round ID |
| answer | int256 | The latest answer |
| startedAt | uint256 | The timestamp when the latest round started |
| updatedAt | uint256 | The timestamp when the latest round was updated |
| answeredInRound | uint80 | The round ID in which the latest answer was computed |
### version
Retrieves the version number of the aggregator.
```solidity
function version() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ----------------------------- |
| (unnamed) | uint256 | The version of the aggregator |
---
# BurnMintERC677Helper v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/burn-mint-erc677-helper
## BurnMintERC677Helper
A helper contract that extends the BurnMintERC677 token contract with additional minting functionality for testing and development purposes.
[`BurnMintERC677Helper`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/ccip/BurnMintERC677Helper.sol)
## Functions
### constructor
Creates a new BurnMintERC677Helper token with the specified name and symbol.
```solidity
constructor(
string memory name,
string memory symbol
) BurnMintERC677(name, symbol, 18, 0)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------ | ----------------------- |
| name | string | The name of the token |
| symbol | string | The symbol of the token |
### drip
Mints a single token to the specified address for testing purposes.
```solidity
function drip(address to) external
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------- |
| to | address | The address to receive the minted token |
---
# CCIPLocalSimulatorFork v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/ccip-local-simulator-fork-js
## CCIPLocalSimulatorFork
A JavaScript module that provides utilities for simulating CCIP (Cross-Chain Interoperability Protocol) message routing in a local or forked environment.
[`CCIPLocalSimulatorFork`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/scripts/CCIPLocalSimulatorFork.js)
## Types
### Evm2EvmMessage
Represents a cross-chain message in the EVM-to-EVM communication protocol. Contains all necessary information for message routing and token transfers across chains.
| Property | Type | Description |
| ------------------- | ---------------------------------------- | ---------------------------------------------- |
| sourceChainSelector | `bigint` | The identifier of the source chain |
| sender | `string` | The address that sent the message |
| receiver | `string` | The address that will receive the message |
| sequenceNumber | `bigint` | The sequence number of the message |
| gasLimit | `bigint` | The gas limit for executing the message |
| strict | `boolean` | Whether the message requires strict execution |
| nonce | `bigint` | The nonce of the message |
| feeToken | `string` | The token used to pay fees |
| feeTokenAmount | `bigint` | The amount of fee token to be paid |
| data | `string` | The message payload data |
| tokenAmounts | `Array<{token: string, amount: bigint}>` | Array of tokens and amounts being transferred |
| sourceTokenData | `Array` | Array of token-specific data from source chain |
| messageId | `string` | The unique identifier of the message |
## Functions
### getEvm2EvmMessage
Extracts and parses a CCIP message from a transaction receipt by looking for the CCIPSendRequested event.
```javascript
function getEvm2EvmMessage(receipt) => Evm2EvmMessage | null
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------ |
| receipt | `object` | The transaction receipt from the `ccipSend` call |
#### Returns
| Type | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------- |
| `Evm2EvmMessage\|null` | The parsed EVM-to-EVM message if found in the receipt logs, or null if no relevant event found |
### requestLinkFromTheFaucet
Requests LINK tokens from a faucet contract for testing purposes.
```javascript
async function requestLinkFromTheFaucet(linkAddress, to, amount) => Promise
```
#### Parameters
| Parameter | Type | Description |
| ----------- | -------- | ------------------------------------------------------- |
| linkAddress | `string` | The address of the LINK contract on the current network |
| to | `string` | The address to send LINK to |
| amount | `bigint` | The amount of LINK to request |
#### Returns
| Type | Description |
| ----------------- | -------------------------------------------------------------- |
| `Promise` | Promise resolving to the transaction hash of the fund transfer |
### routeMessage
Routes a cross-chain message on the destination network by finding the appropriate off-ramp and executing the message.
```javascript
async function routeMessage(routerAddress, evm2EvmMessage) => Promise
```
#### Parameters
| Parameter | Type | Description |
| -------------- | ---------------- | --------------------------------- |
| routerAddress | `string` | Address of the destination Router |
| evm2EvmMessage | `Evm2EvmMessage` | Sent cross-chain message |
#### Returns
| Type | Description |
| --------------- | ------------------------------------------------------------ |
| `Promise` | Resolves with no value if the message is successfully routed |
---
# CCIPLocalSimulatorFork v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/ccip-local-simulator-fork
## CCIPLocalSimulatorFork
A contract that simulates CCIP (Cross-Chain Interoperability Protocol) message routing in forked network environments, specifically designed to work with Foundry for testing cross-chain applications.
[`CCIPLocalSimulatorFork`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/ccip/CCIPLocalSimulatorFork.sol)
## Events
### CCIPSendRequested
```solidity
event CCIPSendRequested(Internal.EVM2EVMMessage message)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ----------------------- | --------------------------------- |
| message | Internal.EVM2EVMMessage | The EVM2EVM message that was sent |
## Interfaces
### IEVM2EVMOffRampFork
Interface for executing CCIP messages on the off-ramp.
```solidity
interface IEVM2EVMOffRampFork {
function executeSingleMessage(
Internal.EVM2EVMMessage memory message,
bytes[] memory offchainTokenData,
uint32[] memory tokenGasOverrides
) external;
}
```
### IRouterFork
Interface for accessing off-ramp configurations.
```solidity
interface IRouterFork {
struct OffRamp {
uint64 sourceChainSelector;
address offRamp;
}
function getOffRamps() external view returns (OffRamp[] memory);
}
```
## Structs
### OffRamp
Configuration for a CCIP off-ramp.
```solidity
struct OffRamp {
uint64 sourceChainSelector;
address offRamp;
}
```
#### Fields
| Field | Type | Description |
| ------------------- | ------- | --------------------------------------- |
| sourceChainSelector | uint64 | The chain selector for the source chain |
| offRamp | address | The address of the offRamp contract |
## Variables
### i_register
```solidity
Register immutable i_register
```
### LINK_FAUCET
```solidity
address constant LINK_FAUCET = 0x4281eCF07378Ee595C564a59048801330f3084eE
```
### s_processedMessages
```solidity
mapping(bytes32 messageId => bool isProcessed) internal s_processedMessages
```
## Functions
### constructor
Sets up the simulator environment by creating a persistent register instance and enabling event recording.
```solidity
constructor()
```
### getNetworkDetails
Fetches the network configuration for a specified blockchain network ID.
```solidity
function getNetworkDetails(uint256 chainId) external view returns (Register.NetworkDetails memory)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------------------------------------------------------------------- |
| chainId | uint256 | The blockchain network chain ID. For example 11155111 for Ethereum Sepolia. Not CCIP chain selector |
#### Returns
| Parameter | Type | Description |
| -------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------- |
| networkDetails | Register.NetworkDetails | The tuple containing: chainSelector, routerAddress, linkAddress, wrappedNativeAddress, ccipBnMAddress, ccipLnMAddress |
### requestLinkFromFaucet
Transfers LINK tokens from the faucet to a specified recipient address.
```solidity
function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ----------------------------------------------- |
| to | address | The address to which LINK tokens are to be sent |
| amount | uint256 | The amount of LINK tokens to send |
#### Returns
| Parameter | Type | Description |
| --------- | ---- | ---------------------------------------------------------------------- |
| success | bool | Returns true if the transfer of tokens was successful, otherwise false |
### setNetworkDetails
Registers or updates the network configuration for a specific blockchain.
```solidity
function setNetworkDetails(uint256 chainId, Register.NetworkDetails memory networkDetails) external
```
#### Parameters
| Parameter | Type | Description |
| -------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------- |
| chainId | uint256 | The blockchain network chain ID. For example 11155111 for Ethereum Sepolia. Not CCIP chain selector |
| networkDetails | Register.NetworkDetails | The tuple containing: chainSelector, routerAddress, linkAddress, wrappedNativeAddress, ccipBnMAddress, ccipLnMAddress |
### switchChainAndRouteMessage
Routes a cross-chain message by finding it in the event logs, switching to the destination chain, and executing it through the appropriate off-ramp.
```solidity
function switchChainAndRouteMessage(uint256 forkId) external
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| forkId | uint256 | The ID of the destination network fork. This is the returned value of `createFork()` or `createSelectFork()` |
---
# CCIPLocalSimulator v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/ccip-local-simulator
## CCIPLocalSimulator
A contract that simulates local CCIP (Cross-Chain Interoperability Protocol) operations for testing and development purposes.
[`CCIPLocalSimulator`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/ccip/CCIPLocalSimulator.sol)
## Errors
### CCIPLocalSimulator__MsgSenderIsNotTokenOwner
```solidity
error CCIPLocalSimulator__MsgSenderIsNotTokenOwner()
```
Thrown when a caller attempts to add a token but is not the token owner or CCIP admin.
## Variables
### CHAIN_SELECTOR
```solidity
uint64 constant CHAIN_SELECTOR = 16015286601757825753
```
### i_ccipBnM
```solidity
BurnMintERC677Helper internal immutable i_ccipBnM
```
### i_ccipLnM
```solidity
BurnMintERC677Helper internal immutable i_ccipLnM
```
### i_linkToken
```solidity
LinkToken internal immutable i_linkToken
```
### i_mockRouter
```solidity
MockCCIPRouter internal immutable i_mockRouter
```
### i_wrappedNative
```solidity
WETH9 internal immutable i_wrappedNative
```
### s_supportedTokens
```solidity
address[] internal s_supportedTokens
```
## Functions
### constructor
Initializes the contract by deploying and configuring all required token instances.
```solidity
constructor()
```
### configuration
Retrieves the complete configuration for local CCIP simulations.
```solidity
function configuration()
public
view
returns (
uint64 chainSelector_,
IRouterClient sourceRouter_,
IRouterClient destinationRouter_,
WETH9 wrappedNative_,
LinkToken linkToken_,
BurnMintERC677Helper ccipBnM_,
BurnMintERC677Helper ccipLnM_
)
```
#### Returns
| Parameter | Type | Description |
| ------------------- | -------------------- | -------------------------------------------------------- |
| chainSelector_ | uint64 | The unique CCIP Chain Selector |
| sourceRouter_ | IRouterClient | The source chain Router contract |
| destinationRouter_ | IRouterClient | The destination chain Router contract |
| wrappedNative_ | WETH9 | The wrapped native token which can be used for CCIP fees |
| linkToken_ | LinkToken | The LINK token |
| ccipBnM_ | BurnMintERC677Helper | The ccipBnM token |
| ccipLnM_ | BurnMintERC677Helper | The ccipLnM token |
### getSupportedTokens
Retrieves the list of tokens supported for cross-chain transfers.
```solidity
function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens)
```
#### Parameters
| Parameter | Type | Description |
| ------------- | ------ | ------------------------------ |
| chainSelector | uint64 | The unique CCIP Chain Selector |
#### Returns
| Parameter | Type | Description |
| --------- | ---------- | ----------------------------------------------------------------------------------------------- |
| tokens | address[] | Returns a list of token addresses that are supported for cross-chain transfers by the simulator |
### isChainSupported
Verifies if a given chain selector is supported by the simulator.
```solidity
function isChainSupported(uint64 chainSelector) public pure returns (bool supported)
```
#### Parameters
| Parameter | Type | Description |
| ------------- | ------ | ------------------------------ |
| chainSelector | uint64 | The unique CCIP Chain Selector |
#### Returns
| Parameter | Type | Description |
| --------- | ---- | ------------------------------------------------------------- |
| supported | bool | Returns true if `chainSelector` is supported by the simulator |
### requestLinkFromFaucet
Transfers LINK tokens from the faucet to a specified recipient address.
```solidity
function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ----------------------------------------------- |
| to | address | The address to which LINK tokens are to be sent |
| amount | uint256 | The amount of LINK tokens to send |
#### Returns
| Parameter | Type | Description |
| --------- | ---- | ---------------------------------------------------------------------- |
| success | bool | Returns true if the transfer of tokens was successful, otherwise false |
### supportNewTokenViaGetCCIPAdmin
Adds support for a new token using CCIP admin verification.
```solidity
function supportNewTokenViaGetCCIPAdmin(address tokenAddress) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | ------- | --------------------------------------------------------------- |
| tokenAddress | address | The address of the token to add to the list of supported tokens |
### supportNewTokenViaOwner
Adds support for a new token using owner verification.
```solidity
function supportNewTokenViaOwner(address tokenAddress) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | ------- | --------------------------------------------------------------- |
| tokenAddress | address | The address of the token to add to the list of supported tokens |
---
# Chainlink Local v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2
## CCIP
These contracts provide Cross-Chain Interoperability Protocol (CCIP) functionality in a local testing environment:
- [CCIPLocalSimulator](/chainlink-local/api-reference/v0.2.2/ccip-local-simulator) - Local CCIP message routing simulator
- [CCIPLocalSimulatorFork](/chainlink-local/api-reference/v0.2.2/ccip-local-simulator-fork) - CCIP simulator for forked networks
- [CCIPLocalSimulatorFork JS](/chainlink-local/api-reference/v0.2.2/ccip-local-simulator-fork-js) - JavaScript utilities for CCIP simulation
- [Register](/chainlink-local/api-reference/v0.2.2/register) - CCIP network configuration registry
## Data Feeds
Contracts for simulating Chainlink Data Feeds:
- [AggregatorInterface](/chainlink-local/api-reference/v0.2.2/aggregator-interface) - Basic price feed interface
- [AggregatorV2V3Interface](/chainlink-local/api-reference/v0.2.2/aggregator-v2-v3-interface) - Combined V2/V3 price feed interface
- [AggregatorV3Interface](/chainlink-local/api-reference/v0.2.2/aggregator-v3-interface) - Extended price feed interface
- [MockOffchainAggregator](/chainlink-local/api-reference/v0.2.2/mock-offchain-aggregator) - Mock implementation of off-chain aggregator
- [MockV3Aggregator](/chainlink-local/api-reference/v0.2.2/mock-v3-aggregator) - Mock implementation of V3 aggregator
## Token Contracts
Standard token implementations for testing:
- [BurnMintERC677Helper](/chainlink-local/api-reference/v0.2.2/burn-mint-erc677-helper) - Helper contract for ERC677 token operations
- [LinkToken](/chainlink-local/api-reference/v0.2.2/link-token) - LINK token implementation
- [WETH9](/chainlink-local/api-reference/v0.2.2/weth9) - Wrapped Ether implementation
---
# LinkToken v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/link-token
## LinkToken
A contract that implements the ChainLink Token (LINK) using the ERC677 standard with a fixed total supply and standard token details.
[`LinkToken`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/shared/LinkToken.sol)
## Functions
### constructor
Initializes a new LINK token with standard token details.
```solidity
constructor()
```
### _onCreate
Internal hook that handles the initial token supply minting.
```solidity
function _onCreate() internal virtual
```
---
# MockOffchainAggregator v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/mock-offchain-aggregator
## MockOffchainAggregator
A mock implementation of an offchain aggregator contract used for testing purposes, simulating price feed behavior with configurable parameters.
[`MockOffchainAggregator`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/data-feeds/MockOffchainAggregator.sol)
## Variables
### decimals
```solidity
uint8 public decimals
```
### getAnswer
```solidity
mapping(uint256 => int256) public getAnswer
```
### getTimestamp
```solidity
mapping(uint256 => uint256) public getTimestamp
```
### latestAnswer
```solidity
int256 public latestAnswer
```
### latestRound
```solidity
uint256 public latestRound
```
### latestTimestamp
```solidity
uint256 public latestTimestamp
```
### maxAnswer
```solidity
int192 public maxAnswer
```
### minAnswer
```solidity
int192 public minAnswer
```
## Functions
### constructor
Initializes a new mock aggregator with the specified decimal precision and starting price.
```solidity
constructor(uint8 _decimals, int256 _initialAnswer)
```
#### Parameters
| Parameter | Type | Description |
| --------------- | ------ | --------------------------------------------------- |
| _decimals | uint8 | The number of decimals for the aggregator |
| _initialAnswer | int256 | The initial answer to be set in the mock aggregator |
### getRoundData
Retrieves the complete round data for a specific round ID.
```solidity
function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------ | -------------------------------- |
| _roundId | uint80 | The round ID to get the data for |
#### Returns
| Parameter | Type | Description |
| --------------- | ------- | --------------------------------------------- |
| roundId | uint80 | The round ID |
| answer | int256 | The answer for the round |
| startedAt | uint256 | The timestamp when the round started |
| updatedAt | uint256 | The timestamp when the round was updated |
| answeredInRound | uint80 | The round ID in which the answer was computed |
### latestRoundData
Retrieves the complete round data for the most recent round.
```solidity
function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
```
#### Returns
| Parameter | Type | Description |
| --------------- | ------- | ---------------------------------------------------- |
| roundId | uint80 | The latest round ID |
| answer | int256 | The latest answer |
| startedAt | uint256 | The timestamp when the latest round started |
| updatedAt | uint256 | The timestamp when the latest round was updated |
| answeredInRound | uint80 | The round ID in which the latest answer was computed |
### updateAnswer
Updates the latest answer and associated data.
```solidity
function updateAnswer(int256 _answer) public
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------ |
| _answer | int256 | The new answer to be set |
### updateMinAndMaxAnswers
Updates the minimum and maximum allowed answers for the aggregator.
```solidity
function updateMinAndMaxAnswers(int192 _minAnswer, int192 _maxAnswer) external
```
#### Parameters
| Parameter | Type | Description |
| ----------- | ------ | ---------------------- |
| _minAnswer | int192 | The new minimum answer |
| _maxAnswer | int192 | The new maximum answer |
### updateRoundData
Updates all data for a specific round.
```solidity
function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public
```
#### Parameters
| Parameter | Type | Description |
| ----------- | ------- | ------------------------------------ |
| _roundId | uint80 | The round ID to be updated |
| _answer | int256 | The new answer to be set |
| _timestamp | uint256 | The timestamp to be set |
| _startedAt | uint256 | The timestamp when the round started |
---
# MockV3Aggregator v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/mock-v3-aggregator
## MockV3Aggregator
A mock implementation of the AggregatorV2V3Interface for testing purposes, providing a simulated price feed through interaction with a MockOffchainAggregator.
[`MockV3Aggregator`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/data-feeds/MockV3Aggregator.sol)
## Inheritance
This contract inherits from:
- [`AggregatorV2V3Interface`](/chainlink-local/api-reference/v0.2.2/aggregator-v2-v3-interface) - Combined interface that includes both AggregatorInterface and AggregatorV3Interface functionality
## Variables
### aggregator
```solidity
address public aggregator
```
### proposedAggregator
```solidity
address public proposedAggregator
```
### version
```solidity
uint256 public constant version
```
## Functions
### confirmAggregator
Confirms and sets a previously proposed aggregator as the current one.
```solidity
function confirmAggregator(address _aggregator) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | ------- | -------------------------------------- |
| _aggregator | address | The address of the proposed aggregator |
### constructor
Initializes the mock aggregator with specified decimals and initial answer.
```solidity
constructor(uint8 _decimals, int256 _initialAnswer)
```
#### Parameters
| Parameter | Type | Description |
| --------------- | ------ | --------------------------------------------------- |
| _decimals | uint8 | The number of decimals for the aggregator |
| _initialAnswer | int256 | The initial answer to be set in the mock aggregator |
### decimals
Retrieves the number of decimal places used by the aggregator.
```solidity
function decimals() external view returns (uint8)
```
#### Returns
| Parameter | Type | Description |
| --------- | ----- | ---------------------- |
| (unnamed) | uint8 | The number of decimals |
### description
Returns the description of the aggregator.
```solidity
function description() external pure returns (string memory)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------- |
| (unnamed) | string | The source file path of the mock aggregator |
### getAnswer
Retrieves the answer for a specific round.
```solidity
function getAnswer(uint256 roundId) external view returns (int256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ---------------------------------- |
| roundId | uint256 | The round ID to get the answer for |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | --------------------------------- |
| (unnamed) | int256 | The answer for the given round ID |
### getRoundData
Retrieves the complete round data for a specific round ID.
```solidity
function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------ | -------------------------------- |
| _roundId | uint80 | The round ID to get the data for |
#### Returns
| Parameter | Type | Description |
| --------------- | ------- | --------------------------------------------- |
| roundId | uint80 | The round ID |
| answer | int256 | The answer for the round |
| startedAt | uint256 | The timestamp when the round started |
| updatedAt | uint256 | The timestamp when the round was updated |
| answeredInRound | uint80 | The round ID in which the answer was computed |
### getTimestamp
Retrieves the timestamp for a specific round.
```solidity
function getTimestamp(uint256 roundId) external view returns (uint256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------- |
| roundId | uint256 | The round ID to get the timestamp for |
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------ |
| (unnamed) | uint256 | The timestamp for the given round ID |
### latestAnswer
Retrieves the most recent answer.
```solidity
function latestAnswer() external view returns (int256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ----------------- |
| (unnamed) | int256 | The latest answer |
### latestRound
Retrieves the most recent round ID.
```solidity
function latestRound() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ------------------- |
| (unnamed) | uint256 | The latest round ID |
### latestRoundData
Retrieves the complete round data for the most recent round.
```solidity
function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
```
#### Returns
| Parameter | Type | Description |
| --------------- | ------- | ---------------------------------------------------- |
| roundId | uint80 | The latest round ID |
| answer | int256 | The latest answer |
| startedAt | uint256 | The timestamp when the latest round started |
| updatedAt | uint256 | The timestamp when the latest round was updated |
| answeredInRound | uint80 | The round ID in which the latest answer was computed |
### latestTimestamp
Retrieves the timestamp of the most recent answer.
```solidity
function latestTimestamp() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ---------------------------------- |
| (unnamed) | uint256 | The timestamp of the latest answer |
### proposeAggregator
Proposes a new aggregator for future use.
```solidity
function proposeAggregator(AggregatorV2V3Interface _aggregator) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | ----------------------- | -------------------------------------- |
| _aggregator | AggregatorV2V3Interface | The address of the proposed aggregator |
### updateAnswer
Updates the latest answer in the underlying mock aggregator.
```solidity
function updateAnswer(int256 _answer) public
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------ |
| _answer | int256 | The new answer to be set |
### updateRoundData
Updates all data for a specific round in the underlying mock aggregator.
```solidity
function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public
```
#### Parameters
| Parameter | Type | Description |
| ----------- | ------- | ------------------------------------ |
| _roundId | uint80 | The round ID to be updated |
| _answer | int256 | The new answer to be set |
| _timestamp | uint256 | The timestamp to be set |
| _startedAt | uint256 | The timestamp when the round started |
---
# Register v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/register
## Register
A contract that manages network configuration details for various blockchain networks, storing and retrieving network-specific parameters.
[`Register`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/ccip/Register.sol)
## Structs
### NetworkDetails
Configuration details for a CCIP network.
```solidity
struct NetworkDetails {
uint64 chainSelector;
address routerAddress;
address linkAddress;
address wrappedNativeAddress;
address ccipBnMAddress;
address ccipLnMAddress;
address rmnProxyAddress;
address registryModuleOwnerCustomAddress;
address tokenAdminRegistryAddress;
}
```
#### Fields
| Field | Type | Description |
| -------------------------------- | ------- | -------------------------------------------------------- |
| chainSelector | uint64 | The unique identifier for the chain |
| routerAddress | address | The address of the CCIP router contract |
| linkAddress | address | The address of the LINK token |
| wrappedNativeAddress | address | The address of the wrapped native token |
| ccipBnMAddress | address | The address of the CCIP BnM token |
| ccipLnMAddress | address | The address of the CCIP LnM token |
| rmnProxyAddress | address | The address of the RMN proxy |
| registryModuleOwnerCustomAddress | address | The address of the registry module owner custom contract |
| tokenAdminRegistryAddress | address | The address of the token admin registry |
## Variables
### s_networkDetails
```solidity
mapping(uint256 chainId => NetworkDetails) internal s_networkDetails
```
## Functions
### constructor
Initializes the contract with predefined network configurations for various test networks.
```solidity
constructor()
```
### getNetworkDetails
Fetches the network configuration for a specified chain ID.
```solidity
function getNetworkDetails(uint256 chainId) external view returns (NetworkDetails memory networkDetails)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------------ |
| chainId | uint256 | The ID of the chain to get the details for |
#### Returns
| Parameter | Type | Description |
| -------------- | -------------- | ---------------------------------------------- |
| networkDetails | NetworkDetails | The network details for the specified chain ID |
### setNetworkDetails
Updates or adds network configuration details for a specific chain ID.
```solidity
function setNetworkDetails(uint256 chainId, NetworkDetails memory networkDetails) external
```
#### Parameters
| Parameter | Type | Description |
| -------------- | -------------- | ----------------------------------------------------- |
| chainId | uint256 | The ID of the chain to set the details for |
| networkDetails | NetworkDetails | The network details to set for the specified chain ID |
---
# WETH9 v0.2.2 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.2/weth9
## WETH9
A contract that implements Wrapped Ether (WETH), allowing users to wrap and unwrap ETH to use it as an ERC20-compatible token.
[`WETH9`](https://github.com/smartcontractkit/chainlink-local/blob/cd3bfb8c42716cfb791174314eba2c0d178551b9/src/shared/WETH9.sol)
## Variables
### allowance
```solidity
mapping(address => mapping(address => uint256)) public allowance
```
### balanceOf
```solidity
mapping(address => uint256) public balanceOf
```
### decimals
```solidity
uint8 public decimals
```
### name
```solidity
string public name
```
### symbol
```solidity
string public symbol
```
## Events
### Approval
```solidity
event Approval(address indexed src, address indexed guy, uint256 wad)
```
Emitted when an approval is set.
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ----------------------------- |
| src | address | The owner of the tokens |
| guy | address | The approved spender |
| wad | uint256 | The amount of tokens approved |
### Deposit
```solidity
event Deposit(address indexed dst, uint256 wad)
```
Emitted when ETH is wrapped to WETH.
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------- |
| dst | address | The recipient of the WETH |
| wad | uint256 | The amount of ETH wrapped |
### Transfer
```solidity
event Transfer(address indexed src, address indexed dst, uint256 wad)
```
Emitted when tokens are transferred.
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | -------------------------------- |
| src | address | The sender of the tokens |
| dst | address | The recipient of the tokens |
| wad | uint256 | The amount of tokens transferred |
### Withdrawal
```solidity
event Withdrawal(address indexed src, uint256 wad)
```
Emitted when WETH is unwrapped back to ETH.
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ---------------------------- |
| src | address | The address unwrapping WETH |
| wad | uint256 | The amount of WETH unwrapped |
## Functions
### approve
Approves a spender to transfer tokens on behalf of the owner.
```solidity
function approve(address guy, uint256 wad) public returns (bool)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ------------------------------- |
| guy | address | The address to approve |
| wad | uint256 | The amount of tokens to approve |
#### Returns
| Parameter | Type | Description |
| --------- | ---- | ------------------- |
| (unnamed) | bool | Always returns true |
### deposit
Wraps ETH to WETH by sending ETH to the contract.
```solidity
function deposit() external payable
```
### totalSupply
Returns the total amount of WETH in circulation.
```solidity
function totalSupply() public view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ------------------------------------------------- |
| (unnamed) | uint256 | The total supply of WETH (contract's ETH balance) |
### transfer
Transfers tokens to a specified address.
```solidity
function transfer(address dst, uint256 wad) public returns (bool)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | -------------------------------- |
| dst | address | The recipient address |
| wad | uint256 | The amount of tokens to transfer |
#### Returns
| Parameter | Type | Description |
| --------- | ---- | ------------------- |
| (unnamed) | bool | Always returns true |
### transferFrom
Transfers tokens from one address to another.
```solidity
function transferFrom(address src, address dst, uint256 wad) public returns (bool)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | -------------------------------- |
| src | address | The source address |
| dst | address | The destination address |
| wad | uint256 | The amount of tokens to transfer |
#### Returns
| Parameter | Type | Description |
| --------- | ---- | ------------------- |
| (unnamed) | bool | Always returns true |
### withdraw
Unwraps WETH back to ETH.
```solidity
function withdraw(uint256 wad) external
```
#### Parameters
| Parameter | Type | Description |
| --------- | ------- | ---------------------------- |
| wad | uint256 | The amount of WETH to unwrap |
---
# AggregatorInterface v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/aggregator-interface
## AggregatorInterface
Interface for accessing price feed data from an aggregator contract.
[`AggregatorInterface`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/data-feeds/interfaces/AggregatorInterface.sol)
## Events
### AnswerUpdated
```solidity
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | --------------------------------------------- |
| current | `int256` | The updated answer |
| roundId | `uint256` | The round ID for which the answer was updated |
| updatedAt | `uint256` | The timestamp when the answer was updated |
### NewRound
```solidity
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------------------- |
| roundId | `uint256` | The round ID of the new round |
| startedBy | `address` | The address of the account that started the round |
| startedAt | `uint256` | The timestamp when the round was started |
## Functions
### getAnswer
Gets the answer for a specific round ID.
```solidity
function getAnswer(uint256 roundId) external view returns (int256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------- |
| roundId | `uint256` | The round ID to get the answer for |
#### Returns
| Parameter | Type | Description |
| --------- | -------- | --------------------------------- |
| (unnamed) | `int256` | The answer for the given round ID |
### getTimestamp
Gets the timestamp for a specific round ID.
```solidity
function getTimestamp(uint256 roundId) external view returns (uint256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------- |
| roundId | `uint256` | The round ID to get the timestamp for |
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------ |
| (unnamed) | `uint256` | The timestamp for the given round ID |
### latestAnswer
Gets the latest answer from the aggregator.
```solidity
function latestAnswer() external view returns (int256)
```
#### Returns
| Parameter | Type | Description |
| --------- | -------- | ----------------- |
| (unnamed) | `int256` | The latest answer |
### latestRound
Gets the latest round ID from the aggregator.
```solidity
function latestRound() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ------------------- |
| (unnamed) | `uint256` | The latest round ID |
### latestTimestamp
Gets the timestamp of the latest answer from the aggregator.
```solidity
function latestTimestamp() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------- |
| (unnamed) | `uint256` | The timestamp of the latest answer |
---
# AggregatorV2V3Interface v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/aggregator-v2-v3-interface
## AggregatorV2V3Interface
Interface that combines functionality from both AggregatorInterface and AggregatorV3Interface.
[`AggregatorV2V3Interface`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/data-feeds/interfaces/AggregatorV2V3Interface.sol)
---
# AggregatorV3Interface v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/aggregator-v3-interface
## AggregatorV3Interface
Interface for accessing detailed data from an aggregator contract, including round data and metadata.
[`AggregatorV3Interface`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/data-feeds/interfaces/AggregatorV3Interface.sol)
## Functions
### decimals
Gets the number of decimals used by the aggregator.
```solidity
function decimals() external view returns (uint8)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ---------------------- |
| (unnamed) | `uint8` | The number of decimals |
### description
Gets the description of the aggregator.
```solidity
function description() external view returns (string memory)
```
#### Returns
| Parameter | Type | Description |
| --------- | -------- | --------------------------------- |
| (unnamed) | `string` | The description of the aggregator |
### getRoundData
Gets the round data for a specific round ID.
```solidity
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | -------------------------------- |
| _roundId | `uint80` | The round ID to get the data for |
#### Returns
| Parameter | Type | Description |
| --------------- | --------- | --------------------------------------------- |
| roundId | `uint80` | The round ID |
| answer | `int256` | The answer for the round |
| startedAt | `uint256` | The timestamp when the round started |
| updatedAt | `uint256` | The timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### latestRoundData
Gets the latest round data.
```solidity
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
#### Returns
| Parameter | Type | Description |
| --------------- | --------- | ---------------------------------------------------- |
| roundId | `uint80` | The latest round ID |
| answer | `int256` | The latest answer |
| startedAt | `uint256` | The timestamp when the latest round started |
| updatedAt | `uint256` | The timestamp when the latest round was updated |
| answeredInRound | `uint80` | The round ID in which the latest answer was computed |
### version
Gets the version of the aggregator.
```solidity
function version() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ----------------------------- |
| (unnamed) | `uint256` | The version of the aggregator |
---
# BurnMintERC677Helper v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/burn-mint-erc677-helper
## BurnMintERC677Helper
A helper contract that extends the BurnMintERC677 token contract to provide additional minting functionality.
[`BurnMintERC677Helper`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/ccip/BurnMintERC677Helper.sol)
## Functions
### constructor
Initializes the token with a name and symbol, setting fixed decimals and initial supply.
```solidity
constructor(string memory name, string memory symbol)
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | ----------------------- |
| name | `string` | The name of the token |
| symbol | `string` | The symbol of the token |
### drip
Mints exactly one token (1e18 units) to a specified address.
```solidity
function drip(address to) external
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | --------------------------------------- |
| to | `address` | The address to receive the minted token |
---
# CCIPLocalSimulatorFork v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork-js
## CCIPLocalSimulatorFork
A JavaScript module that provides utilities for simulating CCIP (Cross-Chain Interoperability Protocol) message routing in a local or forked environment.
[`CCIPLocalSimulatorFork`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/scripts/CCIPLocalSimulatorFork.js)
## Types
### Evm2EvmMessage
Represents a cross-chain message in the EVM-to-EVM communication protocol. Contains all necessary information for message routing and token transfers across chains.
| Property | Type | Description |
| ------------------- | ---------------------------------------- | ---------------------------------------------- |
| sourceChainSelector | `bigint` | The identifier of the source chain |
| sender | `string` | The address that sent the message |
| receiver | `string` | The address that will receive the message |
| sequenceNumber | `bigint` | The sequence number of the message |
| gasLimit | `bigint` | The gas limit for executing the message |
| strict | `boolean` | Whether the message requires strict execution |
| nonce | `bigint` | The nonce of the message |
| feeToken | `string` | The token used to pay fees |
| feeTokenAmount | `bigint` | The amount of fee token to be paid |
| data | `string` | The message payload data |
| tokenAmounts | `Array<{token: string, amount: bigint}>` | Array of tokens and amounts being transferred |
| sourceTokenData | `Array` | Array of token-specific data from source chain |
| messageId | `string` | The unique identifier of the message |
## Functions
### getEvm2EvmMessage
Extracts and parses a CCIP message from a transaction receipt by looking for the CCIPSendRequested event.
```javascript
function getEvm2EvmMessage(receipt) => Evm2EvmMessage | null
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------ |
| receipt | `object` | The transaction receipt from the `ccipSend` call |
#### Returns
| Type | Description |
| ---------------------- | ---------------------------------------------------------------------------------------------- |
| `Evm2EvmMessage\|null` | The parsed EVM-to-EVM message if found in the receipt logs, or null if no relevant event found |
### requestLinkFromTheFaucet
Requests LINK tokens from a faucet contract for testing purposes.
```javascript
async function requestLinkFromTheFaucet(linkAddress, to, amount) => Promise
```
#### Parameters
| Parameter | Type | Description |
| ----------- | -------- | ------------------------------------------------------- |
| linkAddress | `string` | The address of the LINK contract on the current network |
| to | `string` | The address to send LINK to |
| amount | `bigint` | The amount of LINK to request |
#### Returns
| Type | Description |
| ----------------- | -------------------------------------------------------------- |
| `Promise` | Promise resolving to the transaction hash of the fund transfer |
### routeMessage
Routes a cross-chain message on the destination network by finding the appropriate off-ramp and executing the message.
```javascript
async function routeMessage(routerAddress, evm2EvmMessage) => Promise
```
#### Parameters
| Parameter | Type | Description |
| -------------- | ---------------- | --------------------------------- |
| routerAddress | `string` | Address of the destination Router |
| evm2EvmMessage | `Evm2EvmMessage` | Sent cross-chain message |
#### Returns
| Type | Description |
| --------------- | ------------------------------------------------------------ |
| `Promise` | Resolves with no value if the message is successfully routed |
---
# CCIPLocalSimulatorFork v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork
## CCIPLocalSimulatorFork
A contract that simulates CCIP (Cross-Chain Interoperability Protocol) message routing in a Foundry test environment.
[`CCIPLocalSimulatorFork`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/ccip/CCIPLocalSimulatorFork.sol)
## Interfaces
### IRouterFork
Interface for interacting with the CCIP Router contract in a forked environment.
#### OffRamp
```solidity
struct OffRamp {
uint64 sourceChainSelector;
address offRamp;
}
```
| Field | Type | Description |
| ------------------- | --------- | --------------------------------------- |
| sourceChainSelector | `uint64` | The chain selector for the source chain |
| offRamp | `address` | The address of the offRamp contract |
#### getOffRamps
```solidity
function getOffRamps() external view returns (OffRamp[] memory)
```
#### Returns
| Parameter | Type | Description |
| --------- | ----------- | -------------------------------- |
| (unnamed) | `OffRamp[]` | Array of off-ramp configurations |
### IEVM2EVMOffRampFork
Interface for executing CCIP messages on an off-ramp contract in a forked environment.
#### executeSingleMessage
```solidity
function executeSingleMessage(
Internal.EVM2EVMMessage memory message,
bytes[] memory offchainTokenData,
uint32[] memory tokenGasOverrides
) external
```
#### Parameters
| Parameter | Type | Description |
| ----------------- | ---------------- | --------------------------------------- |
| message | `EVM2EVMMessage` | The CCIP message to be executed |
| offchainTokenData | `bytes[]` | Additional off-chain token data |
| tokenGasOverrides | `uint32[]` | Gas limit overrides for token transfers |
## Events
### CCIPSendRequested
```solidity
event CCIPSendRequested(Internal.EVM2EVMMessage message)
```
#### Parameters
| Parameter | Type | Description |
| --------- | ---------------- | --------------------------------- |
| message | `EVM2EVMMessage` | The EVM2EVM message that was sent |
## Variables
### i_register
```solidity
Register immutable i_register
```
### LINK_FAUCET
```solidity
address constant LINK_FAUCET = 0x4281eCF07378Ee595C564a59048801330f3084eE
```
### s_processedMessages
```solidity
mapping(bytes32 messageId => bool isProcessed) internal s_processedMessages
```
## Functions
### constructor
Initializes the contract and sets up logging and persistence.
```solidity
constructor()
```
### switchChainAndRouteMessage
Routes a cross-chain message on the destination network after switching to the specified fork.
```solidity
function switchChainAndRouteMessage(uint256 forkId) external
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------------------------------------------------------------- |
| forkId | `uint256` | The ID of the destination network fork (returned by `createFork()` or `createSelectFork()`) |
### getNetworkDetails
Returns the network configuration details for a specified chain ID.
```solidity
function getNetworkDetails(uint256 chainId) external view returns (Register.NetworkDetails memory)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | --------------------------------------------------------------------- |
| chainId | `uint256` | The blockchain network chain ID (e.g., 11155111 for Ethereum Sepolia) |
#### Returns
| Parameter | Type | Description |
| --------- | ---------------- | --------------------------------------------------------- |
| (unnamed) | `NetworkDetails` | The network configuration details for the specified chain |
### setNetworkDetails
Updates or adds new network configuration details for a specified chain ID.
```solidity
function setNetworkDetails(uint256 chainId, Register.NetworkDetails memory networkDetails) external
```
#### Parameters
| Parameter | Type | Description |
| -------------- | ---------------- | --------------------------------------------------------------------- |
| chainId | `uint256` | The blockchain network chain ID (e.g., 11155111 for Ethereum Sepolia) |
| networkDetails | `NetworkDetails` | The network configuration details to be stored |
### requestLinkFromFaucet
Requests LINK tokens from the faucet for a specified address.
```solidity
function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | -------------------------------------- |
| to | `address` | The address to receive the LINK tokens |
| amount | `uint256` | The amount of LINK tokens to transfer |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------- |
| success | `bool` | Returns true if the token transfer was successful |
---
# CCIPLocalSimulator v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/ccip-local-simulator
## CCIPLocalSimulator
A contract that simulates local CCIP (Cross-Chain Interoperability Protocol) operations for testing and development purposes.
[`CCIPLocalSimulator`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/ccip/CCIPLocalSimulator.sol)
## Variables
### CHAIN_SELECTOR
```solidity
uint64 constant CHAIN_SELECTOR = 16015286601757825753
```
### i_ccipBnM
```solidity
BurnMintERC677Helper internal immutable i_ccipBnM
```
### i_ccipLnM
```solidity
BurnMintERC677Helper internal immutable i_ccipLnM
```
### i_linkToken
```solidity
LinkToken internal immutable i_linkToken
```
### i_mockRouter
```solidity
MockCCIPRouter internal immutable i_mockRouter
```
### i_wrappedNative
```solidity
WETH9 internal immutable i_wrappedNative
```
### s_supportedTokens
```solidity
address[] internal s_supportedTokens
```
## Errors
### CCIPLocalSimulator__MsgSenderIsNotTokenOwner
```solidity
error CCIPLocalSimulator__MsgSenderIsNotTokenOwner()
```
### CCIPLocalSimulator__RequiredRoleNotFound
```solidity
error CCIPLocalSimulator__RequiredRoleNotFound(address account, bytes32 role, address token)
```
## Functions
### configuration
Returns the configuration details for pre-deployed contracts and services needed for local CCIP simulations.
```solidity
function configuration() public view returns (uint64 chainSelector_, IRouterClient sourceRouter_, IRouterClient destinationRouter_, WETH9 wrappedNative_, LinkToken linkToken_, BurnMintERC677Helper ccipBnM_, BurnMintERC677Helper ccipLnM_)
```
#### Returns
| Parameter | Type | Description |
| ------------------- | ---------------------- | -------------------------------------------------------- |
| chainSelector_ | `uint64` | The unique CCIP Chain Selector |
| sourceRouter_ | `IRouterClient` | The source chain Router contract |
| destinationRouter_ | `IRouterClient` | The destination chain Router contract |
| wrappedNative_ | `WETH9` | The wrapped native token which can be used for CCIP fees |
| linkToken_ | `LinkToken` | The LINK token |
| ccipBnM_ | `BurnMintERC677Helper` | The ccipBnM token |
| ccipLnM_ | `BurnMintERC677Helper` | The ccipLnM token |
### constructor
Initializes the contract with pre-deployed token instances.
```solidity
constructor()
```
### getSupportedTokens
Gets the list of supported token addresses for a given chain selector.
```solidity
function getSupportedTokens(uint64 chainSelector) external view returns (address[] memory tokens)
```
#### Parameters
| Parameter | Type | Description |
| ------------- | -------- | ------------------------------ |
| chainSelector | `uint64` | The unique CCIP Chain Selector |
#### Returns
| Parameter | Type | Description |
| --------- | ----------- | ------------------------------------------------------------------------------ |
| tokens | `address[]` | Returns a list of token addresses that are supported for cross-chain transfers |
### isChainSupported
Checks if a given chain selector is supported.
```solidity
function isChainSupported(uint64 chainSelector) public pure returns (bool supported)
```
#### Parameters
| Parameter | Type | Description |
| ------------- | -------- | ------------------------------ |
| chainSelector | `uint64` | The unique CCIP Chain Selector |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------------------- |
| supported | `bool` | Returns true if `chainSelector` is supported by the simulator |
### requestLinkFromFaucet
Transfers LINK tokens from the faucet to a specified address.
```solidity
function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ----------------------------------------------- |
| to | `address` | The address to which LINK tokens are to be sent |
| amount | `uint256` | The amount of LINK tokens to send |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------------------------- |
| success | `bool` | Returns `true` if the transfer of tokens was successful |
### supportNewTokenViaAccessControlDefaultAdmin
Adds a new token to supported tokens list via AccessControl's DEFAULT_ADMIN_ROLE.
```solidity
function supportNewTokenViaAccessControlDefaultAdmin(address tokenAddress) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | --------- | --------------------------------------------------------------- |
| tokenAddress | `address` | The address of the token to add to the list of supported tokens |
### supportNewTokenViaGetCCIPAdmin
Adds a new token to supported tokens list via CCIP admin role.
```solidity
function supportNewTokenViaGetCCIPAdmin(address tokenAddress) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | --------- | --------------------------------------------------------------- |
| tokenAddress | `address` | The address of the token to add to the list of supported tokens |
### supportNewTokenViaOwner
Adds a new token to supported tokens list via token owner.
```solidity
function supportNewTokenViaOwner(address tokenAddress) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | --------- | --------------------------------------------------------------- |
| tokenAddress | `address` | The address of the token to add to the list of supported tokens |
---
# Chainlink Local v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3
## CCIP
These contracts provide Cross-Chain Interoperability Protocol (CCIP) functionality in a local testing environment:
- [CCIPLocalSimulator](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator) - Local CCIP message routing simulator
- [CCIPLocalSimulatorFork](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork) - CCIP simulator for forked networks
- [CCIPLocalSimulatorFork JS](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork-js) - JavaScript utilities for CCIP simulation
- [Register](/chainlink-local/api-reference/v0.2.3/register) - CCIP network configuration registry
## Data Feeds
Contracts for simulating Chainlink Data Feeds:
- [AggregatorInterface](/chainlink-local/api-reference/v0.2.3/aggregator-interface) - Basic price feed interface
- [AggregatorV2V3Interface](/chainlink-local/api-reference/v0.2.3/aggregator-v2-v3-interface) - Combined V2/V3 price feed interface
- [AggregatorV3Interface](/chainlink-local/api-reference/v0.2.3/aggregator-v3-interface) - Extended price feed interface
- [MockOffchainAggregator](/chainlink-local/api-reference/v0.2.3/mock-offchain-aggregator) - Mock implementation of off-chain aggregator
- [MockV3Aggregator](/chainlink-local/api-reference/v0.2.3/mock-v3-aggregator) - Mock implementation of V3 aggregator
## Token Contracts
Standard token implementations for testing:
- [BurnMintERC677Helper](/chainlink-local/api-reference/v0.2.3/burn-mint-erc677-helper) - Helper contract for ERC677 token operations
- [LinkToken](/chainlink-local/api-reference/v0.2.3/link-token) - LINK token implementation
- [WETH9](/chainlink-local/api-reference/v0.2.3/weth9) - Wrapped Ether implementation
---
# LinkToken v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/link-token
## LinkToken
A contract that implements the ChainLink Token (LINK) using the ERC677 standard.
[`LinkToken`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/shared/LinkToken.sol)
## Functions
### constructor
Initializes the contract with fixed token details.
```solidity
constructor()
```
### _onCreate
Internal hook called during contract creation.
```solidity
function _onCreate() internal virtual
```
---
# MockOffchainAggregator v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/mock-offchain-aggregator
## MockOffchainAggregator
A mock implementation of an offchain aggregator for testing purposes.
[`MockOffchainAggregator`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/data-feeds/MockOffchainAggregator.sol)
## Variables
### decimals
```solidity
uint8 public decimals
```
### getAnswer
```solidity
mapping(uint256 => int256) public getAnswer
```
### getTimestamp
```solidity
mapping(uint256 => uint256) public getTimestamp
```
### latestAnswer
```solidity
int256 public latestAnswer
```
### latestRound
```solidity
uint256 public latestRound
```
### latestTimestamp
```solidity
uint256 public latestTimestamp
```
### maxAnswer
```solidity
int192 public maxAnswer
```
### minAnswer
```solidity
int192 public minAnswer
```
## Functions
### constructor
Initializes the contract with decimals and initial answer.
```solidity
constructor(uint8 _decimals, int256 _initialAnswer)
```
#### Parameters
| Parameter | Type | Description |
| --------------- | -------- | ----------------------------------------- |
| _decimals | `uint8` | The number of decimals for the aggregator |
| _initialAnswer | `int256` | The initial answer to be set |
### getRoundData
Gets the round data for a specific round ID.
```solidity
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | -------------------------------- |
| _roundId | `uint80` | The round ID to get the data for |
#### Returns
| Parameter | Type | Description |
| --------------- | --------- | --------------------------------------------- |
| roundId | `uint80` | The round ID |
| answer | `int256` | The answer for the round |
| startedAt | `uint256` | The timestamp when the round started |
| updatedAt | `uint256` | The timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### latestRoundData
Gets the latest round data.
```solidity
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
#### Returns
| Parameter | Type | Description |
| --------------- | --------- | ---------------------------------------------------- |
| roundId | `uint80` | The latest round ID |
| answer | `int256` | The latest answer |
| startedAt | `uint256` | The timestamp when the latest round started |
| updatedAt | `uint256` | The timestamp when the latest round was updated |
| answeredInRound | `uint80` | The round ID in which the latest answer was computed |
### updateAnswer
Updates the answer in the mock aggregator.
```solidity
function updateAnswer(int256 _answer) public
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | ------------------------ |
| _answer | `int256` | The new answer to be set |
### updateMinAndMaxAnswers
Updates the minimum and maximum answers the aggregator can report.
```solidity
function updateMinAndMaxAnswers(int192 _minAnswer, int192 _maxAnswer) external
```
#### Parameters
| Parameter | Type | Description |
| ----------- | -------- | ---------------------- |
| _minAnswer | `int192` | The new minimum answer |
| _maxAnswer | `int192` | The new maximum answer |
#### Possible Reverts
- Reverts if minAnswer is not less than maxAnswer with "minAnswer must be less than maxAnswer"
- Reverts if minAnswer is too low with "minAnswer is too low"
- Reverts if maxAnswer is too high with "maxAnswer is too high"
### updateRoundData
Updates the round data in the mock aggregator.
```solidity
function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public
```
#### Parameters
| Parameter | Type | Description |
| ----------- | --------- | ------------------------------------ |
| _roundId | `uint80` | The round ID to be updated |
| _answer | `int256` | The new answer to be set |
| _timestamp | `uint256` | The timestamp to be set |
| _startedAt | `uint256` | The timestamp when the round started |
---
# MockV3Aggregator v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/mock-v3-aggregator
## MockV3Aggregator
A mock implementation of the AggregatorV2V3Interface for testing purposes.
[`MockV3Aggregator`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/data-feeds/MockV3Aggregator.sol)
## Variables
### aggregator
```solidity
address public aggregator
```
### proposedAggregator
```solidity
address public proposedAggregator
```
### version
```solidity
uint256 public constant override version = 0
```
## Functions
### confirmAggregator
Confirms the proposed aggregator.
```solidity
function confirmAggregator(address _aggregator) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | --------- | -------------------------------------- |
| _aggregator | `address` | The address of the proposed aggregator |
#### Possible Reverts
- Reverts if the provided aggregator address does not match the proposed aggregator with "Invalid proposed aggregator"
### constructor
Initializes the contract with decimals and initial answer.
```solidity
constructor(uint8 _decimals, int256 _initialAnswer)
```
#### Parameters
| Parameter | Type | Description |
| --------------- | -------- | ----------------------------------------- |
| _decimals | `uint8` | The number of decimals for the aggregator |
| _initialAnswer | `int256` | The initial answer to be set |
### decimals
Gets the number of decimals used by the aggregator.
```solidity
function decimals() external view returns (uint8)
```
#### Returns
| Parameter | Type | Description |
| --------- | ------- | ---------------------- |
| (unnamed) | `uint8` | The number of decimals |
### description
Gets the description of the aggregator.
```solidity
function description() external pure returns (string memory)
```
#### Returns
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------ |
| (unnamed) | `string` | The contract path as the description |
### getAnswer
Gets the answer for a specific round ID.
```solidity
function getAnswer(uint256 roundId) external view returns (int256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ------------------------------ |
| roundId | `uint256` | The round ID to get answer for |
#### Returns
| Parameter | Type | Description |
| --------- | -------- | --------------------------------- |
| (unnamed) | `int256` | The answer for the given round ID |
### getRoundData
Gets the round data for a specific round ID.
```solidity
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | -------------------------------- |
| _roundId | `uint80` | The round ID to get the data for |
#### Returns
| Parameter | Type | Description |
| --------------- | --------- | --------------------------------------------- |
| roundId | `uint80` | The round ID |
| answer | `int256` | The answer for the round |
| startedAt | `uint256` | The timestamp when the round started |
| updatedAt | `uint256` | The timestamp when the round was updated |
| answeredInRound | `uint80` | The round ID in which the answer was computed |
### getTimestamp
Gets the timestamp for a specific round ID.
```solidity
function getTimestamp(uint256 roundId) external view returns (uint256)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | --------------------------------- |
| roundId | `uint256` | The round ID to get timestamp for |
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ------------------------------------ |
| (unnamed) | `uint256` | The timestamp for the given round ID |
### latestAnswer
Gets the latest answer from the aggregator.
```solidity
function latestAnswer() external view returns (int256)
```
#### Returns
| Parameter | Type | Description |
| --------- | -------- | ----------------- |
| (unnamed) | `int256` | The latest answer |
### latestRound
Gets the latest round ID from the aggregator.
```solidity
function latestRound() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ------------------- |
| (unnamed) | `uint256` | The latest round ID |
### latestRoundData
Gets the latest round data.
```solidity
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
```
#### Returns
| Parameter | Type | Description |
| --------------- | --------- | ---------------------------------------------------- |
| roundId | `uint80` | The latest round ID |
| answer | `int256` | The latest answer |
| startedAt | `uint256` | The timestamp when the latest round started |
| updatedAt | `uint256` | The timestamp when the latest round was updated |
| answeredInRound | `uint80` | The round ID in which the latest answer was computed |
### latestTimestamp
Gets the timestamp of the latest answer.
```solidity
function latestTimestamp() external view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------- |
| (unnamed) | `uint256` | The timestamp of the latest answer |
### proposeAggregator
Proposes a new aggregator.
```solidity
function proposeAggregator(AggregatorV2V3Interface _aggregator) external
```
#### Parameters
| Parameter | Type | Description |
| ------------ | ------------------------- | -------------------------------------- |
| _aggregator | `AggregatorV2V3Interface` | The address of the proposed aggregator |
#### Possible Reverts
- Reverts if the proposed aggregator is the zero address with "Proposed aggregator cannot be zero address"
- Reverts if the proposed aggregator is the current aggregator with "Proposed aggregator cannot be current aggregator"
### updateAnswer
Updates the answer in the mock aggregator.
```solidity
function updateAnswer(int256 _answer) public
```
#### Parameters
| Parameter | Type | Description |
| --------- | -------- | ------------------------ |
| _answer | `int256` | The new answer to be set |
### updateRoundData
Updates the round data in the mock aggregator.
```solidity
function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public
```
#### Parameters
| Parameter | Type | Description |
| ----------- | --------- | ------------------------------------ |
| _roundId | `uint80` | The round ID to be updated |
| _answer | `int256` | The new answer to be set |
| _timestamp | `uint256` | The timestamp to be set |
| _startedAt | `uint256` | The timestamp when the round started |
---
# Register v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/register
## Register
A contract that stores and manages network configuration details for various blockchain networks supported by CCIP (Cross-Chain Interoperability Protocol).
[`Register`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/ccip/Register.sol)
## Structs
### NetworkDetails
Contains configuration details for a specific network/chain.
| Field | Type | Description |
| -------------------------------- | --------- | -------------------------------------------- |
| chainSelector | `uint64` | Unique identifier for the chain in CCIP |
| routerAddress | `address` | Address of the CCIP Router contract |
| linkAddress | `address` | Address of the LINK token contract |
| wrappedNativeAddress | `address` | Address of the wrapped native token contract |
| ccipBnMAddress | `address` | Address of the CCIP BnM token contract |
| ccipLnMAddress | `address` | Address of the CCIP LnM token contract |
| rmnProxyAddress | `address` | Address of the RMN proxy contract |
| registryModuleOwnerCustomAddress | `address` | Address of the registry module owner |
| tokenAdminRegistryAddress | `address` | Address of the token admin registry contract |
## Variables
### s_networkDetails
```solidity
mapping(uint256 chainId => NetworkDetails) internal s_networkDetails
```
## Functions
### constructor
Initializes the contract with predefined network details for various supported chains.
```solidity
constructor()
```
### getNetworkDetails
Retrieves the network details for a specified chain ID.
```solidity
function getNetworkDetails(uint256 chainId) external view returns (NetworkDetails memory networkDetails)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | -------------------------------------- |
| chainId | `uint256` | The ID of the chain to get details for |
#### Returns
| Type | Description |
| ---------------- | ----------------------------------------------- |
| `NetworkDetails` | The network configuration details for the chain |
### setNetworkDetails
Sets or updates the network details for a specified chain ID.
```solidity
function setNetworkDetails(uint256 chainId, NetworkDetails memory networkDetails) external
```
#### Parameters
| Parameter | Type | Description |
| -------------- | ---------------- | ---------------------------------------------- |
| chainId | `uint256` | The ID of the chain to set details for |
| networkDetails | `NetworkDetails` | The network configuration details to be stored |
---
# WETH9 v0.2.3 API Reference
Source: https://docs.chain.link/chainlink-local/api-reference/v0.2.3/weth9
## WETH9
A contract that implements Wrapped Ether (WETH), allowing users to wrap and unwrap ETH.
[`WETH9`](https://github.com/smartcontractkit/chainlink-local/blob/7d8b2f888e1f10c8841ccd9e0f4af0f5baf11dab/src/shared/WETH9.sol)
## Variables
### allowance
```solidity
mapping(address => mapping(address => uint256)) public allowance
```
### balanceOf
```solidity
mapping(address => uint256) public balanceOf
```
### decimals
```solidity
uint8 public decimals = 18
```
### name
```solidity
string public name = "Wrapped Ether"
```
### symbol
```solidity
string public symbol = "WETH"
```
## Events
### Approval
```solidity
event Approval(address indexed src, address indexed guy, uint256 wad)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ----------------------------------- |
| src | `address` | The address giving the approval |
| guy | `address` | The address receiving the approval |
| wad | `uint256` | The amount of tokens being approved |
### Deposit
```solidity
event Deposit(address indexed dst, uint256 wad)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ------------------------------- |
| dst | `address` | The address receiving the WETH |
| wad | `uint256` | The amount of ETH being wrapped |
### Transfer
```solidity
event Transfer(address indexed src, address indexed dst, uint256 wad)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | -------------------------------------- |
| src | `address` | The address sending the tokens |
| dst | `address` | The address receiving the tokens |
| wad | `uint256` | The amount of tokens being transferred |
### Withdrawal
```solidity
event Withdrawal(address indexed src, uint256 wad)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------- |
| src | `address` | The address unwrapping the WETH |
| wad | `uint256` | The amount of WETH being unwrapped |
## Functions
### approve
Approves another address to spend tokens.
```solidity
function approve(address guy, uint256 wad) public returns (bool)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ------------------------------- |
| guy | `address` | The address to approve |
| wad | `uint256` | The amount of tokens to approve |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------- |
| (unnamed) | `bool` | Always returns true |
### deposit
Deposits ETH to receive WETH.
```solidity
function deposit() external payable
```
### totalSupply
Gets the total supply of WETH.
```solidity
function totalSupply() public view returns (uint256)
```
#### Returns
| Parameter | Type | Description |
| --------- | --------- | ------------------------ |
| (unnamed) | `uint256` | The total supply of WETH |
### transfer
Transfers tokens to another address.
```solidity
function transfer(address dst, uint256 wad) public returns (bool)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | -------------------------------- |
| dst | `address` | The recipient address |
| wad | `uint256` | The amount of tokens to transfer |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------- |
| (unnamed) | `bool` | Returns true if the transfer succeeds |
### transferFrom
Transfers tokens from one address to another.
```solidity
function transferFrom(address src, address dst, uint256 wad) public returns (bool)
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | -------------------------------- |
| src | `address` | The source address |
| dst | `address` | The destination address |
| wad | `uint256` | The amount of tokens to transfer |
#### Returns
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------- |
| (unnamed) | `bool` | Returns true if the transfer succeeds |
#### Possible Reverts
- Reverts if the source address has insufficient balance
- Reverts if the caller has insufficient allowance (unless caller is source or has maximum allowance)
### withdraw
Withdraws ETH by unwrapping WETH.
```solidity
function withdraw(uint256 wad) external
```
#### Parameters
| Parameter | Type | Description |
| --------- | --------- | ---------------------------- |
| wad | `uint256` | The amount of WETH to unwrap |
#### Possible Reverts
- Reverts if the caller has insufficient WETH balance
---
# CCT - getCCIPAdmin() token with Burn and Mint Pool in forked environments
Source: https://docs.chain.link/chainlink-local/build/ccip/foundry/cct-burn-and-mint-fork
This tutorial will guide you through the process of testing the procedure of enabling your own tokens in CCIP. We will use the CCT-compatible ERC-20 token with `getCCIPAdmin` function and burning & minting capabilities. We will use Burn & Mint Pool for transferring this token across different blockchains using Chainlink CCIP.
## Prerequisites
Before we start with this guide, let's recap parts of the CCT standard that we will need for it.
### Requirements for Cross-Chain Tokens
Before enabling an ERC20-compatible token in CCIP, it's important to understand the requirements it must fulfill to integrate with CCIP.
- **Recommended Permissionless Token Administrator address registration methods**: A token can utilize either of these supported function signatures to register permissionlessly:
- `owner()`: This function returns the token contract owner's address.
- `getCCIPAdmin()`: This function returns the token administrator's address and is recommended for new tokens, as it allows for abstraction of the CCIP Token Administrator role from other common roles, like `owner()`.
- **Requirements for CCIP token transfers**: The token's smart contract must meet minimum requirements to integrate with CCIP.
- **Burn & Mint Requirements**:
- The token smart contract must have the following functions:
- `mint(address account, uint256 amount)`: This function is used to mint the `amount` of tokens to a given `account` on the destination blockchain.
- `burn(uint256 amount)`: This function is used to burn the `amount` of tokens on the source blockchain.
- `decimals()`: Returns the token's number of decimals.
- `balanceOf(address account)`: Returns the current token balance of the specified `account`.
- `burnFrom(address account, uint256 amount)`: This function burns a specified number of tokens from the provided account on the source blockchain. **Note**: This is an optional function. We generally recommend using the `burn` function, but if you use a tokenPool that calls `burnFrom`, your token contract will need to implement this function.
- On the source and destination blockchains, the token contract must support granting mint and burn permissions. The token developers or another role (such as the token administrator) will grant these permissions to the token pool.
- **Lock & Mint Requirements**:
- The token smart contract must have the following functions:
- `decimals()`: Returns the token's number of decimals.
- `balanceOf(address account)`: Returns the current token balance of the specified `account`.
- On the destination blockchain, The token contract must support granting mint and burn permissions. The token developers or another role (such as the token administrator) will grant these permissions to the token pool.
If you don't have an existing token: For all blockchains where tokens need to be burned and minted (for example, the source or destination chain in the case of Burn and Mint, or the destination blockchain in the case of Lock and Mint), Chainlink provides a [BurnMintERC677](https://github.com/smartcontractkit/ccip/tree/release/contracts-ccip-1.5.1/contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol) contract that you can use to deploy your token in minutes. This token follows the [ERC677](https://github.com/ethereum/EIPs/issues/677) or [ERC777](https://ethereum.org/en/developers/docs/standards/tokens/erc-777/), allowing you to use it as-is or extend it to meet your specific requirements.
### Understanding the Procedure
It is also important first to understand the overall procedure for enabling your tokens in CCIP. This procedure involves deploying tokens and token pools, registering administrative roles, and configuring token pools to enable secure token transfers using CCIP. The steps in the diagram below highlight the flow of actions needed to enable a token for cross-chain transfers. Whether you're working with an Externally Owned Account (EOA) or a **Smart Account** (such as one using a multisig scheme), the overall logic remains the same. You'll follow the same process to enable cross-chain token transfers, configure pools, and register administrative roles.
The diagram below outlines the entire process:
## Before You Begin
1. **Install Foundry**:
If you haven't already, follow the instructions in the [Foundry documentation](https://book.getfoundry.sh/getting-started/installation) to install Foundry.
2. **Create new Foundry project**: Create a new Foundry project by running the following command:
```bash
forge init
```
3. **Set up your environment**: Create a `.env` file, and fill in the required values:
Example `.env` file:
```bash
ETHEREUM_SEPOLIA_RPC_URL=
BASE_SEPOLIA_RPC_URL=
```
## Create the `getCCIPAdmin()` ERC-20 token
Inside the `test` folder create the new Solidity file and name it `CCIPv1_5ForkBurnMintPoolFork.t.sol`. We will use only this file through out the rest of this guide.
Create the CCT-compatible ERC-20 token with `getCCIPAdmin()` function and burning and minting capabilities.
```solidity
// test/CCIPv1_5ForkBurnMintPoolFork.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ERC20, ERC20Burnable, IERC20 } 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 MockERC20BurnAndMintToken is IBurnMintERC20, ERC20Burnable, AccessControl {
address internal immutable i_CCIPAdmin;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() ERC20("MockERC20BurnAndMintToken", "MTK") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
_grantRole(BURNER_ROLE, msg.sender);
i_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() public view returns (address) {
return i_CCIPAdmin;
}
}
```
## Test the CCT enabling procedure
Expand the existing `CCIPv1_5ForkBurnMintPoolFork.t.sol` file to create and set up our basic test.
```solidity
// test/CCIPv1_5ForkBurnMintPoolFork.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Test, Vm } from "forge-std/Test.sol";
import { CCIPLocalSimulatorFork, Register } from "../../../src/ccip/CCIPLocalSimulatorFork.sol";
import { BurnMintTokenPool, TokenPool } from "@chainlink/contracts-ccip/src/v0.8/ccip/pools/BurnMintTokenPool.sol";
import { LockReleaseTokenPool } from "@chainlink/contracts-ccip/src/v0.8/ccip/pools/LockReleaseTokenPool.sol"; // not used in this test
import { IBurnMintERC20 } from "@chainlink/contracts-ccip/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol";
import { RegistryModuleOwnerCustom } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol";
import { TokenAdminRegistry } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/TokenAdminRegistry.sol";
import { RateLimiter } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/RateLimiter.sol";
import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
/**
* The token code part from previous section goes here...
*/
contract CCIPv1_5BurnMintPoolFork is Test {
CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
MockERC20BurnAndMintToken public mockERC20TokenEthSepolia;
MockERC20BurnAndMintToken public mockERC20TokenBaseSepolia;
BurnMintTokenPool public burnMintTokenPoolEthSepolia;
BurnMintTokenPool public burnMintTokenPoolBaseSepolia;
Register.NetworkDetails ethSepoliaNetworkDetails;
Register.NetworkDetails baseSepoliaNetworkDetails;
uint256 ethSepoliaFork;
uint256 baseSepoliaFork;
address alice;
function setUp() public {
alice = makeAddr("alice");
string memory ETHEREUM_SEPOLIA_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
string memory BASE_SEPOLIA_RPC_URL = vm.envString("BASE_SEPOLIA_RPC_URL");
ethSepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
baseSepoliaFork = vm.createFork(BASE_SEPOLIA_RPC_URL);
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));
}
}
```
#### Step 1) Deploy token on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function setUp() public {
// Code from previous section goes here...
// Step 1) Deploy token on Ethereum Sepolia
vm.startPrank(alice);
mockERC20TokenEthSepolia = new MockERC20BurnAndMintToken();
vm.stopPrank();
}
}
```
#### Step 2) Deploy token on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function setUp() public {
// Code from previous section goes here...
// Step 2) Deploy token on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia = new MockERC20BurnAndMintToken();
vm.stopPrank();
}
}
```
#### Step 3) Deploy BurnMintTokenPool on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
// Code from previous section goes here...
function test_forkSupportNewCCIPToken() public {
// Step 3) Deploy BurnMintTokenPool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
ethSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
address[] memory allowlist = new address[](0);
uint8 localTokenDecimals = 18;
vm.startPrank(alice);
burnMintTokenPoolEthSepolia = new BurnMintTokenPool(
IBurnMintERC20(address(mockERC20TokenEthSepolia)),
localTokenDecimals,
allowlist,
ethSepoliaNetworkDetails.rmnProxyAddress,
ethSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
}
}
```
#### Step 4) Deploy BurnMintTokenPool on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 4) Deploy BurnMintTokenPool on Base Sepolia
vm.selectFork(baseSepoliaFork);
baseSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
vm.startPrank(alice);
burnMintTokenPoolBaseSepolia = new BurnMintTokenPool(
IBurnMintERC20(address(mockERC20TokenBaseSepolia)),
localTokenDecimals,
allowlist,
baseSepoliaNetworkDetails.rmnProxyAddress,
baseSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
}
}
```
#### Step 5) Grant Mint and Burn roles to BurnMintTokenPool on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 5) Grant Mint and Burn roles to BurnMintTokenPool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
mockERC20TokenEthSepolia.grantRole(mockERC20TokenEthSepolia.MINTER_ROLE(), address(burnMintTokenPoolEthSepolia));
mockERC20TokenEthSepolia.grantRole(mockERC20TokenEthSepolia.BURNER_ROLE(), address(burnMintTokenPoolEthSepolia));
vm.stopPrank();
}
}
```
#### Step 6) Grant Mint and Burn roles to BurnMintTokenPool on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 6) Grant Mint and Burn roles to BurnMintTokenPool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia.grantRole(mockERC20TokenBaseSepolia.MINTER_ROLE(), address(burnMintTokenPoolBaseSepolia));
mockERC20TokenBaseSepolia.grantRole(mockERC20TokenBaseSepolia.BURNER_ROLE(), address(burnMintTokenPoolBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 7) Claim Admin role on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 7) Claim Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomEthSepolia = RegistryModuleOwnerCustom(
ethSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomEthSepolia.registerAdminViaGetCCIPAdmin(address(mockERC20TokenEthSepolia));
vm.stopPrank();
}
}
```
#### Step 8) Claim Admin role on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 8) Claim Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomBaseSepolia = RegistryModuleOwnerCustom(
baseSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomBaseSepolia.registerAdminViaGetCCIPAdmin(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 9) Accept Admin role on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 9) Accept Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
TokenAdminRegistry tokenAdminRegistryEthSepolia = TokenAdminRegistry(
ethSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.acceptAdminRole(address(mockERC20TokenEthSepolia));
vm.stopPrank();
}
}
```
#### Step 10) Accept Admin role on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 10) Accept Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
TokenAdminRegistry tokenAdminRegistryBaseSepolia = TokenAdminRegistry(
baseSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.acceptAdminRole(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 11) Link token to pool on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 11) Link token to pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.setPool(address(mockERC20TokenEthSepolia), address(burnMintTokenPoolEthSepolia));
vm.stopPrank();
}
}
```
#### Step 12) Link token to pool on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 12) Link token to pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.setPool(address(mockERC20TokenBaseSepolia), address(burnMintTokenPoolBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 13) Configure Token Pool on Ethereum Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 13) Configure Token Pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
TokenPool.ChainUpdate[] memory chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesEthSepolia = new bytes[](1);
remotePoolAddressesEthSepolia[0] = abi.encode(address(burnMintTokenPoolEthSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: baseSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesEthSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenBaseSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 })
});
uint64[] memory remoteChainSelectorsToRemove = new uint64[](0);
burnMintTokenPoolEthSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
}
}
```
#### Step 14) Configure Token Pool on Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 14) Configure Token Pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesBaseSepolia = new bytes[](1);
remotePoolAddressesBaseSepolia[0] = abi.encode(address(burnMintTokenPoolEthSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: ethSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesBaseSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenEthSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 })
});
burnMintTokenPoolBaseSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
}
}
```
#### Step 15) Mint tokens on Ethereum Sepolia and transfer them to Base Sepolia
```solidity
contract CCIPv1_5BurnMintPoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 15) Mint tokens on Ethereum Sepolia and transfer them to Base Sepolia
vm.selectFork(ethSepoliaFork);
address linkSepolia = ethSepoliaNetworkDetails.linkAddress;
ccipLocalSimulatorFork.requestLinkFromFaucet(address(alice), 20 ether);
uint256 amountToSend = 100;
Client.EVMTokenAmount[] memory tokenToSendDetails = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
token: address(mockERC20TokenEthSepolia),
amount: amountToSend
});
tokenToSendDetails[0] = tokenAmount;
vm.startPrank(alice);
mockERC20TokenEthSepolia.mint(address(alice), amountToSend);
mockERC20TokenEthSepolia.approve(ethSepoliaNetworkDetails.routerAddress, amountToSend);
IERC20(linkSepolia).approve(ethSepoliaNetworkDetails.routerAddress, 20 ether);
uint256 balanceOfAliceBeforeEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
IRouterClient routerEthSepolia = IRouterClient(ethSepoliaNetworkDetails.routerAddress);
routerEthSepolia.ccipSend(
baseSepoliaNetworkDetails.chainSelector,
Client.EVM2AnyMessage({
receiver: abi.encode(address(alice)),
data: "",
tokenAmounts: tokenToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({ gasLimit: 0 })),
feeToken: linkSepolia
})
);
uint256 balanceOfAliceAfterEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
vm.stopPrank();
assertEq(balanceOfAliceAfterEthSepolia, balanceOfAliceBeforeEthSepolia - amountToSend);
ccipLocalSimulatorFork.switchChainAndRouteMessage(baseSepoliaFork);
uint256 balanceOfAliceAfterBaseSepolia = mockERC20TokenBaseSepolia.balanceOf(alice);
assertEq(balanceOfAliceAfterBaseSepolia, amountToSend);
}
}
```
## Final code - full example
```solidity
// test/CCIPv1_5ForkBurnMintPoolFork.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Test, Vm } from "forge-std/Test.sol";
import { CCIPLocalSimulatorFork, Register } from "../../../src/ccip/CCIPLocalSimulatorFork.sol";
import { BurnMintTokenPool, TokenPool } from "@chainlink/contracts-ccip/src/v0.8/ccip/pools/BurnMintTokenPool.sol";
import { LockReleaseTokenPool } from "@chainlink/contracts-ccip/src/v0.8/ccip/pools/LockReleaseTokenPool.sol"; // not used in this test
import { IBurnMintERC20 } from "@chainlink/contracts-ccip/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol";
import { RegistryModuleOwnerCustom } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol";
import { TokenAdminRegistry } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/TokenAdminRegistry.sol";
import { RateLimiter } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/RateLimiter.sol";
import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import { ERC20, ERC20Burnable, IERC20 } 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 MockERC20BurnAndMintToken is IBurnMintERC20, ERC20Burnable, AccessControl {
address internal immutable i_CCIPAdmin;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
constructor() ERC20("MockERC20BurnAndMintToken", "MTK") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
_grantRole(BURNER_ROLE, msg.sender);
i_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() public view returns (address) {
return i_CCIPAdmin;
}
}
contract CCIPv1_5BurnMintPoolFork is Test {
CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
MockERC20BurnAndMintToken public mockERC20TokenEthSepolia;
MockERC20BurnAndMintToken public mockERC20TokenBaseSepolia;
BurnMintTokenPool public burnMintTokenPoolEthSepolia;
BurnMintTokenPool public burnMintTokenPoolBaseSepolia;
Register.NetworkDetails ethSepoliaNetworkDetails;
Register.NetworkDetails baseSepoliaNetworkDetails;
uint256 ethSepoliaFork;
uint256 baseSepoliaFork;
address alice;
function setUp() public {
alice = makeAddr("alice");
string memory ETHEREUM_SEPOLIA_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
string memory BASE_SEPOLIA_RPC_URL = vm.envString("BASE_SEPOLIA_RPC_URL");
ethSepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
baseSepoliaFork = vm.createFork(BASE_SEPOLIA_RPC_URL);
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));
// Step 1) Deploy token on Ethereum Sepolia
vm.startPrank(alice);
mockERC20TokenEthSepolia = new MockERC20BurnAndMintToken();
vm.stopPrank();
// Step 2) Deploy token on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia = new MockERC20BurnAndMintToken();
vm.stopPrank();
}
function test_forkSupportNewCCIPToken() public {
// Step 3) Deploy BurnMintTokenPool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
ethSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
address[] memory allowlist = new address[](0);
uint8 localTokenDecimals = 18;
vm.startPrank(alice);
burnMintTokenPoolEthSepolia = new BurnMintTokenPool(
IBurnMintERC20(address(mockERC20TokenEthSepolia)),
localTokenDecimals,
allowlist,
ethSepoliaNetworkDetails.rmnProxyAddress,
ethSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
// Step 4) Deploy BurnMintTokenPool on Base Sepolia
vm.selectFork(baseSepoliaFork);
baseSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
vm.startPrank(alice);
burnMintTokenPoolBaseSepolia = new BurnMintTokenPool(
IBurnMintERC20(address(mockERC20TokenBaseSepolia)),
localTokenDecimals,
allowlist,
baseSepoliaNetworkDetails.rmnProxyAddress,
baseSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
// Step 5) Grant Mint and Burn roles to BurnMintTokenPool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
mockERC20TokenEthSepolia.grantRole(mockERC20TokenEthSepolia.MINTER_ROLE(), address(burnMintTokenPoolEthSepolia));
mockERC20TokenEthSepolia.grantRole(mockERC20TokenEthSepolia.BURNER_ROLE(), address(burnMintTokenPoolEthSepolia));
vm.stopPrank();
// Step 6) Grant Mint and Burn roles to BurnMintTokenPool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia.grantRole(mockERC20TokenBaseSepolia.MINTER_ROLE(), address(burnMintTokenPoolBaseSepolia));
mockERC20TokenBaseSepolia.grantRole(mockERC20TokenBaseSepolia.BURNER_ROLE(), address(burnMintTokenPoolBaseSepolia));
vm.stopPrank();
// Step 7) Claim Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomEthSepolia = RegistryModuleOwnerCustom(
ethSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomEthSepolia.registerAdminViaGetCCIPAdmin(address(mockERC20TokenEthSepolia));
vm.stopPrank();
// Step 8) Claim Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomBaseSepolia = RegistryModuleOwnerCustom(
baseSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomBaseSepolia.registerAdminViaGetCCIPAdmin(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
// Step 9) Accept Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
TokenAdminRegistry tokenAdminRegistryEthSepolia = TokenAdminRegistry(
ethSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.acceptAdminRole(address(mockERC20TokenEthSepolia));
vm.stopPrank();
// Step 10) Accept Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
TokenAdminRegistry tokenAdminRegistryBaseSepolia = TokenAdminRegistry(
baseSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.acceptAdminRole(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
// Step 11) Link token to pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.setPool(address(mockERC20TokenEthSepolia), address(burnMintTokenPoolEthSepolia));
vm.stopPrank();
// Step 12) Link token to pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.setPool(address(mockERC20TokenBaseSepolia), address(burnMintTokenPoolBaseSepolia));
vm.stopPrank();
// Step 13) Configure Token Pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
TokenPool.ChainUpdate[] memory chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesEthSepolia = new bytes[](1);
remotePoolAddressesEthSepolia[0] = abi.encode(address(burnMintTokenPoolEthSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: baseSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesEthSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenBaseSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 })
});
uint64[] memory remoteChainSelectorsToRemove = new uint64[](0);
burnMintTokenPoolEthSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
// Step 14) Configure Token Pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesBaseSepolia = new bytes[](1);
remotePoolAddressesBaseSepolia[0] = abi.encode(address(burnMintTokenPoolEthSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: ethSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesBaseSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenEthSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: 100_000, rate: 167 })
});
burnMintTokenPoolBaseSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
// Step 15) Mint tokens on Ethereum Sepolia and transfer them to Base Sepolia
vm.selectFork(ethSepoliaFork);
address linkSepolia = ethSepoliaNetworkDetails.linkAddress;
ccipLocalSimulatorFork.requestLinkFromFaucet(address(alice), 20 ether);
uint256 amountToSend = 100;
Client.EVMTokenAmount[] memory tokenToSendDetails = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
token: address(mockERC20TokenEthSepolia),
amount: amountToSend
});
tokenToSendDetails[0] = tokenAmount;
vm.startPrank(alice);
mockERC20TokenEthSepolia.mint(address(alice), amountToSend);
mockERC20TokenEthSepolia.approve(ethSepoliaNetworkDetails.routerAddress, amountToSend);
IERC20(linkSepolia).approve(ethSepoliaNetworkDetails.routerAddress, 20 ether);
uint256 balanceOfAliceBeforeEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
IRouterClient routerEthSepolia = IRouterClient(ethSepoliaNetworkDetails.routerAddress);
routerEthSepolia.ccipSend(
baseSepoliaNetworkDetails.chainSelector,
Client.EVM2AnyMessage({
receiver: abi.encode(address(alice)),
data: "",
tokenAmounts: tokenToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({ gasLimit: 0 })),
feeToken: linkSepolia
})
);
uint256 balanceOfAliceAfterEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
vm.stopPrank();
assertEq(balanceOfAliceAfterEthSepolia, balanceOfAliceBeforeEthSepolia - amountToSend);
ccipLocalSimulatorFork.switchChainAndRouteMessage(baseSepoliaFork);
uint256 balanceOfAliceAfterBaseSepolia = mockERC20TokenBaseSepolia.balanceOf(alice);
assertEq(balanceOfAliceAfterBaseSepolia, amountToSend);
}
}
```
---
# CCT - owner() token with Lock and Release Pool in forked environments
Source: https://docs.chain.link/chainlink-local/build/ccip/foundry/cct-lock-and-release-fork
This tutorial will guide you through the process of testing the procedure of enabling your own tokens in CCIP. We will use the CCT-compatible ERC-20 token with `owner()` function implemented. We will use Lock & Release Pool for transferring this token across different blockchains using Chainlink CCIP.
## Prerequisites
Before we start with this guide, let's recap parts of the CCT standard that we will need for it.
### Requirements for Cross-Chain Tokens
Before enabling an ERC20-compatible token in CCIP, it's important to understand the requirements it must fulfill to integrate with CCIP.
- **Recommended Permissionless Token Administrator address registration methods**: A token can utilize either of these supported function signatures to register permissionlessly:
- `owner()`: This function returns the token contract owner's address.
- `getCCIPAdmin()`: This function returns the token administrator's address and is recommended for new tokens, as it allows for abstraction of the CCIP Token Administrator role from other common roles, like `owner()`.
- **Requirements for CCIP token transfers**: The token's smart contract must meet minimum requirements to integrate with CCIP.
- **Burn & Mint Requirements**:
- The token smart contract must have the following functions:
- `mint(address account, uint256 amount)`: This function is used to mint the `amount` of tokens to a given `account` on the destination blockchain.
- `burn(uint256 amount)`: This function is used to burn the `amount` of tokens on the source blockchain.
- `decimals()`: Returns the token's number of decimals.
- `balanceOf(address account)`: Returns the current token balance of the specified `account`.
- `burnFrom(address account, uint256 amount)`: This function burns a specified number of tokens from the provided account on the source blockchain. **Note**: This is an optional function. We generally recommend using the `burn` function, but if you use a tokenPool that calls `burnFrom`, your token contract will need to implement this function.
- On the source and destination blockchains, the token contract must support granting mint and burn permissions. The token developers or another role (such as the token administrator) will grant these permissions to the token pool.
- **Lock & Mint Requirements**:
- The token smart contract must have the following functions:
- `decimals()`: Returns the token's number of decimals.
- `balanceOf(address account)`: Returns the current token balance of the specified `account`.
- On the destination blockchain, The token contract must support granting mint and burn permissions. The token developers or another role (such as the token administrator) will grant these permissions to the token pool.
If you don't have an existing token: For all blockchains where tokens need to be burned and minted (for example, the source or destination chain in the case of Burn and Mint, or the destination blockchain in the case of Lock and Mint), Chainlink provides a [BurnMintERC677](https://github.com/smartcontractkit/ccip/tree/release/contracts-ccip-1.5.1/contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol) contract that you can use to deploy your token in minutes. This token follows the [ERC677](https://github.com/ethereum/EIPs/issues/677) or [ERC777](https://ethereum.org/en/developers/docs/standards/tokens/erc-777/), allowing you to use it as-is or extend it to meet your specific requirements.
### Understanding the Procedure
It is also important first to understand the overall procedure for enabling your tokens in CCIP. This procedure involves deploying tokens and token pools, registering administrative roles, and configuring token pools to enable secure token transfers using CCIP. The steps in the diagram below highlight the flow of actions needed to enable a token for cross-chain transfers. Whether you're working with an Externally Owned Account (EOA) or a **Smart Account** (such as one using a multisig scheme), the overall logic remains the same. You'll follow the same process to enable cross-chain token transfers, configure pools, and register administrative roles.
The diagram below outlines the entire process:
## Before You Begin
1. **Install Foundry**:
If you haven't already, follow the instructions in the [Foundry documentation](https://book.getfoundry.sh/getting-started/installation) to install Foundry.
2. **Create new Foundry project**: Create a new Foundry project by running the following command:
```bash
forge init
```
3. **Set up your environment**: Create a `.env` file, and fill in the required values:
Example `.env` file:
```bash
ETHEREUM_SEPOLIA_RPC_URL=
BASE_SEPOLIA_RPC_URL=
```
## Create the `owner()` ERC-20 token
Inside the `test` folder create the new Solidity file and name it `CCIPv1_5LockReleasePoolFork.t.sol`. We will use only this file through out the rest of this guide.
Create the CCT-compatible ERC-20 token with `owner()` function.
```solidity
// test/CCIPv1_5LockReleasePoolFork.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ERC20, IERC20 } from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol";
import { OwnerIsCreator } from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol";
contract MockERC20TokenOwner is ERC20, OwnerIsCreator {
constructor() ERC20("MockERC20Token", "MTK") {}
function mint(address account, uint256 amount) public onlyOwner {
_mint(account, amount);
}
}
```
## Test the CCT enabling procedure
Expand the existing `CCIPv1_5LockReleasePoolFork.t.sol` file to create and set up our basic test.
```solidity
// test/CCIPv1_5LockReleasePoolFork.t.sol
import { Test, Vm } from "forge-std/Test.sol";
import { CCIPLocalSimulatorFork, Register } from "../../../src/ccip/CCIPLocalSimulatorFork.sol";
import { LockReleaseTokenPool, TokenPool } from "@chainlink/contracts-ccip/src/v0.8/ccip/pools/LockReleaseTokenPool.sol";
import { RegistryModuleOwnerCustom } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol";
import { TokenAdminRegistry } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/TokenAdminRegistry.sol";
import { RateLimiter } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/RateLimiter.sol";
import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
/**
* The token code part from previous section goes here...
*/
contract CCIPv1_5LockReleasePoolFork is Test {
CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
MockERC20TokenOwner public mockERC20TokenEthSepolia;
MockERC20TokenOwner public mockERC20TokenBaseSepolia;
LockReleaseTokenPool public lockReleaseTokenPoolEthSepolia;
LockReleaseTokenPool public lockReleaseTokenPoolBaseSepolia;
Register.NetworkDetails ethSepoliaNetworkDetails;
Register.NetworkDetails baseSepoliaNetworkDetails;
uint256 ethSepoliaFork;
uint256 baseSepoliaFork;
address alice;
function setUp() public {
alice = makeAddr("alice");
string memory ETHEREUM_SEPOLIA_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
string memory BASE_SEPOLIA_RPC_URL = vm.envString("BASE_SEPOLIA_RPC_URL");
ethSepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
baseSepoliaFork = vm.createFork(BASE_SEPOLIA_RPC_URL);
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));
}
}
```
#### Step 1) Deploy token on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function setUp() public {
// Code from previous section goes here...
// Step 1) Deploy token on Ethereum Sepolia
vm.startPrank(alice);
mockERC20TokenEthSepolia = new MockERC20TokenOwner();
vm.stopPrank();
}
}
```
#### Step 2) Deploy token on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function setUp() public {
// Code from previous section goes here...
// Step 2) Deploy token on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia = new MockERC20TokenOwner();
vm.stopPrank();
}
}
```
#### Step 3) Deploy LockReleaseTokenPool on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
// Code from previous section goes here...
function test_forkSupportNewCCIPToken() public {
// Step 3) Deploy LockReleaseTokenPool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
ethSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
address[] memory allowlist = new address[](0);
uint8 localTokenDecimals = 18;
vm.startPrank(alice);
lockReleaseTokenPoolEthSepolia = new LockReleaseTokenPool(
IERC20(address(mockERC20TokenEthSepolia)),
localTokenDecimals,
allowlist,
ethSepoliaNetworkDetails.rmnProxyAddress,
true, // acceptLiquidity
ethSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
}
}
```
#### Step 4) Deploy LockReleaseTokenPool on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 4) Deploy LockReleaseTokenPool on Base Sepolia
vm.selectFork(baseSepoliaFork);
baseSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
vm.startPrank(alice);
lockReleaseTokenPoolBaseSepolia = new LockReleaseTokenPool(
IERC20(address(mockERC20TokenBaseSepolia)),
localTokenDecimals,
allowlist,
baseSepoliaNetworkDetails.rmnProxyAddress,
true, // acceptLiquidity
baseSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
}
}
```
#### Step 5) Set the LiquidityManager address and Add liquidity to the pool on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 5) Set the LiquidityManager address and Add liquidity to the pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
uint256 amountToMint = 1_000_000;
uint128 liquidityAmount = 100_000;
vm.startPrank(alice);
mockERC20TokenEthSepolia.mint(address(alice), amountToMint);
mockERC20TokenEthSepolia.approve(address(lockReleaseTokenPoolEthSepolia), liquidityAmount);
lockReleaseTokenPoolEthSepolia.setRebalancer(address(alice));
lockReleaseTokenPoolEthSepolia.provideLiquidity(liquidityAmount);
vm.stopPrank();
}
}
```
#### Step 6) Set the LiquidityManager address and Add liquidity to the pool on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 6) Set the LiquidityManager address and Add liquidity to the pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia.mint(address(alice), amountToMint);
mockERC20TokenBaseSepolia.approve(address(lockReleaseTokenPoolBaseSepolia), liquidityAmount);
lockReleaseTokenPoolBaseSepolia.setRebalancer(address(alice));
lockReleaseTokenPoolBaseSepolia.provideLiquidity(liquidityAmount);
vm.stopPrank();
}
}
```
#### Step 7) Claim Admin role on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 7) Claim Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomEthSepolia = RegistryModuleOwnerCustom(
ethSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomEthSepolia.registerAdminViaOwner(address(mockERC20TokenEthSepolia));
vm.stopPrank();
}
}
```
#### Step 8) Claim Admin role on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 8) Claim Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomBaseSepolia = RegistryModuleOwnerCustom(
baseSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomBaseSepolia.registerAdminViaOwner(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 9) Accept Admin role on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 9) Accept Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
TokenAdminRegistry tokenAdminRegistryEthSepolia = TokenAdminRegistry(
ethSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.acceptAdminRole(address(mockERC20TokenEthSepolia));
vm.stopPrank();
}
}
```
#### Step 10) Accept Admin role on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 10) Accept Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
TokenAdminRegistry tokenAdminRegistryBaseSepolia = TokenAdminRegistry(
baseSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.acceptAdminRole(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 11) Link token to pool on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 11) Link token to pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.setPool(address(mockERC20TokenEthSepolia), address(lockReleaseTokenPoolEthSepolia));
vm.stopPrank();
}
}
```
#### Step 12) Link token to pool on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 12) Link token to pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.setPool(address(mockERC20TokenBaseSepolia), address(lockReleaseTokenPoolBaseSepolia));
vm.stopPrank();
}
}
```
#### Step 13) Configure Token Pool on Ethereum Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 13) Configure Token Pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
TokenPool.ChainUpdate[] memory chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesEthSepolia = new bytes[](1);
remotePoolAddressesEthSepolia[0] = abi.encode(address(lockReleaseTokenPoolBaseSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: baseSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesEthSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenBaseSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 })
});
uint64[] memory remoteChainSelectorsToRemove = new uint64[](0);
lockReleaseTokenPoolEthSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
}
}
```
#### Step 14) Configure Token Pool on Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 14) Configure Token Pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesBaseSepolia = new bytes[](1);
remotePoolAddressesBaseSepolia[0] = abi.encode(address(lockReleaseTokenPoolEthSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: ethSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesBaseSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenEthSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 })
});
lockReleaseTokenPoolBaseSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
}
}
```
#### Step 15) Mint tokens on Ethereum Sepolia and transfer them to Base Sepolia
```solidity
contract CCIPv1_5LockReleasePoolFork is Test {
function test_forkSupportNewCCIPToken() public {
// Code from previous section goes here...
// Step 15) Transfer tokens from Ethereum Sepolia to Base Sepolia
vm.selectFork(ethSepoliaFork);
address linkEthSepoliaAddress = ethSepoliaNetworkDetails.linkAddress;
address routerEthSepoliaAddress = ethSepoliaNetworkDetails.routerAddress;
ccipLocalSimulatorFork.requestLinkFromFaucet(address(alice), 20 ether);
uint256 amountToSend = 100;
Client.EVMTokenAmount[] memory tokenToSendDetails = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
token: address(mockERC20TokenEthSepolia),
amount: amountToSend
});
tokenToSendDetails[0] = tokenAmount;
vm.startPrank(alice);
mockERC20TokenEthSepolia.approve(routerEthSepoliaAddress, amountToSend);
IERC20(linkEthSepoliaAddress).approve(routerEthSepoliaAddress, 20 ether);
uint256 balanceOfAliceBeforeEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
uint64 destinationChainSelector = baseSepoliaNetworkDetails.chainSelector;
IRouterClient routerEthSepolia = IRouterClient(routerEthSepoliaAddress);
routerEthSepolia.ccipSend(
destinationChainSelector,
Client.EVM2AnyMessage({
receiver: abi.encode(address(alice)),
data: "",
tokenAmounts: tokenToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({ gasLimit: 0 })),
feeToken: linkEthSepoliaAddress
})
);
uint256 balanceOfAliceAfterEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
vm.stopPrank();
assertEq(balanceOfAliceAfterEthSepolia, balanceOfAliceBeforeEthSepolia - amountToSend);
ccipLocalSimulatorFork.switchChainAndRouteMessage(baseSepoliaFork);
uint256 balanceOfAliceAfterBaseSepolia = mockERC20TokenBaseSepolia.balanceOf(alice);
assertEq(balanceOfAliceAfterBaseSepolia, balanceOfAliceBeforeEthSepolia + amountToSend);
}
}
```
## Final code - full example
```solidity
// test/CCIPv1_5LockReleasePoolFork.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Test, Vm } from "forge-std/Test.sol";
import { CCIPLocalSimulatorFork, Register } from "../../../src/ccip/CCIPLocalSimulatorFork.sol";
import { LockReleaseTokenPool, TokenPool } from "@chainlink/contracts-ccip/src/v0.8/ccip/pools/LockReleaseTokenPool.sol";
import { RegistryModuleOwnerCustom } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol";
import { TokenAdminRegistry } from "@chainlink/contracts-ccip/src/v0.8/ccip/tokenAdminRegistry/TokenAdminRegistry.sol";
import { RateLimiter } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/RateLimiter.sol";
import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
import { ERC20, IERC20 } from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol";
import { OwnerIsCreator } from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol";
contract MockERC20TokenOwner is ERC20, OwnerIsCreator {
constructor() ERC20("MockERC20Token", "MTK") {}
function mint(address account, uint256 amount) public onlyOwner {
_mint(account, amount);
}
}
contract CCIPv1_5LockReleasePoolFork is Test {
CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
MockERC20TokenOwner public mockERC20TokenEthSepolia;
MockERC20TokenOwner public mockERC20TokenBaseSepolia;
LockReleaseTokenPool public lockReleaseTokenPoolEthSepolia;
LockReleaseTokenPool public lockReleaseTokenPoolBaseSepolia;
Register.NetworkDetails ethSepoliaNetworkDetails;
Register.NetworkDetails baseSepoliaNetworkDetails;
uint256 ethSepoliaFork;
uint256 baseSepoliaFork;
address alice;
function setUp() public {
alice = makeAddr("alice");
string memory ETHEREUM_SEPOLIA_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
string memory BASE_SEPOLIA_RPC_URL = vm.envString("BASE_SEPOLIA_RPC_URL");
ethSepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
baseSepoliaFork = vm.createFork(BASE_SEPOLIA_RPC_URL);
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));
// Step 1) Deploy token on Ethereum Sepolia
vm.startPrank(alice);
mockERC20TokenEthSepolia = new MockERC20TokenOwner();
vm.stopPrank();
// Step 2) Deploy token on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia = new MockERC20TokenOwner();
vm.stopPrank();
}
function test_forkSupportNewCCIPToken() public {
// Step 3) Deploy LockReleaseTokenPool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
ethSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
address[] memory allowlist = new address[](0);
uint8 localTokenDecimals = 18;
vm.startPrank(alice);
lockReleaseTokenPoolEthSepolia = new LockReleaseTokenPool(
IERC20(address(mockERC20TokenEthSepolia)),
localTokenDecimals,
allowlist,
ethSepoliaNetworkDetails.rmnProxyAddress,
true, // acceptLiquidity
ethSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
// Step 4) Deploy LockReleaseTokenPool on Base Sepolia
vm.selectFork(baseSepoliaFork);
baseSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
vm.startPrank(alice);
lockReleaseTokenPoolBaseSepolia = new LockReleaseTokenPool(
IERC20(address(mockERC20TokenBaseSepolia)),
localTokenDecimals,
allowlist,
baseSepoliaNetworkDetails.rmnProxyAddress,
true, // acceptLiquidity
baseSepoliaNetworkDetails.routerAddress
);
vm.stopPrank();
// Step 5) Set the LiquidityManager address and Add liquidity to the pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
uint256 amountToMint = 1_000_000;
uint128 liquidityAmount = 100_000;
vm.startPrank(alice);
mockERC20TokenEthSepolia.mint(address(alice), amountToMint);
mockERC20TokenEthSepolia.approve(address(lockReleaseTokenPoolEthSepolia), liquidityAmount);
lockReleaseTokenPoolEthSepolia.setRebalancer(address(alice));
lockReleaseTokenPoolEthSepolia.provideLiquidity(liquidityAmount);
vm.stopPrank();
// Step 6) Set the LiquidityManager address and Add liquidity to the pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
mockERC20TokenBaseSepolia.mint(address(alice), amountToMint);
mockERC20TokenBaseSepolia.approve(address(lockReleaseTokenPoolBaseSepolia), liquidityAmount);
lockReleaseTokenPoolBaseSepolia.setRebalancer(address(alice));
lockReleaseTokenPoolBaseSepolia.provideLiquidity(liquidityAmount);
vm.stopPrank();
// Step 7) Claim Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomEthSepolia = RegistryModuleOwnerCustom(
ethSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomEthSepolia.registerAdminViaOwner(address(mockERC20TokenEthSepolia));
vm.stopPrank();
// Step 8) Claim Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
RegistryModuleOwnerCustom registryModuleOwnerCustomBaseSepolia = RegistryModuleOwnerCustom(
baseSepoliaNetworkDetails.registryModuleOwnerCustomAddress
);
vm.startPrank(alice);
registryModuleOwnerCustomBaseSepolia.registerAdminViaOwner(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
// Step 9) Accept Admin role on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
TokenAdminRegistry tokenAdminRegistryEthSepolia = TokenAdminRegistry(
ethSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.acceptAdminRole(address(mockERC20TokenEthSepolia));
vm.stopPrank();
// Step 10) Accept Admin role on Base Sepolia
vm.selectFork(baseSepoliaFork);
TokenAdminRegistry tokenAdminRegistryBaseSepolia = TokenAdminRegistry(
baseSepoliaNetworkDetails.tokenAdminRegistryAddress
);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.acceptAdminRole(address(mockERC20TokenBaseSepolia));
vm.stopPrank();
// Step 11) Link token to pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryEthSepolia.setPool(address(mockERC20TokenEthSepolia), address(lockReleaseTokenPoolEthSepolia));
vm.stopPrank();
// Step 12) Link token to pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
tokenAdminRegistryBaseSepolia.setPool(address(mockERC20TokenBaseSepolia), address(lockReleaseTokenPoolBaseSepolia));
vm.stopPrank();
// Step 13) Configure Token Pool on Ethereum Sepolia
vm.selectFork(ethSepoliaFork);
vm.startPrank(alice);
TokenPool.ChainUpdate[] memory chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesEthSepolia = new bytes[](1);
remotePoolAddressesEthSepolia[0] = abi.encode(address(lockReleaseTokenPoolBaseSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: baseSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesEthSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenBaseSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 })
});
uint64[] memory remoteChainSelectorsToRemove = new uint64[](0);
lockReleaseTokenPoolEthSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
// Step 14) Configure Token Pool on Base Sepolia
vm.selectFork(baseSepoliaFork);
vm.startPrank(alice);
chains = new TokenPool.ChainUpdate[](1);
bytes[] memory remotePoolAddressesBaseSepolia = new bytes[](1);
remotePoolAddressesBaseSepolia[0] = abi.encode(address(lockReleaseTokenPoolEthSepolia));
chains[0] = TokenPool.ChainUpdate({
remoteChainSelector: ethSepoliaNetworkDetails.chainSelector,
remotePoolAddresses: remotePoolAddressesBaseSepolia,
remoteTokenAddress: abi.encode(address(mockERC20TokenEthSepolia)),
outboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 }),
inboundRateLimiterConfig: RateLimiter.Config({ isEnabled: true, capacity: liquidityAmount, rate: 167 })
});
lockReleaseTokenPoolBaseSepolia.applyChainUpdates(remoteChainSelectorsToRemove, chains);
vm.stopPrank();
// Step 15) Transfer tokens from Ethereum Sepolia to Base Sepolia
vm.selectFork(ethSepoliaFork);
address linkEthSepoliaAddress = ethSepoliaNetworkDetails.linkAddress;
address routerEthSepoliaAddress = ethSepoliaNetworkDetails.routerAddress;
ccipLocalSimulatorFork.requestLinkFromFaucet(address(alice), 20 ether);
uint256 amountToSend = 100;
Client.EVMTokenAmount[] memory tokenToSendDetails = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
token: address(mockERC20TokenEthSepolia),
amount: amountToSend
});
tokenToSendDetails[0] = tokenAmount;
vm.startPrank(alice);
mockERC20TokenEthSepolia.approve(routerEthSepoliaAddress, amountToSend);
IERC20(linkEthSepoliaAddress).approve(routerEthSepoliaAddress, 20 ether);
uint256 balanceOfAliceBeforeEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
uint64 destinationChainSelector = baseSepoliaNetworkDetails.chainSelector;
IRouterClient routerEthSepolia = IRouterClient(routerEthSepoliaAddress);
routerEthSepolia.ccipSend(
destinationChainSelector,
Client.EVM2AnyMessage({
receiver: abi.encode(address(alice)),
data: "",
tokenAmounts: tokenToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({ gasLimit: 0 })),
feeToken: linkEthSepoliaAddress
})
);
uint256 balanceOfAliceAfterEthSepolia = mockERC20TokenEthSepolia.balanceOf(alice);
vm.stopPrank();
assertEq(balanceOfAliceAfterEthSepolia, balanceOfAliceBeforeEthSepolia - amountToSend);
ccipLocalSimulatorFork.switchChainAndRouteMessage(baseSepoliaFork);
uint256 balanceOfAliceAfterBaseSepolia = mockERC20TokenBaseSepolia.balanceOf(alice);
assertEq(balanceOfAliceAfterBaseSepolia, balanceOfAliceBeforeEthSepolia + amountToSend);
}
}
```
---
# Using the CCIP Local Simulator to fork mainnets
Source: https://docs.chain.link/chainlink-local/build/ccip/foundry/forking-mainnets
This guide explains how to use the CCIP Local Simulator to test cross-chain transactions in forked mainnet environments using Foundry. You'll learn how to leverage the `Register.sol` contract to streamline the simulation process and properly configure mainnet details that aren't included by default.
## Understanding Network Registration
The `Register.sol` smart contract contains pre-configured details for test networks to help accelerate cross-chain transaction simulations in Foundry. However, mainnet details are intentionally excluded and must be manually configured.
Here's an example of how to verify network details for a test network such as Sepolia:
```solidity
pragma solidity ^0.8.0;
import { Test, console } from "forge-std/Test.sol"
import {
CCIPLocalSimulatorFork,
Register
} from "@chainlink/local/src/ccip/CCIPLocalSimulatorFork.sol"
contract ExampleTest is Test {
CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
uint256 public ethSepoliaFork;
Register.NetworkDetails public ethSepoliaNetworkDetails;
function setUp() public {
// Create a fork of the Sepolia network
string memory SEPOLIA_RPC_URL = vm.envString("SEPOLIA_RPC_URL");
ethSepoliaFork = vm.createFork(SEPOLIA_RPC_URL);
// Initialize the CCIP simulator
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));
// Select and verify the fork
vm.selectFork(ethSepoliaFork);
assertEq(block.chainid, 11155111);
// Verify network details match the CCIP Directory
ethSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
assertEq(ethSepoliaNetworkDetails.chainSelector, 16015286601757825753);
assertEq(ethSepoliaNetworkDetails.routerAddress, 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59);
}
}
```
## Working with Mainnet Forks
The CCIP Local Simulator supports any forked environment where CCIP contracts exist. Since mainnet details aren't included in `Register.sol`, you'll need to configure them manually. Let's walk through a complete example that demonstrates how to simulate cross-chain messages between Ethereum and Polygon mainnets. This example shows:
1. Setting up mainnet forks
2. Configuring network details
3. Creating and sending a cross-chain message
4. Routing the message between chains
Here's the complete implementation:
```solidity
pragma solidity ^0.8.0;
import { Test, console } from "forge-std/Test.sol";
import { CCIPLocalSimulatorFork, Register } from "@chainlink/local/src/ccip/CCIPLocalSimulatorFork.sol";
import { IRouterClient } from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
import { Client } from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
contract ExampleTest is Test {
CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
uint256 public ethereumMainnetForkId;
uint256 public polygonMainnetForkId;
function setUp() public {
// Create forks of both networks
string memory ETHEREUM_MAINNET_RPC_URL = vm.envString("ETHEREUM_MAINNET_RPC_URL");
string memory POLYGON_MAINNET_RPC_URL = vm.envString("POLYGON_MAINNET_RPC_URL");
ethereumMainnetForkId = vm.createFork(ETHEREUM_MAINNET_RPC_URL);
polygonMainnetForkId = vm.createFork(POLYGON_MAINNET_RPC_URL);
address ethereumMainnetCcipRouterAddress = 0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D;
uint64 polygonMainnetChainSelector = 4051577828743386545;
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
ccipLocalSimulatorFork.setNetworkDetails(
polygonMainnetForkId,
Register.NetworkDetails({
chainSelector: polygonMainnetChainSelector,
routerAddress: polygonMainnetCcipRouterAddress,
linkAddress: address(0), // not needed for this test
wrappedNativeAddress: address(0), // not needed for this test
ccipBnMAddress: address(0), // not needed for this test
ccipLnMAddress: address(0), // not needed for this test
rmnProxyAddress: address(0), // not needed for this test
registryModuleOwnerCustomAddress: address(0), // not needed for this test
tokenAdminRegistryAddress: address(0) // not needed for this test
})
);
vm.makePersistent(address(ccipLocalSimulatorFork));
}
function test_example() public {
// Set up the source chain (Ethereum)
vm.selectFork(ethereumMainnetForkId);
Register.NetworkDetails memory polygonMainnetNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(
polygonMainnetForkId
);
address alice = makeAddr("alice");
vm.deal(alice, 1 ether);
// Prepare the cross-chain message
vm.startPrank(alice);
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(ccipReceiverAddress),
data: abi.encode("Hello world"),
tokenAmounts: new Client.EVMTokenAmount[](0),
extraArgs: "",
feeToken: address(0)
});
// Send the message using CCIP
IRouterClient(ethereumMainnetCcipRouterAddress).ccipSend(polygonMainnetNetworkDetails.routerAddress, message);
vm.stopPrank();
// Route the message to Polygon
ccipLocalSimulatorFork.switchChainAndRouteMessage(polygonMainnetForkId);
}
}
```
**Code explanation**:
- **Network Setup**: The `setUp()` function creates forks of both Ethereum and Polygon mainnets and initializes the CCIP simulator.
- **Network Configuration**: We configure Polygon mainnet details using [`setNetworkDetails()`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork#setnetworkdetails). This step is crucial because mainnet details aren't included in `Register.sol`. The configuration includes:
- `chainSelector`: The unique CCIP identifier for the Polygon network. Check the [CCIP Directory](/ccip/directory/mainnet) for the correct value.
- `routerAddress`: The address of the CCIP router on Polygon. Check the [CCIP Directory](/ccip/directory/mainnet) for the correct value.
- Parameters that can be set to `address(0)` because they are optional for messaging.
- **Message Transfer**: The `test_example()` function demonstrates:
- Setting up a test user (alice) with funds
- Creating a cross-chain message
- Sending the message through CCIP
- Routing the message to the destination chain
After this configuration, you can simulate cross-chain messages between mainnet forks, enabling thorough testing of your cross-chain applications in a local environment.
---
# Using Chainlink Local to Test CCIP in Your Foundry Project
Source: https://docs.chain.link/chainlink-local/build/ccip/foundry
There are two comprehensive guides to help you test Chainlink CCIP in your Foundry project using Chainlink Local - one for each mode (with and without forking).
We recommend starting with the first guide (without forking) for initial development before advancing to the guide that demonstrates how to test in forked environments.
## Guides
### Using the CCIP Local Simulator in your Foundry project
This [guide](/chainlink-local/build/ccip/foundry/local-simulator) helps you set up and run CCIP in a localhost environment within your Foundry project. It provides step-by-step instructions on:
- Cloning the [CCIP Foundry Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-foundry)
- Installing necessary dependencies
- Running tests for token transfers between two accounts
Using the local simulator without forking is ideal for initial development and testing in an isolated environment.
### Using the CCIP Local Simulator in your Foundry project with forked environments
This [guide](/chainlink-local/build/ccip/foundry/local-simulator-fork) extends your testing capabilities by demonstrating how to use the CCIP Local Simulator in a forked environment. It includes instructions on:
- Setting up forks of real blockchain networks (such as Arbitrum Sepolia and Ethereum Sepolia)
- Writing test cases
- Running tests for cross-chain token transfers between two accounts
Using the local simulator with forked environments is ideal for testing in a more realistic yet controlled setting.
---
# Using the CCIP Local Simulator in your Foundry project with forked environments
Source: https://docs.chain.link/chainlink-local/build/ccip/foundry/local-simulator-fork
You can use Chainlink Local to run CCIP in forked environments within your Foundry project. To get started quickly, you will use the
[CCIP Foundry Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-foundry). This project includes the `Example01ForkTest` 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, you will fork *Arbitrum Sepolia*
and *Ethereum Sepolia*.
## Prerequisites
This guide assumes that you are familiar with the guide [Using the CCIP Local Simulator in your Foundry project](/chainlink-local/build/ccip/foundry/local-simulator).
If not, please get familiar with it and run all the prerequisites.
Set up an `.env` file with the following data:
- `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](https://www.alchemy.com/) or [Infura](https://www.infura.io/) 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](https://www.alchemy.com/),
[Infura](https://www.infura.io/), 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 `Example01ForkTest.t.sol` is located in the `./test/fork` directory.
This file contains two test cases:
1. **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.
2. **Transfer with native gas fees**: This test case transfers tokens from the sender account to the receiver account, paying fees in native gas. At the end of the test,
it verifies that the sender account was debited and the receiver account was credited.
In these tests, we simulate transfers from a source blockchain (which is a fork of *Arbitrum Sepolia*) to 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](#examine-the-code) section.
In your terminal, run the following command to execute the test:
```shell
forge test --match-contract Example01ForkTest
```
Example output:
```text
$ forge test --match-contract Example01ForkTest
[⠊] Compiling...
No files changed, compilation skipped
Ran 2 tests for test/fork/Example01Fork.t.sol:Example01ForkTest
[PASS] test_transferTokensFromEoaToEoaPayFeesInLink() (gas: 475199)
[PASS] test_transferTokensFromEoaToEoaPayFeesInNative() (gas: 451096)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.71s (17.29s CPU time)
Ran 1 test suite in 11.90s (11.71s CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
```
## Examine the code
### Setup
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 `setUp()` function is invoked before each test case to reinitialize all the variables, ensuring a clean state for each test:
1. Initialize the source and destination forks:
```solidity
string memory DESTINATION_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
string memory SOURCE_RPC_URL = vm.envString("ARBITRUM_SEPOLIA_RPC_URL");
destinationFork = vm.createSelectFork(DESTINATION_RPC_URL);
sourceFork = vm.createFork(SOURCE_RPC_URL);
```
2. Initialize the sender and receiver accounts:
```solidity
bob = makeAddr("bob");
alice = makeAddr("alice");
```
3. Initialize the [fork CCIP local simulator](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork#cciplocalsimulatorfork).
**Note**: `vm.makePersistent` is used to make the `ccipLocalSimulatorFork` address persistent across forks:
```solidity
ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
vm.makePersistent(address(ccipLocalSimulatorFork));
```
4. Retrieve and set up the network details for the destination chain.
**Note**: [`Register.NetworkDetails`](/chainlink-local/api-reference/v0.2.3/register#networkdetails)
is a struct that stores network details (such as chain selector, router address, link address, wrapped native address, or CCIP test tokens), and
[`getNetworkDetails`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork#getnetworkdetails) pulls network details based on chain IDs:
```solidity
Register.NetworkDetails memory destinationNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
destinationCCIPBnMToken = BurnMintERC677Helper(destinationNetworkDetails.ccipBnMAddress);
destinationChainSelector = destinationNetworkDetails.chainSelector;
```
5. Switch to the source fork and retrieve the network details for the source chain:
```solidity
vm.selectFork(sourceFork);
Register.NetworkDetails memory sourceNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
sourceCCIPBnMToken = BurnMintERC677Helper(sourceNetworkDetails.ccipBnMAddress);
sourceLinkToken = IERC20(sourceNetworkDetails.linkAddress);
sourceRouter = IRouterClient(sourceNetworkDetails.routerAddress);
```
6. All the variables are stored in the contract state for use in the test cases.
### Prepare scenario (helper function)
The `prepareScenario()` function is invoked at the beginning of each test case. It performs the following actions:
1. Select the source fork and request CCIP-BnM tokens for Alice:
```solidity
vm.selectFork(sourceFork);
vm.startPrank(alice);
sourceCCIPBnMToken.drip(alice);
```
2. Approve the source router to spend tokens on behalf of Alice:
```solidity
uint256 amountToSend = 100;
sourceCCIPBnMToken.approve(address(sourceRouter), amountToSend);
```
3. Create an array [Client.EVMTokenAmount](/ccip/api-reference/evm/v1.6.2/client#evmtokenamount)[] to specify the token transfer details. This array and the amount to send
are returned by the `prepareScenario()` function for use in the calling test case:
```solidity
tokensToSendDetails = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenToSendDetails =
Client.EVMTokenAmount({token: address(ccipBnMToken), amount: amountToSend});
tokensToSendDetails[0] = tokenToSendDetails;
```
4. Stop impersonating Alice (sender):
```solidity
vm.stopPrank();
```
### Test case 1: Transfer with LINK fees
The `test_transferTokensFromEoaToEoaPayFeesInLink` function tests the transfer of tokens between two externally owned accounts (EOA) while paying fees in LINK.
Here are the steps involved in this test case:
1. Invoke the `prepareScenario()` function to set up the necessary variables:
```solidity
(Client.EVMTokenAmount[] memory tokensToSendDetails, uint256 amountToSend) = prepareScenario();
```
2. Select the destination fork and record the initial token balance of Bob receiver:
```solidity
vm.selectFork(destinationFork);
uint256 balanceOfBobBefore = destinationCCIPBnMToken.balanceOf(bob);
```
3. Select the source fork and record the initial token balance of Alice (sender):
```solidity
vm.selectFork(sourceFork);
uint256 balanceOfAliceBefore = sourceCCIPBnMToken.balanceOf(alice);
```
4. Request 10 LINK tokens from the CCIP local simulator faucet for Alice (sender):
```solidity
ccipLocalSimulatorFork.requestLinkFromFaucet(alice, 10 ether);
```
5. Construct the [Client.EVM2AnyMessage](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) structure with the receiver, token amounts, and other necessary details.
- Set the `data` parameter to an empty string because you are not sending any arbitrary data, only tokens.
- In `extraArgs`, set the gas limit to `0`. This gas limit is for execution of receiver logic, which doesn't apply here because you're sending tokens to an EOA.
```solidity
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(bob),
data: abi.encode(""),
tokenAmounts: tokensToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})),
feeToken: address(sourceLinkToken)
});
```
6. Calculate the required fees for the transfer and approve the router to spend LINK tokens for these fees:
```solidity
uint256 fees = sourceRouter.getFee(destinationChainSelector, message);
sourceLinkToken.approve(address(sourceRouter), fees);
```
7. Send the CCIP transfer request to the router:
```solidity
sourceRouter.ccipSend(destinationChainSelector, message);
```
8. Stop impersonating Alice (sender):
```solidity
vm.stopPrank();
```
9. Record Alice's final token balance and verify that it has decreased by the amount sent:
```solidity
uint256 balanceOfAliceAfter = sourceCCIPBnMToken.balanceOf(alice);
assertEq(balanceOfAliceAfter, balanceOfAliceBefore - amountToSend);
```
10. Call the [`switchChainAndRouteMessage`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork#switchchainandroutemessage) function to switch
to the destination fork and route the message to complete the transfer:
```solidity
ccipLocalSimulatorFork.switchChainAndRouteMessage(destinationFork);
```
11. Record Bob's final token balance and verify that it has increased by the amount sent:
```solidity
uint256 balanceOfBobAfter = destinationCCIPBnMToken.balanceOf(bob);
assertEq(balanceOfBobAfter, balanceOfBobBefore + amountToSend);
```
### Test case 2: Transfer with native gas fees
The `test_transferTokensFromEoaToEoaPayFeesInNative` function tests the transfer of tokens between two externally owned accounts (EOA) while paying fees in native gas.
Here are the steps involved in this test case:
1. Invoke the `prepareScenario()` function to set up the necessary variables. (This function is the same as in the previous test case.)
2. Select the destination fork and record Bob's initial token balance. (This step is the same as in the previous test case.)
3. Select the source fork and record Alice's initial token balance. (This step is the same as in the previous test case.)
4. Begin impersonating Alice (sender) and provide her with native gas:
```solidity
vm.startPrank(alice);
deal(alice, 5 ether);
```
5. Construct the [Client.EVM2AnyMessage](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) structure. This step is the same as in the previous test case.
The main difference is that the `feeToken` is set with `address(0)` to indicate that the fees are paid in native gas:
```solidity
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(bob),
data: abi.encode(""),
tokenAmounts: tokensToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})),
feeToken: address(0)
});
```
6. Calculate the required fees for the transfer and send the CCIP transfer request along with the necessary native gas:
```solidity
uint256 fees = sourceRouter.getFee(destinationChainSelector, message);
sourceRouter.ccipSend{value: fees}(destinationChainSelector, message, fees);
```
7. Stop impersonating Alice (sender). (This step is the same as in the previous test case.)
8. Verify Alice's (sender) balance. (This step is the same as in the previous test case.)
9. Call the [`switchChainAndRouteMessage`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork#switchchainandroutemessage) function
to switch to the destination fork and route the message to complete the transfer. (This step is the same as in the previous test case.)
10. Verify Bob's (receiver) balance. (This step is the same as in the previous test case.)
---
# Using CCIP local simulator in your Foundry project
Source: https://docs.chain.link/chainlink-local/build/ccip/foundry/local-simulator
You can use Chainlink Local to run CCIP in a localhost environment within your Foundry project. To get started quickly, you will use the
[CCIP Foundry Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-foundry). This project is a Foundry boilerplate that includes the Chainlink Local
package and several CCIP examples.
## Prerequisites
1. In a terminal, clone the [CCIP Foundry Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-foundry) repository and change directories.
```shell
git clone https://github.com/smartcontractkit/ccip-starter-kit-foundry.git && \
cd ./ccip-starter-kit-foundry/
```
2. Run `npm install` to install the dependencies. This command installs the [Chainlink Local](https://github.com/smartcontractkit/chainlink-local) package
and other required packages:
```shell
npm install
```
3. Run `forge install` to install packages:
```shell
forge install
```
4. Run `forge build` to compile the contracts:
```shell
forge build
```
## Test tokens transfers
You will run a test to transfer tokens between two accounts. The test file `Example01.t.sol` is located in the `./test/no-fork` directory. This file contains two test cases:
1. **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.
2. **Transfer with native gas fees**: This test case transfers tokens from the sender account to the receiver account, paying fees in native gas. At the end of the test, it verifies that the sender account was debited and the receiver account was credited.
For a detailed explanation of the test file, refer to the [Examine the code](#examine-the-code) section.
In your terminal, run the following command to execute the test:
```shell
forge test --match-contract Example01Test
```
Example output:
```text
$ forge test --match-contract Example01Test
[⠊] Compiling...
No files changed, compilation skipped
Ran 2 tests for test/no-fork/Example01.t.sol:Example01Test
[PASS] test_transferTokensFromEoaToEoaPayFeesInLink() (gas: 167576)
[PASS] test_transferTokensFromEoaToEoaPayFeesInNative() (gas: 122348)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 8.79ms (976.54µs CPU time)
Ran 1 test suite in 201.00ms (8.79ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
```
## Examine the code
### Setup
To transfer tokens using CCIP, we need the following:
- Destination chain selector
- Source CCIP router
- LINK token for paying CCIP fees
- A test token contract (such as CCIP-BnM)
- A sender account (Alice)
- A receiver account (Bob)
The `setUp()` function is invoked before each test case to reinitialize all the variables, ensuring a clean state for each test:
1. Initialize the [CCIP local simulator](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#cciplocalsimulator) contract:
```solidity
ccipLocalSimulator = new CCIPLocalSimulator();
```
2. Invoke the [`configuration`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#configuration) function to retrieve the configuration details for the pre-deployed contracts and services needed for local CCIP simulations:
```solidity
(
uint64 chainSelector,
IRouterClient sourceRouter,
,
,
LinkToken linkToken,
BurnMintERC677Helper ccipBnM,
) = ccipLocalSimulator.configuration();
```
**Note**: The [`configuration`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#configuration) function also returns the destination router, WETH9, and CCIP-LnM contracts, but we are not using them in these test cases. Hence, there are commas in the return values.
3. Initialize the sender and receiver accounts:
```solidity
alice = makeAddr("alice")
bob = makeAddr("bob")
```
4. All the variables are stored in the contract state for use in the test cases.
### Prepare scenario (helper function)
The `prepareScenario()` function is invoked at the beginning of each test case. It performs the following actions:
1. Request CCIP-BnM tokens for Alice (sender):
```solidity
ccipBnMToken.drip(alice);
```
2. Approve the source router to spend tokens on behalf of Alice (sender):
```solidity
amountToSend = 100;
ccipBnMToken.approve(address(router), amountToSend);
```
3. Create an array [Client.EVMTokenAmount](/ccip/api-reference/evm/v1.6.2/client#evmtokenamount)[] to specify the token transfer details. This array and the amount to send are returned by the prepareScenario() function for use in the calling test case:
```solidity
tokensToSendDetails = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenToSendDetails =
Client.EVMTokenAmount({token: address(ccipBnMToken), amount: amountToSend});
tokensToSendDetails[0] = tokenToSendDetails;
```
### Test case 1: Transfer with LINK fees
The `test_transferTokensFromEoaToEoaPayFeesInLink` function tests the transfer of tokens between two externally owned accounts (EOA) while paying fees in LINK.
Here are the steps involved in this test case:
1. Invoke the `prepareScenario()` function to set up the necessary variables:
```solidity
(Client.EVMTokenAmount[] memory tokensToSendDetails, uint256 amountToSend) = prepareScenario();
```
2. Record the initial token balances for Alice (sender) and Bob (receiver):
```solidity
uint256 balanceOfAliceBefore = ccipBnMToken.balanceOf(alice);
uint256 balanceOfBobBefore = ccipBnMToken.balanceOf(bob);
```
3. Begin impersonating Alice (sender) to perform the subsequent actions:
```solidity
vm.startPrank(alice);
```
4. Request 5 LINK tokens from the CCIP Local Simulator faucet for Alice (sender):
```solidity
ccipLocalSimulator.requestLinkFromFaucet(alice, 5 ether);
```
5. Construct the [Client.EVM2AnyMessage](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) structure with the receiver, token amounts, and other necessary details.
- Set the `data` parameter to an empty string because you are not sending any arbitrary data, only tokens.
- In `extraArgs`, set the gas limit to `0`. This gas limit is for execution of receiver logic, which doesn't apply here because you're sending tokens to an EOA.
```solidity
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(bob),
data: abi.encode(""),
tokenAmounts: tokensToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})),
feeToken: address(linkToken)
});
```
6. Calculate the required fees for the transfer and approve the router to spend LINK tokens for these fees:
```solidity
uint256 fees = router.getFee(destinationChainSelector, message);
linkToken.approve(address(router), fees);
```
7. Send the CCIP transfer request to the router:
```solidity
router.ccipSend(destinationChainSelector, message);
```
8. Stop impersonating (sender):
```solidity
vm.stopPrank();
```
9. Record the final token balances for Alice (sender) and Bob (receiver):
```solidity
uint256 balanceOfAliceAfter = ccipBnMToken.balanceOf(alice);
uint256 balanceOfBobAfter = ccipBnMToken.balanceOf(bob);
```
10. Verify that Alice's balance has decreased by the amount sent and Bob's balance has increased by the same amount:
```solidity
assertEq(balanceOfAliceAfter, balanceOfAliceBefore - amountToSend);
assertEq(balanceOfBobAfter, balanceOfBobBefore + amountToSend);
```
### Test case 2: Transfer with native gas fees
The `test_transferTokensFromEoaToEoaPayFeesInNative` function tests the transfer of tokens between two externally owned accounts (EOA) while paying fees in native gas.
Here are the steps involved in this test case:
1. Invoke the `prepareScenario()` function to set up the necessary variables. (This step is the same as in the previous test case.)
2. Record the initial token balances of Alice (sender) and Bob (receiver). (This step is the same as in the previous test case.)
3. Begin impersonating Alice (sender) and provide her with native gas to pay for the fees:
```solidity
vm.startPrank(alice);
deal(alice, 5 ether);
```
4. Construct the [Client.EVM2AnyMessage](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) structure. This step is the same as in the previous test case.
The main difference is that the `feeToken` is set with `address(0)` to indicate that the fees are paid in native gas:
```solidity
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(bob),
data: abi.encode(""),
tokenAmounts: tokensToSendDetails,
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 0})),
feeToken: address(0)
});
```
5. Calculate the required fees for the transfer and send the CCIP transfer request along with the necessary native gas:
```solidity
uint256 fees = router.getFee(destinationChainSelector, message);
router.ccipSend{value: fees}(destinationChainSelector, message);
```
6. Stop impersonating Alice (sender) and verify the token balances for Alice (sender) and Bob (receiver). (This step is the same as in the previous test case.)
## Next steps
For more advanced scenarios, please refer to other test files in the `./test/no-fork` directory. To learn how to use Chainlink Local in forked environments, refer to
the guide on [Using the CCIP Local Simulator in your Foundry project with forked environments](/chainlink-local/build/ccip/foundry/local-simulator-fork).
---
# Using Chainlink Local to Test CCIP in Your Hardhat Project
Source: https://docs.chain.link/chainlink-local/build/ccip/hardhat
There are two comprehensive guides to help you test Chainlink CCIP in your Hardhat project using Chainlink Local - one for each mode (with and without forking).
We recommend starting with the first guide (without forking) for initial development before advancing to the guide that demonstrates how to test in forked environments.
## Guides
### Using CCIP Local Simulator in your Hardhat project
This [guide](/chainlink-local/build/ccip/hardhat/local-simulator) helps you set up and run CCIP in a localhost environment within your Hardhat project. It provides step-by-step instructions on :
- Cloning the
[CCIP Hardhat Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-hardhat)
- Installing necessary dependencies
- Running tests for token transfers between two accounts
Using the local simulator without forking is ideal for initial development and testing in an isolated environment.
### Using CCIP Local Simulator in your Hardhat project with forked environments
This [guide](/chainlink-local/build/ccip/hardhat/local-simulator-fork) extends your testing capabilities by demonstrating how to use the CCIP Local Simulator in a forked environment. It includes instructions on:
- Setting up forks of real blockchain networks (e.g., *Arbitrum Sepolia* and *Ethereum Sepolia*)
- Writing test cases
- Running tests for cross-chain token transfers between two accounts
Using the local simulator with forked environments is ideal for testing in a more realistic yet controlled setting.
---
# Using CCIP Local Simulator in Your Hardhat Project - Forked Environments
Source: https://docs.chain.link/chainlink-local/build/ccip/hardhat/local-simulator-fork
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](https://github.com/smartcontractkit/ccip-starter-kit-hardhat).
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](/chainlink-local/build/ccip/hardhat/local-simulator).
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](https://www.npmjs.com/package/@chainlink/env-enc).
Use this tool to encrypt your environment variables at rest.
1. Set an encryption password for your environment variables.
```shell
npx env-enc set-pw
```
2. Run `npx env-enc set` to configure a `.env.enc` file 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](https://www.alchemy.com/) or [Infura](https://www.infura.io/) 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](https://www.alchemy.com/), [Infura](https://www.infura.io/), 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](#examine-the-code) section.
In your terminal, run the following command to execute the test:
```shell
npx hardhat test test/fork/Example1.spec.ts
```
Example output:
```text
$ 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:
1. Initialize the sender and receiver accounts:
```typescript
const [alice, bob] = await hre.ethers.getSigners()
```
2. Set the source and destination chains:
```typescript
const [source, destination] = ["ethereumSepolia", "arbitrumSepolia"]
```
3. Retrieve the necessary addresses and configurations (such as the LINK token address, source router address, and so on).
4. Fork the source network:
```typescript
await hre.network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: getProviderRpcUrl(source),
},
},
],
})
```
5. Connect to the source router and CCIP-BnM contracts.
6. Call `sourceCCIPBnM.drip` to request CCIP-BnM tokens for Alice (sender).
7. Approve the source router to spend tokens on behalf of Alice (sender).
8. Construct the `Client.EVM2AnyMessage` structure. This step is similar to the non fork example.
9. Call the source router to estimate the fees.
10. Call the [`requestLinkFromFaucet`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork-js#requestlinkfromthefaucet)
function to request LINK tokens for Alice (sender).
11. Connect to the LINK contract and approve the LINK token for paying CCIP fees.
12. Estimate Alice (sender) balance before the transfer.
13. Call the source router to send the CCIP request.
14. Wait for the transaction to be included in a block.
15. Call the [`getEvm2EvmMessage`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork-js#getevm2evmmessage)
function to parse the transaction receipt and extract the `CCIPSendRequested` event and then decodes it to an object.
16. Verify that Alice's balance has decreased by the amount sent.
17. Fork and switch to the destination network:
```typescript
await hre.network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: getProviderRpcUrl(destination),
},
},
],
})
```
18. Call the [`routeMessage`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator-fork-js#routemessage) function
to route the message to the destination router.
19. Connect to the CCIP-BnM contract using the CCIP-BnM destination address.
20. Verify that Bob's balance has increased by the amount sent.
---
# Using CCIP local simulator in your Hardhat project
Source: https://docs.chain.link/chainlink-local/build/ccip/hardhat/local-simulator
You can use Chainlink Local to run CCIP in a localhost environment within your Hardhat project. To get started quickly, you will use the
[CCIP Hardhat Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-hardhat). This project is a Hardhat boilerplate that includes the
Chainlink Local package and several CCIP examples.
## Prerequisites
1. In a terminal, clone the [CCIP Hardhat Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-hardhat) repository and change directories:
```shell
git clone https://github.com/smartcontractkit/ccip-starter-kit-hardhat && \
cd ./ccip-starter-kit-hardhat/
```
2. Install the [Chainlink Local](https://github.com/smartcontractkit/chainlink-local) package and other required packages:
```shell
npm install
```
3. Compile the contracts:
```shell
npm run compile
```
## Test tokens transfers
You will run a test to transfer tokens between two accounts. The test file `Example1.spec.ts` is located in the `./test/no-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.
For a detailed explanation of the test file, refer to the [Examine the code](#examine-the-code) section.
In your terminal, run the following command to execute the test:
```shell
npx hardhat test test/no-fork/Example1.spec.ts
```
Example output:
```text
$ npx hardhat test test/no-fork/Example1.spec.ts
Example 1
✔ Should transfer CCIP test tokens from EOA to EOA (1057ms)
1 passing (1s)
```
## Examine the code
### Setup
To transfer tokens using CCIP, we need the following:
- Destination chain selector
- Source CCIP router
- LINK token for paying CCIP fees
- A test token contract (such as CCIP-BnM)
- A sender account (Alice)
- A receiver account (Bob)
The `deployFixture` function is used to set up the initial state for the tests. This function deploys the CCIP local simulator contract and initializes
the sender and receiver accounts.
1. Initialize the [CCIP local simulator](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#cciplocalsimulator) contract:
```typescript
const ccipLocalSimulatorFactory = await hre.ethers.getContractFactory("CCIPLocalSimulator")
const ccipLocalSimulator: CCIPLocalSimulator = await ccipLocalSimulatorFactory.deploy()
```
2. Initialize the sender and receiver accounts:
```typescript
const [alice, bob] = await hre.ethers.getSigners()
```
### Test case: Transfer with LINK fees
The `it("Should transfer CCIP test tokens from EOA to EOA")` function tests the transfer of tokens between two externally owned accounts (EOA)
while paying fees in LINK. Here are the steps involved in this test case:
1. Invoke the `deployFixture` function to set up the necessary variables:
```typescript
const { ccipLocalSimulator, alice, bob } = await loadFixture(deployFixture)
```
2. Invoke the [`configuration`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#configuration) function to retrieve the configuration details
for the pre-deployed contracts and services needed for local CCIP simulations.
3. Connect to the source router and CCIP-BnM contracts.
4. Call `ccipBnM.drip` to request CCIP-BnM tokens for Alice (sender).
5. Create an array `Client.EVMTokenAmount[]` to specify the token transfer details:
```typescript
const tokenAmounts = [
{
token: config.ccipBnM_,
amount: amountToSend,
},
]
```
6. Construct the [Client.EVM2AnyMessage](/ccip/api-reference/evm/v1.6.2/client#evm2anymessage) structure with the receiver, token amounts, and other necessary details.
- Use an empty string for the `data` parameter because you are not sending any arbitrary data (only tokens).
- Set `gasLimit` to `0` because you are sending tokens to an EOA, which means you do not expect any execution of receiver logic (and therefore do not need gas for that).
```typescript
const gasLimit = 0
const functionSelector = id("CCIP EVMExtraArgsV1").slice(0, 10)
const defaultAbiCoder = AbiCoder.defaultAbiCoder()
const extraArgs = defaultAbiCoder.encode(["uint256"], [gasLimit])
const encodedExtraArgs = `${functionSelector}${extraArgs.slice(2)}`
const message = {
receiver: defaultAbiCoder.encode(["address"], [bob.address]),
data: defaultAbiCoder.encode(["string"], [""]), // no data
tokenAmounts: tokenAmounts,
feeToken: config.linkToken_,
extraArgs: encodedExtraArgs,
}
```
7. Calculate the required fees for the transfer and approve the router to spend LINK tokens for these fees:
```typescript
const fee = await mockCcipRouter.getFee(config.chainSelector_, message)
await linkToken.connect(alice).approve(mockCcipRouterAddress, fee)
```
8. Send the CCIP transfer request to the router:
```typescript
await mockCcipRouter.connect(alice).ccipSend(config.chainSelector_, message)
```
9. Verify that Alice's balance has decreased by the amount sent and Bob's balance has increased by the same amount:
```typescript
expect(await ccipBnM.balanceOf(alice.address)).to.deep.equal(ONE_ETHER - amountToSend)
expect(await ccipBnM.balanceOf(bob.address)).to.deep.equal(amountToSend)
```
## Next steps
For more advanced scenarios, please refer to other test files in the `./test/no-fork` directory. To learn how to use Chainlink local in forked environments,
refer to the guide on [Using CCIP Local Simulator in your Hardhat project with forked environments](/chainlink-local/build/ccip/hardhat/local-simulator-fork).
---
# Using Chainlink Local to Test CCIP in RemixIDE
Source: https://docs.chain.link/chainlink-local/build/ccip/remix
There is one comprehensive guide to help you test Chainlink CCIP in Remix IDE using Chainlink Local - without forking. Testing in forked environments is
currently supported **only** for Foundry and Hardhat.
## Guides
### Using CCIP Local Simulator in Remix IDE
This [guide](/chainlink-local/build/ccip/remix/local-simulator) helps you to test [Chainlink CCIP getting started guide](/ccip/getting-started) locally in Remix IDE. You will:
- Deploy a CCIP sender contract
- Deploy CCIP receiver contract
- Use the local simulator to send data from the sender contract to the receiver contract
---
# Using the CCIP local simulator in Remix IDE
Source: https://docs.chain.link/chainlink-local/build/ccip/remix/local-simulator
In this guide, you will test the [Chainlink CCIP getting started guide](/ccip/getting-started) locally in Remix IDE. You will:
- Deploy a CCIP sender contract
- Deploy a CCIP receiver contract
- Use the local simulator to send data from the sender contract to the receiver contract
## Prerequisites
[Remix IDE](https://remix.ethereum.org/) is an online development environment that allows you to write, deploy, and test smart contracts. By default Remix IDE
does not persist the files that you open from an external source. To save files, you will need to manually create a workspace and copy the files into the workspace.
1. Open the [Remix IDE](https://remix.ethereum.org/) in your browser.
2. Create a [new workspace](https://remix-ide.readthedocs.io/en/latest/file_explorer.html#new-workspace).
3. Copy the content of the [Test CCIP Local Simulator contract](https://docs.chain.link/samples/CCIP/TestCCIPLocalSimulator.sol) into a new file in the workspace.
4. Copy the content of the [Sender contract](https://docs.chain.link/samples/CCIP/Sender.sol) into a new file in the workspace.
5. Copy the content of the [Receiver contract](https://docs.chain.link/samples/CCIP/Receiver.sol) into a new file in the workspace.
At this point, you should have three files in your workspace:
- **TestCCIPLocalSimulator.sol**: The file imports the Chainlink CCIP local simulator contract.
- **Sender.sol**: The file contains the Sender contract that interacts with CCIP to send data to the Receiver contract.
- **Receiver.sol**: The file contains the Receiver contract that receives data from the Sender contract.
## Deploy the contracts
1. Compile the contracts.
2. Under the **Deploy & Run Transactions** tab, make sure *Remix VM* is selected in the **Environment** drop-down list. Remix will use a sandbox blockchain in the
browser to deploy the contracts.
3. Deploy the [CCIP local simulator](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#cciplocalsimulator):
1. Select the `TestCCIPLocalSimulator.sol` file in the file explorer.
2. In the *Contract* drop-down list, select `CCIPLocalSimulator`.
3. Click the **Deploy** button.
4. The `CCIPLocalSimulator` is shown in the **Deployed Contracts** section.
5. In the list of functions, click the [`configuration`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#configuration) function to retrieve the
configuration details for the pre-deployed contracts and services needed for local CCIP simulations:
4. You will interact with the LINK token contract to fund the sender contract with LINK tokens. The LINK token contract is pre-deployed in the local simulator
configuration, so you can simply load the LINK token contract instance:
1. Select `LinkToken` in the *Contract* drop-down list.
2. Fill in the *At Address* field with the address of the `LINK` token contract from the `CCIPLocalSimulator` configuration.
3. Click the **At Address** button.
The `LinkToken` contract is shown in the **Deployed Contracts** section.
5. Deploy the `Sender.sol` contract:
1. Select the `Sender.sol` file in the file explorer.
2. In the *Contract* drop-down list, select `Sender`.
3. Under the *Deploy* section, fill in the constructor parameters:
- `_router`: The address of the `sourceRouter` contract from the `CCIPLocalSimulator` configuration.
- `_link`: The address of the `LINK` token contract from the `CCIPLocalSimulator` configuration.
4. Click the **Deploy** button.
The `Sender` contract is shown in the **Deployed Contracts** section.
6. Deploy the `Receiver.sol` contract:
1. Select the `Receiver.sol` file in the file explorer.
2. In the *Contract* drop-down list, select `Receiver`.
3. Under the *Deploy* section, fill in the constructor parameters:
- `_router`: The address of the `destinationRouter` contract from the `CCIPLocalSimulator` configuration.
4. Click the **Deploy** button.
The `Receiver` contract is shown in the **Deployed Contracts** section.
## Transfer data from the sender to the receiver
1. Fund the sender contract with LINK tokens to pay for CCIP fees:
1. Copy the address of the `Sender` contract from the **Deployed Contracts** section.
2. In the `CCIPLocalSimulator` contract, fill in the [`requestLinkFromFaucet`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#requestlinkfromfaucet)
function with the following inputs:
- `to`: The address of the `Sender` contract.
- `amount`: The amount of LINK tokens to transfer. For instance: 1000000000000000000.
3. Click the **Transact** button.
2. Send data from the sender contract to the receiver contract:
1. Copy the address of the `Receiver` contract from the **Deployed Contracts** section.
2. In the `Sender` contract, fill in the `sendMessage` function with:
- `destinationChainSelector`: The destination chain selector. You can find it in the `CCIPLocalSimulator` configuration.
- `receiver`: The address of the `Receiver` contract.
- `text`: The text to send. For instance Hello!.
3. Remix IDE fails to estimate the gas properly for the `sendMessage` function. To work around this, you need to set the gas limit manually
to 3000000:
4. Click the **Transact** button.
3. Check the receiver contract to verify the data transfer:
1. In the `Receiver` contract, click on the `getLastReceivedMessageDetails` function.
2. The `getLastReceivedMessageDetails` function returns the text sent from the `Sender` contract:
## Next steps
You have successfully tested the CCIP getting started guide within a few minutes using Remix IDE. Testing locally is useful to debug your contracts and fix any
issues before testing them on testnets, saving you time and resources. As an exercise, you can try any of the
[CCIP guides](/ccip/tutorials) using the local simulator in Remix IDE.
---
# Chainlink Local
Source: https://docs.chain.link/chainlink-local
**Chainlink Local** is an installable package that allows you to run Chainlink services locally. You can import Chainlink Local into your preferred local development environment,
such as Foundry projects, Hardhat scripts, or the Remix IDE. Chainlink Local enables rapid exploration, prototyping, local development, and iteration with Chainlink services
before transitioning to a testnet.
For instance, you can use Chainlink Local to execute CCIP token transfers and arbitrary messages on a local Hardhat or Anvil (Foundry) development node. Chainlink Local also supports forked nodes,
allowing you to work with multiple locally running blockchain networks using historical network states. User contracts tested with Chainlink Local can be deployed to test networks without modifications,
ensuring a seamless transition from local development to live testnets.
**Key Features of Chainlink Local:**
- **Local Simulation:** Run Chainlink services on a local development blockchain node, enabling fast and efficient testing and prototyping.
- **Forked Networks:** Work with deployed Chainlink contracts using one or multiple forked networks, providing a more realistic testing environment.
- **Seamless Integration:** Integrate with Foundry, Hardhat, and Remix IDE for a streamlined development process.
To get started testing CCIP with Chainlink Local, follow the installation and setup steps in the CCIP guides for [Foundry](/chainlink-local/build/ccip/foundry), [Hardhat](/chainlink-local/build/ccip/hardhat), or [Remix IDE](/chainlink-local/build/ccip/remix).
---
# Chainlink Local Architecture
Source: https://docs.chain.link/chainlink-local/learn/architecture
[Chainlink Local](https://github.com/smartcontractkit/chainlink-local) is a package that you can import into your development environment
(such as Hardhat, Foundry, or Remix IDE) to use Chainlink services locally. It supports two primary modes:
- Local testing without forking.
- Local testing with forking.
This enables you to test Chainlink smart contracts and services, such as CCIP, either in a clean local blockchain state or by using a forked state from a live blockchain.
After testing with Chainlink Local, you can deploy your contracts to test networks without any modifications.
## Local testing without forking
In this mode, you work with mock contracts on a locally running development blockchain node, such as Hardhat, Anvil (Foundry), or Remix VM.
**How it works**: Import the [Chainlink Local](https://www.npmjs.com/package/@chainlink/local) package and deploy a set of Chainlink smart contracts to a blank Hardhat/Anvil network EVM state as part of your tests.
You can then deploy your smart contracts and start testing with them.
## Local testing with forking
In this mode, you work with deployed Chainlink smart contracts using one or more forked blockchains. This setup provides a more realistic testing environment by incorporating
the state of a live blockchain.
**How it works**: In your test scripts, you fork one or more blockchains to create locally running blockchain network(s). Chainlink Local provides the necessary interfaces
to interact with the forked Chainlink services and utilities for testing in a forked environment. For example, in CCIP, Chainlink Local provides a utility function that helps
you switch from one fork (as the source chain) to another fork (as the destination chain).
---
# Contributing to Chainlink Local
Source: https://docs.chain.link/chainlink-local/learn/contributing
Contributions to Chainlink Local are welcome! These contributions can include bug fixes, new features, and documentation updates. This guide provides a list of repositories you can contribute to:
- [Chainlink Local](https://github.com/smartcontractkit/chainlink-local): The main repository for Chainlink Local.
- [CCIP Foundry Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-foundry): A Foundry starter kit for Chainlink CCIP. It uses Chainlink Local test different scenarios on a local environment with and without forking.
- [CCIP Hardhat Starter Kit](https://github.com/smartcontractkit/ccip-starter-kit-hardhat): A Hardhat starter kit for Chainlink CCIP. It uses Chainlink Local test different scenarios on a local environment with and without forking.
- [Chainlink Technical Documentation](https://github.com/smartcontractkit/documentation): You are welcome to write new guides or update existing guides in the Chainlink documentation.