├── .github
└── workflows
│ ├── publish-beta-release.yml
│ └── publish.yml
├── .gitignore
├── .gitmodules
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── abi
├── EVM2EVMOffRamp.json
├── EVM2EVMOnRamp.json
├── LinkToken.json
└── Router.json
├── api_reference
├── index.mdx
├── javascript
│ ├── CCIPLocalSimulatorFork.mdx
│ └── index.mdx
└── solidity
│ ├── ccip
│ ├── BurnMintERC677Helper.mdx
│ ├── CCIPLocalSimulator.mdx
│ ├── CCIPLocalSimulatorFork.mdx
│ ├── Register.mdx
│ └── index.mdx
│ ├── data-feeds
│ ├── MockOffchainAggregator.mdx
│ ├── MockV3Aggregator.mdx
│ ├── index.mdx
│ └── interfaces
│ │ ├── AggregatorInterface.mdx
│ │ ├── AggregatorV2V3Interface.mdx
│ │ ├── AggregatorV3Interface.mdx
│ │ └── index.mdx
│ ├── data-streams
│ ├── DataStreamsLocalSimulator.mdx
│ ├── DataStreamsLocalSimulatorFork.mdx
│ ├── MockFeeManager.mdx
│ ├── MockReportGenerator.mdx
│ ├── MockRewardManager.mdx
│ ├── MockVerifier.mdx
│ ├── MockVerifierProxy.mdx
│ ├── Register.mdx
│ ├── ReportVersions.mdx
│ ├── index.mdx
│ └── interfaces
│ │ ├── IRewardManager.mdx
│ │ ├── IVerifier.mdx
│ │ ├── IVerifierFeeManager.mdx
│ │ └── index.mdx
│ ├── index.mdx
│ └── shared
│ ├── LinkToken.mdx
│ ├── WETH9.mdx
│ └── index.mdx
├── assets
└── thumbnail.png
├── foundry.toml
├── hardhat.config.ts
├── helper_doc
├── generate-index-files.ts
└── generate-jsdoc.ts
├── package-lock.json
├── package.json
├── scripts
├── CCIPLocalSimulatorFork.js
├── data-streams
│ ├── DataStreamsLocalSimulatorFork.js
│ ├── MockReportGenerator.js
│ └── ReportVersions.js
└── examples
│ ├── DataStreamsConsumerFork.ts
│ ├── UnsafeTokenAndDataTransfer.ts
│ └── UnsafeTokenAndDataTransferFork.ts
├── src
├── ccip
│ ├── BurnMintERC677Helper.sol
│ ├── CCIPLocalSimulator.sol
│ ├── CCIPLocalSimulatorFork.sol
│ └── Register.sol
├── data-feeds
│ ├── MockOffchainAggregator.sol
│ ├── MockV3Aggregator.sol
│ └── interfaces
│ │ ├── AggregatorInterface.sol
│ │ ├── AggregatorV2V3Interface.sol
│ │ └── AggregatorV3Interface.sol
├── data-streams
│ ├── DataStreamsLocalSimulator.sol
│ ├── DataStreamsLocalSimulatorFork.sol
│ ├── MockFeeManager.sol
│ ├── MockReportGenerator.sol
│ ├── MockRewardManager.sol
│ ├── MockVerifier.sol
│ ├── MockVerifierProxy.sol
│ ├── Register.sol
│ ├── ReportVersions.sol
│ └── interfaces
│ │ ├── IRewardManager.sol
│ │ ├── IVerifier.sol
│ │ └── IVerifierFeeManager.sol
├── shared
│ ├── LinkToken.sol
│ └── WETH9.sol
└── test
│ ├── ccip
│ ├── BasicTokenSender.sol
│ ├── CCIPReceiver_Unsafe.sol
│ ├── CCIPSender_Unsafe.sol
│ ├── Ping.sol
│ ├── Pong.sol
│ ├── ProgrammableDefensiveTokenTransfers.sol
│ ├── ProgrammableTokenTransfers.sol
│ └── TokenTransferor.sol
│ ├── data-feeds
│ └── BasicDataConsumerV3.sol
│ └── data-streams
│ ├── ChainlinkDataStreamProvider.sol
│ ├── ClientReportsVerifier.sol
│ └── ERC7412Compatible.sol
├── test
├── e2e
│ ├── ccip
│ │ ├── CCIPv1_5ForkBurnMintPoolFork.t.sol
│ │ ├── CCIPv1_5LockReleasePoolFork.t.sol
│ │ ├── PingPongFork.t.sol
│ │ └── TokenTransferorFork.t.sol
│ ├── data-feeds
│ │ └── BasicDataConsumerV3.t.sol
│ └── data-streams
│ │ └── DataStreamsConsumerFork.t.sol
├── smoke
│ ├── ccip
│ │ ├── PayWithNative.t.sol
│ │ ├── PingPong.t.sol
│ │ ├── ProgrammableDefensiveTokenTransfers.t.sol
│ │ ├── ProgrammableTokenTransfers.t.sol
│ │ ├── TokenTransferor.t.sol
│ │ ├── UnsafeTokenAndDataTransfer.spec.ts
│ │ └── UnsafeTokenAndDataTransfer.t.sol
│ ├── data-feeds
│ │ ├── BasicDataConsumerV3.spec.ts
│ │ └── BasicDataConsumerV3.t.sol
│ └── data-streams
│ │ ├── ChainlinkDataStreamProvider.t.sol
│ │ ├── ClientReportsVerifier.spec.ts
│ │ ├── ClientReportsVerifier.t.sol
│ │ └── ERC7412Compatible.t.sol
└── unit
│ ├── ccip
│ └── CCIPLocalSimulatorUnit.t.sol
│ └── data-feeds
│ └── DataFeedsUnit.t.sol
└── tsconfig.json
/.github/workflows/publish-beta-release.yml:
--------------------------------------------------------------------------------
1 | name: Manual NPM Publish (Beta Release)
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | publish:
8 | runs-on: ubuntu-latest
9 | environment: publish
10 |
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 |
15 | - name: Use Node.js
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: "20.x"
19 | registry-url: "https://registry.npmjs.org"
20 | always-auth: true
21 |
22 | - name: Setup npm authentication
23 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
24 |
25 | - name: Install dependencies
26 | run: npm ci
27 |
28 | - name: Publish
29 | run: npm publish --tag beta
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Manual NPM Publish
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | publish:
8 | runs-on: ubuntu-latest
9 | environment: publish
10 |
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 |
15 | - name: Use Node.js
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: "20.x"
19 | registry-url: "https://registry.npmjs.org"
20 | always-auth: true
21 |
22 | - name: Setup npm authentication
23 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
24 |
25 | - name: Install dependencies
26 | run: npm ci
27 |
28 | - name: Publish
29 | run: npm publish
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | .env.enc
4 | .DS_Store
5 |
6 | # Foundry files
7 | cache_forge/
8 | out/
9 |
10 | # Hardhat files
11 | /cache
12 | /artifacts
13 |
14 | # TypeChain files
15 | /typechain
16 | /typechain-types
17 |
18 | # solidity-coverage files
19 | /coverage
20 | /coverage.json
21 |
22 | # Ignores development broadcast logs
23 | !/broadcast
24 | /broadcast/*/31337/
25 | /broadcast/**/dry-run/
26 |
27 | # Docs
28 | docs/
29 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/forge-std"]
2 | path = lib/forge-std
3 | url = https://github.com/foundry-rs/forge-std
4 | [submodule "lib/ccip"]
5 | path = lib/ccip
6 | url = https://github.com/smartcontractkit/ccip
7 | [submodule "lib/chainlink-brownie-contracts"]
8 | path = lib/chainlink-brownie-contracts
9 | url = https://github.com/smartcontractkit/chainlink-brownie-contracts
10 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": true,
7 | "trailingComma": "es5",
8 | "bracketSpacing": true,
9 | "jsxBracketSameLine": false,
10 | "arrowParens": "avoid",
11 | "proseWrap": "always"
12 | }
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 SmartContract ChainLink Limited SEZC
2 |
3 | Portions of this software are licensed as follows:
4 |
5 | The MIT License (MIT)
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
25 |
26 | *All content residing under (1) “src/ccip/BurnMintERC677Helper.sol”; (2)
27 | “src/ccip/CCIPLocalSimulator.sol”; (3) “src/ccip/CCIPLocalSimulatorFork.sol”; (4) "src/ccip/Register.sol" are licensed
28 | under “Business Source License 1.1” with a Change Date of May 23, 2027 and
29 | Change License to “MIT License”
30 |
31 | * Content outside of the above mentioned directories or restrictions
32 | above is available under the "MIT" license as defined above.
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Chainlink Local
2 |
3 | Chainlink Local is an installable dependency. It provides a tool (the Chainlink Local Simulator) that developers import into their Foundry or Hardhat or Remix projects. This tool runs [Chainlink CCIP](https://docs.chain.link/ccip) locally which means developers can rapidly explore, prototype and iterate CCIP dApps off-chain in a local environment, and move to testnet only when they're ready to test in a live environment.
4 |
5 | The package exposes a set of smart contracts and scripts with which you build, deploy and execute CCIP token transfers and arbitrary messages on a local Remix, Hardhat or Anvil (Foundry) development node. Chainlink Local also supports forked nodes.
6 |
7 | User Contracts tested with Chainlink Local can be deployed to test networks without any modifications (assuming network specific contract addresses such as Router contracts and LINK token addresses are passed in via a constructor).
8 |
9 | To view more detailed documentation and more examples, visit the [Chainlink Local Documentation](https://docs.chain.link/chainlink-local).
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ### Installation
18 |
19 | Install the package by running:
20 |
21 | #### Foundry (git)
22 |
23 | ```
24 | forge install smartcontractkit/chainlink-local
25 | ```
26 |
27 | and then set remappings to: `@chainlink/local/=lib/chainlink-local/` in either `remappings.txt` or `foundry.toml` file
28 |
29 | #### Foundry (soldeer)
30 |
31 | ```
32 | forge soldeer install chainlink-local~v0.2.4-beta https://github.com/smartcontractkit/chainlink-local.git
33 | ```
34 | Replace `v0.2.4-beta` with your desired version number.
35 |
36 | #### Hardhat (npm)
37 |
38 | ```
39 | npm install @chainlink/local
40 | ```
41 |
42 | #### Remix IDE
43 |
44 | ```solidity
45 | import "https://github.com/smartcontractkit/chainlink-local/blob/main/src/ccip/CCIPLocalSimulator.sol";
46 | ```
47 |
48 | Once you have installed CCIP Local, you are now ready to start using it with your project.
49 |
50 | ### Usage
51 |
52 | Import `CCIPLocalSimulator.sol` inside your tests or scripts, for example:
53 |
54 | ```solidity
55 | // test/demo.t.sol
56 |
57 | pragma solidity ^0.8.19;
58 |
59 | import {Test, console2} from "forge-std/Test.sol";
60 | import {IRouterClient, WETH9, LinkToken, BurnMintERC677Helper} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
61 | import {CCIPLocalSimulator} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
62 |
63 | contract Demo is Test {
64 | CCIPLocalSimulator public ccipLocalSimulator;
65 |
66 | function setUp() public {
67 | ccipLocalSimulator = new CCIPLocalSimulator();
68 |
69 | (
70 | uint64 chainSelector,
71 | IRouterClient sourceRouter,
72 | IRouterClient destinationRouter,
73 | WETH9 wrappedNative,
74 | LinkToken linkToken,
75 | BurnMintERC677Helper ccipBnM,
76 | BurnMintERC677Helper ccipLnM
77 | ) = ccipLocalSimulator.configuration();
78 |
79 |
80 | ccipLocalSimulator.requestLinkFromFaucet(receiver, amount);
81 | }
82 |
83 | }
84 | ```
85 |
86 | ### Learn more
87 |
88 | To view detailed documentation and more examples, visit the [Chainlink Local Documentation](https://docs.chain.link/chainlink-local).
89 |
90 | > **Note**
91 | >
92 | > _This tutorial represents an educational example to use a Chainlink system, product, or service and is provided to demonstrate how to interact with Chainlink’s systems, products, and services to integrate them into your own. This template is provided “AS IS” and “AS AVAILABLE” without warranties of any kind, it has not been audited, and it may be missing key checks or error handling to make the usage of the system, product or service more clear. Do not use the code in this example in a production environment without completing your own audits and application of best practices. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs that are generated due to errors in code._
93 |
--------------------------------------------------------------------------------
/api_reference/index.mdx:
--------------------------------------------------------------------------------
1 | # API Reference
2 |
3 | - [javascript](javascript/index.mdx)
4 | - [solidity](solidity/index.mdx)
5 |
--------------------------------------------------------------------------------
/api_reference/javascript/CCIPLocalSimulatorFork.mdx:
--------------------------------------------------------------------------------
1 | ## Functions
2 |
3 |
4 | -
5 |
6 | requestLinkFromTheFaucet(linkAddress, to, amount)
7 | {' '}
8 | ⇒
Promise.<string>
9 |
10 | -
11 |
Requests LINK tokens from the faucet and returns the transaction hash
12 |
13 | -
14 | getEvm2EvmMessage(receipt) ⇒{' '}
15 |
16 | Evm2EvmMessage
17 |
{' '}
18 | | null
19 |
20 | -
21 |
22 | Parses a transaction receipt to extract the sent message Scans through
23 | transaction logs to find a CCIPSendRequested
event and then
24 | decodes it to an object
25 |
26 |
27 | -
28 | routeMessage(routerAddress, evm2EvmMessage) ⇒{' '}
29 |
Promise.<void>
30 |
31 | -
32 |
33 | Routes the sent message from the source network on the destination
34 | (current) network
35 |
36 |
37 |
38 |
39 | ## Typedefs
40 |
41 |
42 | -
43 | Evm2EvmMessage :
Object
44 |
45 |
46 |
47 |
48 |
49 |
50 | ## requestLinkFromTheFaucet(linkAddress, to, amount) ⇒ Promise.<string>
51 |
52 | Requests LINK tokens from the faucet and returns the transaction hash
53 |
54 | **Kind**: global function
55 | **Returns**: Promise.<string>
- Promise resolving to the
56 | transaction hash of the fund transfer
57 |
58 | | Param | Type | Description |
59 | | ----------- | ------------------- | ------------------------------------------------------- |
60 | | linkAddress | string
| The address of the LINK contract on the current network |
61 | | to | string
| The address to send LINK to |
62 | | amount | bigint
| The amount of LINK to request |
63 |
64 |
65 |
66 | ## getEvm2EvmMessage(receipt) ⇒ [Evm2EvmMessage
](#Evm2EvmMessage) \| null
67 |
68 | Parses a transaction receipt to extract the sent message Scans through
69 | transaction logs to find a `CCIPSendRequested` event and then decodes it to an
70 | object
71 |
72 | **Kind**: global function
73 | **Returns**: [Evm2EvmMessage
](#Evm2EvmMessage) \|
74 | null
- Returns either the sent message or null if provided receipt
75 | does not contain `CCIPSendRequested` log
76 |
77 | | Param | Type | Description |
78 | | ------- | ------------------- | ------------------------------------------------ |
79 | | receipt | object
| The transaction receipt from the `ccipSend` call |
80 |
81 |
82 |
83 | ## routeMessage(routerAddress, evm2EvmMessage) ⇒ Promise.<void>
84 |
85 | Routes the sent message from the source network on the destination (current)
86 | network
87 |
88 | **Kind**: global function
89 | **Returns**: Promise.<void>
- Either resolves with no value
90 | if the message is successfully routed, or reverts
91 | **Throws**:
92 |
93 | - Error
Fails if no off-ramp matches the message's source chain
94 | selector or if calling `router.getOffRamps()`
95 |
96 | | Param | Type | Description |
97 | | -------------- | ---------------------------------------------- | --------------------------------- |
98 | | routerAddress | string
| Address of the destination Router |
99 | | evm2EvmMessage | [Evm2EvmMessage
](#Evm2EvmMessage) | Sent cross-chain message |
100 |
101 |
102 |
103 | ## Evm2EvmMessage : Object
104 |
105 | **Kind**: global typedef
106 | **Properties**
107 |
108 | | Name | Type |
109 | | ------------------- | ----------------------------------------------------------- |
110 | | sourceChainSelector | bigint
|
111 | | sender | string
|
112 | | receiver | string
|
113 | | sequenceNumber | bigint
|
114 | | gasLimit | bigint
|
115 | | strict | boolean
|
116 | | nonce | bigint
|
117 | | feeToken | string
|
118 | | feeTokenAmount | bigint
|
119 | | data | string
|
120 | | tokenAmounts | Array.<\{token: string, amount: bigint}>
|
121 | | sourceTokenData | Array.<string>
|
122 | | messageId | string
|
123 |
--------------------------------------------------------------------------------
/api_reference/javascript/index.mdx:
--------------------------------------------------------------------------------
1 | # Javascript API Reference
2 |
3 | - [CCIPLocalSimulatorFork](CCIPLocalSimulatorFork.mdx)
4 |
--------------------------------------------------------------------------------
/api_reference/solidity/ccip/BurnMintERC677Helper.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## BurnMintERC677Helper
4 |
5 | This contract extends the functionality of the BurnMintERC677 token contract to
6 | include a `drip` function that mints one full token to a specified address.
7 |
8 | _Inherits from the BurnMintERC677 contract and sets the token name, symbol,
9 | decimals, and initial supply in the constructor._
10 |
11 | ### constructor
12 |
13 | ```solidity
14 | constructor(string name, string symbol) public
15 | ```
16 |
17 | Constructor to initialize the BurnMintERC677Helper contract with a name and
18 | symbol.
19 |
20 | _Calls the parent constructor of BurnMintERC677 with fixed decimals (18) and
21 | initial supply (0)._
22 |
23 | #### Parameters
24 |
25 | | Name | Type | Description |
26 | | ------ | ------ | -------------------------- |
27 | | name | string | - The name of the token. |
28 | | symbol | string | - The symbol of the token. |
29 |
30 | ### drip
31 |
32 | ```solidity
33 | function drip(address to) external
34 | ```
35 |
36 | Mints one full token (1e18) to the specified address.
37 |
38 | _Calls the internal `_mint` function from the BurnMintERC677 contract._
39 |
40 | #### Parameters
41 |
42 | | Name | Type | Description |
43 | | ---- | ------- | ------------------------------------------ |
44 | | to | address | - The address to receive the minted token. |
45 |
--------------------------------------------------------------------------------
/api_reference/solidity/ccip/Register.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## Register
4 |
5 | This contract allows storing and retrieving network details for various chains.
6 |
7 | _Stores network details in a mapping based on chain IDs._
8 |
9 | ### NetworkDetails
10 |
11 | ```solidity
12 | struct NetworkDetails {
13 | uint64 chainSelector;
14 | address routerAddress;
15 | address linkAddress;
16 | address wrappedNativeAddress;
17 | address ccipBnMAddress;
18 | address ccipLnMAddress;
19 | address rmnProxyAddress;
20 | address registryModuleOwnerCustomAddress;
21 | address tokenAdminRegistryAddress;
22 | }
23 | ```
24 |
25 | ### s_networkDetails
26 |
27 | ```solidity
28 | mapping(uint256 => struct Register.NetworkDetails) s_networkDetails
29 | ```
30 |
31 | Mapping to store network details based on chain ID.
32 |
33 | ### constructor
34 |
35 | ```solidity
36 | constructor() public
37 | ```
38 |
39 | Constructor to initialize the network details for various chains.
40 |
41 | ### getNetworkDetails
42 |
43 | ```solidity
44 | function getNetworkDetails(uint256 chainId) external view returns (struct Register.NetworkDetails networkDetails)
45 | ```
46 |
47 | Retrieves network details for a given chain ID.
48 |
49 | #### Parameters
50 |
51 | | Name | Type | Description |
52 | | ------- | ------- | --------------------------------------------- |
53 | | chainId | uint256 | - The ID of the chain to get the details for. |
54 |
55 | #### Return Values
56 |
57 | | Name | Type | Description |
58 | | -------------- | ------------------------------ | ------------------------------------------------- |
59 | | networkDetails | struct Register.NetworkDetails | - The network details for the specified chain ID. |
60 |
61 | ### setNetworkDetails
62 |
63 | ```solidity
64 | function setNetworkDetails(uint256 chainId, struct Register.NetworkDetails networkDetails) external
65 | ```
66 |
67 | Sets the network details for a given chain ID.
68 |
69 | #### Parameters
70 |
71 | | Name | Type | Description |
72 | | -------------- | ------------------------------ | -------------------------------------------------------- |
73 | | chainId | uint256 | - The ID of the chain to set the details for. |
74 | | networkDetails | struct Register.NetworkDetails | - The network details to set for the specified chain ID. |
75 |
--------------------------------------------------------------------------------
/api_reference/solidity/ccip/index.mdx:
--------------------------------------------------------------------------------
1 | # Ccip API Reference
2 |
3 | - [BurnMintERC677Helper](BurnMintERC677Helper.mdx)
4 | - [CCIPLocalSimulator](CCIPLocalSimulator.mdx)
5 | - [CCIPLocalSimulatorFork](CCIPLocalSimulatorFork.mdx)
6 | - [Register](Register.mdx)
7 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-feeds/MockOffchainAggregator.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## MockOffchainAggregator
4 |
5 | This contract is a mock implementation of an offchain aggregator for testing
6 | purposes.
7 |
8 | _This contract simulates the behavior of an offchain aggregator and allows for
9 | updating answers and round data._
10 |
11 | ### decimals
12 |
13 | ```solidity
14 | uint8 decimals
15 | ```
16 |
17 | The number of decimals used by the aggregator.
18 |
19 | ### latestAnswer
20 |
21 | ```solidity
22 | int256 latestAnswer
23 | ```
24 |
25 | The latest answer reported by the aggregator.
26 |
27 | ### latestTimestamp
28 |
29 | ```solidity
30 | uint256 latestTimestamp
31 | ```
32 |
33 | The timestamp of the latest answer.
34 |
35 | ### latestRound
36 |
37 | ```solidity
38 | uint256 latestRound
39 | ```
40 |
41 | The latest round ID.
42 |
43 | ### minAnswer
44 |
45 | ```solidity
46 | int192 minAnswer
47 | ```
48 |
49 | The minimum answer the aggregator is allowed to report.
50 |
51 | ### maxAnswer
52 |
53 | ```solidity
54 | int192 maxAnswer
55 | ```
56 |
57 | The maximum answer the aggregator is allowed to report.
58 |
59 | ### getAnswer
60 |
61 | ```solidity
62 | mapping(uint256 => int256) getAnswer
63 | ```
64 |
65 | Mapping to get the answer for a specific round ID.
66 |
67 | ### getTimestamp
68 |
69 | ```solidity
70 | mapping(uint256 => uint256) getTimestamp
71 | ```
72 |
73 | Mapping to get the timestamp for a specific round ID.
74 |
75 | ### constructor
76 |
77 | ```solidity
78 | constructor(uint8 _decimals, int256 _initialAnswer) public
79 | ```
80 |
81 | Constructor to initialize the MockOffchainAggregator contract with initial
82 | parameters.
83 |
84 | #### Parameters
85 |
86 | | Name | Type | Description |
87 | | --------------- | ------ | ------------------------------------------------------ |
88 | | \_decimals | uint8 | - The number of decimals for the aggregator. |
89 | | \_initialAnswer | int256 | - The initial answer to be set in the mock aggregator. |
90 |
91 | ### updateAnswer
92 |
93 | ```solidity
94 | function updateAnswer(int256 _answer) public
95 | ```
96 |
97 | Updates the answer in the mock aggregator.
98 |
99 | #### Parameters
100 |
101 | | Name | Type | Description |
102 | | -------- | ------ | --------------------------- |
103 | | \_answer | int256 | - The new answer to be set. |
104 |
105 | ### updateRoundData
106 |
107 | ```solidity
108 | function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public
109 | ```
110 |
111 | Updates the round data in the mock aggregator.
112 |
113 | #### Parameters
114 |
115 | | Name | Type | Description |
116 | | ----------- | ------- | --------------------------------------- |
117 | | \_roundId | uint80 | - The round ID to be updated. |
118 | | \_answer | int256 | - The new answer to be set. |
119 | | \_timestamp | uint256 | - The timestamp to be set. |
120 | | \_startedAt | uint256 | - The timestamp when the round started. |
121 |
122 | ### getRoundData
123 |
124 | ```solidity
125 | function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
126 | ```
127 |
128 | Gets the round data for a specific round ID.
129 |
130 | #### Parameters
131 |
132 | | Name | Type | Description |
133 | | --------- | ------ | ----------------------------------- |
134 | | \_roundId | uint80 | - The round ID to get the data for. |
135 |
136 | #### Return Values
137 |
138 | | Name | Type | Description |
139 | | --------------- | ------- | ------------------------------------------------ |
140 | | roundId | uint80 | - The round ID. |
141 | | answer | int256 | - The answer for the round. |
142 | | startedAt | uint256 | - The timestamp when the round started. |
143 | | updatedAt | uint256 | - The timestamp when the round was updated. |
144 | | answeredInRound | uint80 | - The round ID in which the answer was computed. |
145 |
146 | ### latestRoundData
147 |
148 | ```solidity
149 | function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
150 | ```
151 |
152 | Gets the latest round data.
153 |
154 | #### Return Values
155 |
156 | | Name | Type | Description |
157 | | --------------- | ------- | ------------------------------------------------------- |
158 | | roundId | uint80 | - The latest round ID. |
159 | | answer | int256 | - The latest answer. |
160 | | startedAt | uint256 | - The timestamp when the latest round started. |
161 | | updatedAt | uint256 | - The timestamp when the latest round was updated. |
162 | | answeredInRound | uint80 | - The round ID in which the latest answer was computed. |
163 |
164 | ### updateMinAndMaxAnswers
165 |
166 | ```solidity
167 | function updateMinAndMaxAnswers(int192 _minAnswer, int192 _maxAnswer) external
168 | ```
169 |
170 | Updates the minimum and maximum answers the aggregator can report.
171 |
172 | #### Parameters
173 |
174 | | Name | Type | Description |
175 | | ----------- | ------ | ------------------------- |
176 | | \_minAnswer | int192 | - The new minimum answer. |
177 | | \_maxAnswer | int192 | - The new maximum answer. |
178 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-feeds/index.mdx:
--------------------------------------------------------------------------------
1 | # Data-feeds API Reference
2 |
3 | - [MockOffchainAggregator](MockOffchainAggregator.mdx)
4 | - [MockV3Aggregator](MockV3Aggregator.mdx)
5 | - [interfaces](interfaces/index.mdx)
6 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-feeds/interfaces/AggregatorInterface.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## AggregatorInterface
4 |
5 | Interface for accessing data from an aggregator contract.
6 |
7 | _Provides methods to get the latest data and historical data for specific
8 | rounds._
9 |
10 | ### latestAnswer
11 |
12 | ```solidity
13 | function latestAnswer() external view returns (int256)
14 | ```
15 |
16 | Gets the latest answer from the aggregator.
17 |
18 | #### Return Values
19 |
20 | | Name | Type | Description |
21 | | ---- | ------ | --------------------------- |
22 | | [0] | int256 | int256 - The latest answer. |
23 |
24 | ### latestTimestamp
25 |
26 | ```solidity
27 | function latestTimestamp() external view returns (uint256)
28 | ```
29 |
30 | Gets the timestamp of the latest answer from the aggregator.
31 |
32 | #### Return Values
33 |
34 | | Name | Type | Description |
35 | | ---- | ------- | --------------------------------------------- |
36 | | [0] | uint256 | uint256 - The timestamp of the latest answer. |
37 |
38 | ### latestRound
39 |
40 | ```solidity
41 | function latestRound() external view returns (uint256)
42 | ```
43 |
44 | Gets the latest round ID from the aggregator.
45 |
46 | #### Return Values
47 |
48 | | Name | Type | Description |
49 | | ---- | ------- | ------------------------------ |
50 | | [0] | uint256 | uint256 - The latest round ID. |
51 |
52 | ### getAnswer
53 |
54 | ```solidity
55 | function getAnswer(uint256 roundId) external view returns (int256)
56 | ```
57 |
58 | Gets the answer for a specific round ID.
59 |
60 | #### Parameters
61 |
62 | | Name | Type | Description |
63 | | ------- | ------- | ------------------------------------- |
64 | | roundId | uint256 | - The round ID to get the answer for. |
65 |
66 | #### Return Values
67 |
68 | | Name | Type | Description |
69 | | ---- | ------ | ------------------------------------------- |
70 | | [0] | int256 | int256 - The answer for the given round ID. |
71 |
72 | ### getTimestamp
73 |
74 | ```solidity
75 | function getTimestamp(uint256 roundId) external view returns (uint256)
76 | ```
77 |
78 | Gets the timestamp for a specific round ID.
79 |
80 | #### Parameters
81 |
82 | | Name | Type | Description |
83 | | ------- | ------- | ---------------------------------------- |
84 | | roundId | uint256 | - The round ID to get the timestamp for. |
85 |
86 | #### Return Values
87 |
88 | | Name | Type | Description |
89 | | ---- | ------- | ----------------------------------------------- |
90 | | [0] | uint256 | uint256 - The timestamp for the given round ID. |
91 |
92 | ### AnswerUpdated
93 |
94 | ```solidity
95 | event AnswerUpdated(int256 current, uint256 roundId, uint256 updatedAt)
96 | ```
97 |
98 | Emitted when the answer is updated.
99 |
100 | #### Parameters
101 |
102 | | Name | Type | Description |
103 | | --------- | ------- | ------------------------------------------------ |
104 | | current | int256 | - The updated answer. |
105 | | roundId | uint256 | - The round ID for which the answer was updated. |
106 | | updatedAt | uint256 | - The timestamp when the answer was updated. |
107 |
108 | ### NewRound
109 |
110 | ```solidity
111 | event NewRound(uint256 roundId, address startedBy, uint256 startedAt)
112 | ```
113 |
114 | Emitted when a new round is started.
115 |
116 | #### Parameters
117 |
118 | | Name | Type | Description |
119 | | --------- | ------- | ---------------------------------------------------- |
120 | | roundId | uint256 | - The round ID of the new round. |
121 | | startedBy | address | - The address of the account that started the round. |
122 | | startedAt | uint256 | - The timestamp when the round was started. |
123 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-feeds/interfaces/AggregatorV2V3Interface.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## AggregatorV2V3Interface
4 |
5 | Interface that inherits from both AggregatorInterface and AggregatorV3Interface.
6 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-feeds/interfaces/AggregatorV3Interface.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## AggregatorV3Interface
4 |
5 | Interface for accessing detailed data from an aggregator contract, including
6 | round data and metadata.
7 |
8 | _Provides methods to get the latest data, historical data for specific rounds,
9 | and metadata such as decimals and description._
10 |
11 | ### decimals
12 |
13 | ```solidity
14 | function decimals() external view returns (uint8)
15 | ```
16 |
17 | Gets the number of decimals used by the aggregator.
18 |
19 | #### Return Values
20 |
21 | | Name | Type | Description |
22 | | ---- | ----- | ------------------------------- |
23 | | [0] | uint8 | uint8 - The number of decimals. |
24 |
25 | ### description
26 |
27 | ```solidity
28 | function description() external view returns (string)
29 | ```
30 |
31 | Gets the description of the aggregator.
32 |
33 | #### Return Values
34 |
35 | | Name | Type | Description |
36 | | ---- | ------ | -------------------------------------------------- |
37 | | [0] | string | string memory - The description of the aggregator. |
38 |
39 | ### version
40 |
41 | ```solidity
42 | function version() external view returns (uint256)
43 | ```
44 |
45 | Gets the version of the aggregator.
46 |
47 | #### Return Values
48 |
49 | | Name | Type | Description |
50 | | ---- | ------- | ---------------------------------------- |
51 | | [0] | uint256 | uint256 - The version of the aggregator. |
52 |
53 | ### getRoundData
54 |
55 | ```solidity
56 | function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
57 | ```
58 |
59 | Gets the round data for a specific round ID.
60 |
61 | _This function should raise "No data present" if no data is available for the
62 | given round ID._
63 |
64 | #### Parameters
65 |
66 | | Name | Type | Description |
67 | | --------- | ------ | ----------------------------------- |
68 | | \_roundId | uint80 | - The round ID to get the data for. |
69 |
70 | #### Return Values
71 |
72 | | Name | Type | Description |
73 | | --------------- | ------- | ------------------------------------------------ |
74 | | roundId | uint80 | - The round ID. |
75 | | answer | int256 | - The answer for the round. |
76 | | startedAt | uint256 | - The timestamp when the round started. |
77 | | updatedAt | uint256 | - The timestamp when the round was updated. |
78 | | answeredInRound | uint80 | - The round ID in which the answer was computed. |
79 |
80 | ### latestRoundData
81 |
82 | ```solidity
83 | function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
84 | ```
85 |
86 | Gets the latest round data.
87 |
88 | _This function should raise "No data present" if no data is available._
89 |
90 | #### Return Values
91 |
92 | | Name | Type | Description |
93 | | --------------- | ------- | ------------------------------------------------------- |
94 | | roundId | uint80 | - The latest round ID. |
95 | | answer | int256 | - The latest answer. |
96 | | startedAt | uint256 | - The timestamp when the latest round started. |
97 | | updatedAt | uint256 | - The timestamp when the latest round was updated. |
98 | | answeredInRound | uint80 | - The round ID in which the latest answer was computed. |
99 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-feeds/interfaces/index.mdx:
--------------------------------------------------------------------------------
1 | # Interfaces API Reference
2 |
3 | - [AggregatorInterface](AggregatorInterface.mdx)
4 | - [AggregatorV2V3Interface](AggregatorV2V3Interface.mdx)
5 | - [AggregatorV3Interface](AggregatorV3Interface.mdx)
6 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/DataStreamsLocalSimulator.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## DataStreamsLocalSimulator
4 |
5 | ### s_mockVerifier
6 |
7 | ```solidity
8 | contract MockVerifier s_mockVerifier
9 | ```
10 |
11 | ### s_mockVerifierProxy
12 |
13 | ```solidity
14 | contract MockVerifierProxy s_mockVerifierProxy
15 | ```
16 |
17 | ### s_mockFeeManager
18 |
19 | ```solidity
20 | contract MockFeeManager s_mockFeeManager
21 | ```
22 |
23 | ### s_mockRewardManager
24 |
25 | ```solidity
26 | contract MockRewardManager s_mockRewardManager
27 | ```
28 |
29 | ### i_wrappedNative
30 |
31 | ```solidity
32 | contract WETH9 i_wrappedNative
33 | ```
34 |
35 | The wrapped native token instance
36 |
37 | ### i_linkToken
38 |
39 | ```solidity
40 | contract LinkToken i_linkToken
41 | ```
42 |
43 | The LINK token instance
44 |
45 | ### constructor
46 |
47 | ```solidity
48 | constructor() public
49 | ```
50 |
51 | ### requestLinkFromFaucet
52 |
53 | ```solidity
54 | function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
55 | ```
56 |
57 | Requests LINK tokens from the faucet. The provided amount of tokens are
58 | transferred to provided destination address.
59 |
60 | #### Parameters
61 |
62 | | Name | Type | Description |
63 | | ------ | ------- | -------------------------------------------------- |
64 | | to | address | - The address to which LINK tokens are to be sent. |
65 | | amount | uint256 | - The amount of LINK tokens to send. |
66 |
67 | #### Return Values
68 |
69 | | Name | Type | Description |
70 | | ------- | ---- | ----------------------------------------------------------------------------- |
71 | | success | bool | - Returns `true` if the transfer of tokens was successful, otherwise `false`. |
72 |
73 | ### configuration
74 |
75 | ```solidity
76 | function configuration() public view returns (contract WETH9 wrappedNative_, contract LinkToken linkToken_, contract MockVerifier mockVerifier_, contract MockVerifierProxy mockVerifierProxy_, contract MockFeeManager mockFeeManager_, contract MockRewardManager mockRewardManager_)
77 | ```
78 |
79 | @notice Returns configuration details for pre-deployed contracts and services
80 | needed for local Data Streams simulations.
81 |
82 | #### Return Values
83 |
84 | | Name | Type | Description |
85 | | ------------------- | -------------------------- | ----------------------------------- |
86 | | wrappedNative\_ | contract WETH9 | - The wrapped native token. |
87 | | linkToken\_ | contract LinkToken | - The LINK token. |
88 | | mockVerifier\_ | contract MockVerifier | - The mock verifier contract. |
89 | | mockVerifierProxy\_ | contract MockVerifierProxy | - The mock verifier proxy contract. |
90 | | mockFeeManager\_ | contract MockFeeManager | - The mock fee manager contract. |
91 | | mockRewardManager\_ | contract MockRewardManager | - The mock reward manager contract. |
92 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/DataStreamsLocalSimulatorFork.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## DataStreamsLocalSimulatorFork
4 |
5 | ### i_register
6 |
7 | ```solidity
8 | contract Register i_register
9 | ```
10 |
11 | The immutable register instance
12 |
13 | ### LINK_FAUCET
14 |
15 | ```solidity
16 | address LINK_FAUCET
17 | ```
18 |
19 | The address of the LINK faucet
20 |
21 | ### constructor
22 |
23 | ```solidity
24 | constructor() public
25 | ```
26 |
27 | Constructor to initialize the contract
28 |
29 | ### getNetworkDetails
30 |
31 | ```solidity
32 | function getNetworkDetails(uint256 chainId) external view returns (struct Register.NetworkDetails)
33 | ```
34 |
35 | Returns the default values for currently Data Streams supported networks. If
36 | network is not present or some of the values are changed, user can manually add
37 | new network details using the `setNetworkDetails` function.
38 |
39 | #### Parameters
40 |
41 | | Name | Type | Description |
42 | | ------- | ------- | ----------------------------------------------------------------------------- |
43 | | chainId | uint256 | - The blockchain network chain ID. For example 11155111 for Ethereum Sepolia. |
44 |
45 | #### Return Values
46 |
47 | | Name | Type | Description |
48 | | ---- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
49 | | [0] | struct Register.NetworkDetails | networkDetails - The tuple containing: verifierProxyAddress - The address of the Verifier Proxy smart contract. linkAddress - The address of the LINK token. |
50 |
51 | ### setNetworkDetails
52 |
53 | ```solidity
54 | function setNetworkDetails(uint256 chainId, struct Register.NetworkDetails networkDetails) external
55 | ```
56 |
57 | If network details are not present or some of the values are changed, user can
58 | manually add new network details using the `setNetworkDetails` function.
59 |
60 | #### Parameters
61 |
62 | | Name | Type | Description |
63 | | -------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
64 | | chainId | uint256 | - The blockchain network chain ID. For example 11155111 for Ethereum Sepolia. |
65 | | networkDetails | struct Register.NetworkDetails | - The tuple containing: verifierProxyAddress - The address of the Verifier Proxy smart contract. linkAddress - The address of the LINK token. |
66 |
67 | ### requestLinkFromFaucet
68 |
69 | ```solidity
70 | function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success)
71 | ```
72 |
73 | Requests LINK tokens from the faucet. The provided amount of tokens are
74 | transferred to provided destination address.
75 |
76 | #### Parameters
77 |
78 | | Name | Type | Description |
79 | | ------ | ------- | -------------------------------------------------- |
80 | | to | address | - The address to which LINK tokens are to be sent. |
81 | | amount | uint256 | - The amount of LINK tokens to send. |
82 |
83 | #### Return Values
84 |
85 | | Name | Type | Description |
86 | | ------- | ---- | ----------------------------------------------------------------------------- |
87 | | success | bool | - Returns `true` if the transfer of tokens was successful, otherwise `false`. |
88 |
89 | ### requestNativeFromFaucet
90 |
91 | ```solidity
92 | function requestNativeFromFaucet(address to, uint256 amount) external
93 | ```
94 |
95 | Requests native coints from the faucet.
96 |
97 | #### Parameters
98 |
99 | | Name | Type | Description |
100 | | ------ | ------- | --------------------------------------------------- |
101 | | to | address | - The address to which native coins are to be sent. |
102 | | amount | uint256 | - The amount of native coins to send. |
103 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/MockFeeManager.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## Common
4 |
5 | ### Asset
6 |
7 | ```solidity
8 | struct Asset {
9 | address assetAddress;
10 | uint256 amount;
11 | }
12 | ```
13 |
14 | ## MockFeeManager
15 |
16 | ### Unauthorized
17 |
18 | ```solidity
19 | error Unauthorized()
20 | ```
21 |
22 | ### InvalidAddress
23 |
24 | ```solidity
25 | error InvalidAddress()
26 | ```
27 |
28 | ### InvalidQuote
29 |
30 | ```solidity
31 | error InvalidQuote()
32 | ```
33 |
34 | ### ExpiredReport
35 |
36 | ```solidity
37 | error ExpiredReport()
38 | ```
39 |
40 | ### InvalidDiscount
41 |
42 | ```solidity
43 | error InvalidDiscount()
44 | ```
45 |
46 | ### InvalidSurcharge
47 |
48 | ```solidity
49 | error InvalidSurcharge()
50 | ```
51 |
52 | ### InvalidDeposit
53 |
54 | ```solidity
55 | error InvalidDeposit()
56 | ```
57 |
58 | ### i_linkAddress
59 |
60 | ```solidity
61 | address i_linkAddress
62 | ```
63 |
64 | ### i_nativeAddress
65 |
66 | ```solidity
67 | address i_nativeAddress
68 | ```
69 |
70 | ### i_proxyAddress
71 |
72 | ```solidity
73 | address i_proxyAddress
74 | ```
75 |
76 | ### i_rewardManager
77 |
78 | ```solidity
79 | contract IRewardManager i_rewardManager
80 | ```
81 |
82 | ### s_nativeSurcharge
83 |
84 | ```solidity
85 | uint256 s_nativeSurcharge
86 | ```
87 |
88 | ### s_mockDiscounts
89 |
90 | ```solidity
91 | mapping(address => uint256) s_mockDiscounts
92 | ```
93 |
94 | ### onlyProxy
95 |
96 | ```solidity
97 | modifier onlyProxy()
98 | ```
99 |
100 | ### constructor
101 |
102 | ```solidity
103 | constructor(address linkAddress, address nativeAddress, address proxyAddress, address rewardManager) public
104 | ```
105 |
106 | ### processFee
107 |
108 | ```solidity
109 | function processFee(bytes payload, bytes parameterPayload, address subscriber) external payable
110 | ```
111 |
112 | ### processFeeBulk
113 |
114 | ```solidity
115 | function processFeeBulk(bytes[] payloads, bytes parameterPayload, address subscriber) external payable
116 | ```
117 |
118 | ### getFeeAndReward
119 |
120 | ```solidity
121 | function getFeeAndReward(address subscriber, bytes report, address quoteAddress) public view returns (struct Common.Asset, struct Common.Asset, uint256)
122 | ```
123 |
124 | ### \_processFee
125 |
126 | ```solidity
127 | function _processFee(bytes payload, bytes parameterPayload, address subscriber) internal
128 | ```
129 |
130 | ### setNativeSurcharge
131 |
132 | ```solidity
133 | function setNativeSurcharge(uint64 surcharge) external
134 | ```
135 |
136 | ### setMockDiscount
137 |
138 | ```solidity
139 | function setMockDiscount(address subscriber, uint256 discount) external
140 | ```
141 |
142 | ### getMockDiscount
143 |
144 | ```solidity
145 | function getMockDiscount(address subscriber) external view returns (uint256)
146 | ```
147 |
148 | ### supportsInterface
149 |
150 | ```solidity
151 | function supportsInterface(bytes4 interfaceId) external pure returns (bool)
152 | ```
153 |
154 | \_Returns true if this contract implements the interface defined by
155 | `interfaceId`. See the corresponding
156 | https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP
157 | section] to learn more about how these ids are created.
158 |
159 | This function call must use less than 30 000 gas.\_
160 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/MockReportGenerator.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## MockReportGenerator
4 |
5 | ### i_donAddress
6 |
7 | ```solidity
8 | address i_donAddress
9 | ```
10 |
11 | ### i_donDigest
12 |
13 | ```solidity
14 | uint256 i_donDigest
15 | ```
16 |
17 | ### i_reportV2MockFeedId
18 |
19 | ```solidity
20 | bytes32 i_reportV2MockFeedId
21 | ```
22 |
23 | ### i_reportV3MockFeedId
24 |
25 | ```solidity
26 | bytes32 i_reportV3MockFeedId
27 | ```
28 |
29 | ### i_reportV4MockFeedId
30 |
31 | ```solidity
32 | bytes32 i_reportV4MockFeedId
33 | ```
34 |
35 | ### s_price
36 |
37 | ```solidity
38 | int192 s_price
39 | ```
40 |
41 | ### s_bid
42 |
43 | ```solidity
44 | int192 s_bid
45 | ```
46 |
47 | ### s_ask
48 |
49 | ```solidity
50 | int192 s_ask
51 | ```
52 |
53 | ### s_expiresPeriod
54 |
55 | ```solidity
56 | uint32 s_expiresPeriod
57 | ```
58 |
59 | ### s_marketStatus
60 |
61 | ```solidity
62 | uint32 s_marketStatus
63 | ```
64 |
65 | ### s_nativeFee
66 |
67 | ```solidity
68 | uint192 s_nativeFee
69 | ```
70 |
71 | ### s_linkFee
72 |
73 | ```solidity
74 | uint192 s_linkFee
75 | ```
76 |
77 | ### MockReportGenerator\_\_InvalidBid
78 |
79 | ```solidity
80 | error MockReportGenerator__InvalidBid()
81 | ```
82 |
83 | ### MockReportGenerator\_\_InvalidAsk
84 |
85 | ```solidity
86 | error MockReportGenerator__InvalidAsk()
87 | ```
88 |
89 | ### MockReportGenerator\_\_CastOverflow
90 |
91 | ```solidity
92 | error MockReportGenerator__CastOverflow()
93 | ```
94 |
95 | ### constructor
96 |
97 | ```solidity
98 | constructor(int192 initialPrice) public
99 | ```
100 |
101 | ### generateReport
102 |
103 | ```solidity
104 | function generateReport(struct ReportVersions.ReportV2 report) external returns (bytes signedReport)
105 | ```
106 |
107 | ### generateReport
108 |
109 | ```solidity
110 | function generateReport(struct ReportVersions.ReportV3 report) external returns (bytes signedReport)
111 | ```
112 |
113 | ### generateReport
114 |
115 | ```solidity
116 | function generateReport(struct ReportVersions.ReportV4 report) external returns (bytes signedReport)
117 | ```
118 |
119 | ### generateReportV2
120 |
121 | ```solidity
122 | function generateReportV2() external returns (bytes signedReport, struct ReportVersions.ReportV2 report)
123 | ```
124 |
125 | ### generateReportV3
126 |
127 | ```solidity
128 | function generateReportV3() external returns (bytes signedReport, struct ReportVersions.ReportV3 report)
129 | ```
130 |
131 | ### generateReportV4
132 |
133 | ```solidity
134 | function generateReportV4() external returns (bytes signedReport, struct ReportVersions.ReportV4 report)
135 | ```
136 |
137 | ### updatePrice
138 |
139 | ```solidity
140 | function updatePrice(int192 price) public
141 | ```
142 |
143 | ### updatePriceBidAndAsk
144 |
145 | ```solidity
146 | function updatePriceBidAndAsk(int192 price, int192 bid, int192 ask) external
147 | ```
148 |
149 | ### updateExpiresPeriod
150 |
151 | ```solidity
152 | function updateExpiresPeriod(uint32 period) external
153 | ```
154 |
155 | ### updateMarketStatus
156 |
157 | ```solidity
158 | function updateMarketStatus(uint32 status) external
159 | ```
160 |
161 | ### updateFees
162 |
163 | ```solidity
164 | function updateFees(uint192 nativeFee, uint192 linkFee) external
165 | ```
166 |
167 | ### getMockDonAddress
168 |
169 | ```solidity
170 | function getMockDonAddress() external view returns (address)
171 | ```
172 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/MockRewardManager.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## MockRewardManager
4 |
5 | ### Unauthorized
6 |
7 | ```solidity
8 | error Unauthorized()
9 | ```
10 |
11 | ### i_linkAddress
12 |
13 | ```solidity
14 | address i_linkAddress
15 | ```
16 |
17 | ### s_feeManagerAddress
18 |
19 | ```solidity
20 | address s_feeManagerAddress
21 | ```
22 |
23 | ### FeePaid
24 |
25 | ```solidity
26 | event FeePaid(struct IRewardManager.FeePayment[] payments, address payer)
27 | ```
28 |
29 | ### onlyFeeManager
30 |
31 | ```solidity
32 | modifier onlyFeeManager()
33 | ```
34 |
35 | ### constructor
36 |
37 | ```solidity
38 | constructor(address linkAddress) public
39 | ```
40 |
41 | ### onFeePaid
42 |
43 | ```solidity
44 | function onFeePaid(struct IRewardManager.FeePayment[] payments, address payer) external
45 | ```
46 |
47 | ### claimRewards
48 |
49 | ```solidity
50 | function claimRewards(bytes32[]) external pure
51 | ```
52 |
53 | ### setFeeManager
54 |
55 | ```solidity
56 | function setFeeManager(address newFeeManager) external
57 | ```
58 |
59 | ### supportsInterface
60 |
61 | ```solidity
62 | function supportsInterface(bytes4 interfaceId) external pure returns (bool)
63 | ```
64 |
65 | \_Returns true if this contract implements the interface defined by
66 | `interfaceId`. See the corresponding
67 | https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP
68 | section] to learn more about how these ids are created.
69 |
70 | This function call must use less than 30 000 gas.\_
71 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/MockVerifier.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## MockVerifier
4 |
5 | ### AccessForbidden
6 |
7 | ```solidity
8 | error AccessForbidden()
9 | ```
10 |
11 | ### InactiveFeed
12 |
13 | ```solidity
14 | error InactiveFeed(bytes32 feedId)
15 | ```
16 |
17 | ### DigestInactive
18 |
19 | ```solidity
20 | error DigestInactive(bytes32 feedId, bytes32 configDigest)
21 | ```
22 |
23 | ### BadVerification
24 |
25 | ```solidity
26 | error BadVerification()
27 | ```
28 |
29 | ### InvalidV
30 |
31 | ```solidity
32 | error InvalidV()
33 | ```
34 |
35 | ### AlreadyUsedSignature
36 |
37 | ```solidity
38 | error AlreadyUsedSignature()
39 | ```
40 |
41 | ### InvalidSignatureLength
42 |
43 | ```solidity
44 | error InvalidSignatureLength()
45 | ```
46 |
47 | ### InvalidS
48 |
49 | ```solidity
50 | error InvalidS()
51 | ```
52 |
53 | ### N_2
54 |
55 | ```solidity
56 | uint256 N_2
57 | ```
58 |
59 | ### MOCK_DATA_STREAM_DON_ADDRESS
60 |
61 | ```solidity
62 | address MOCK_DATA_STREAM_DON_ADDRESS
63 | ```
64 |
65 | ### i_verifierProxy
66 |
67 | ```solidity
68 | address i_verifierProxy
69 | ```
70 |
71 | ### s_inactiveDigests
72 |
73 | ```solidity
74 | mapping(bytes32 => mapping(bytes32 => bool)) s_inactiveDigests
75 | ```
76 |
77 | ### s_inactiveFeeds
78 |
79 | ```solidity
80 | mapping(bytes32 => bool) s_inactiveFeeds
81 | ```
82 |
83 | ### s_verifiedSignatures
84 |
85 | ```solidity
86 | mapping(bytes => bool) s_verifiedSignatures
87 | ```
88 |
89 | ### ReportVerified
90 |
91 | ```solidity
92 | event ReportVerified(bytes32 feedId, address sender)
93 | ```
94 |
95 | ### constructor
96 |
97 | ```solidity
98 | constructor(address verifierProxy) public
99 | ```
100 |
101 | ### verify
102 |
103 | ```solidity
104 | function verify(bytes signedReport, address sender) external returns (bytes verifierResponse)
105 | ```
106 |
107 | ### deactivateConfig
108 |
109 | ```solidity
110 | function deactivateConfig(bytes32 feedId, bytes32 configDigest) external
111 | ```
112 |
113 | ### activateConfig
114 |
115 | ```solidity
116 | function activateConfig(bytes32 feedId, bytes32 configDigest) external
117 | ```
118 |
119 | ### deactivateFeed
120 |
121 | ```solidity
122 | function deactivateFeed(bytes32 feedId) external
123 | ```
124 |
125 | ### activateFeed
126 |
127 | ```solidity
128 | function activateFeed(bytes32 feedId) external
129 | ```
130 |
131 | ### supportsInterface
132 |
133 | ```solidity
134 | function supportsInterface(bytes4 interfaceId) external pure returns (bool)
135 | ```
136 |
137 | \_Returns true if this contract implements the interface defined by
138 | `interfaceId`. See the corresponding
139 | https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP
140 | section] to learn more about how these ids are created.
141 |
142 | This function call must use less than 30 000 gas.\_
143 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/MockVerifierProxy.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## MockVerifierProxy
4 |
5 | ### ZeroAddress
6 |
7 | ```solidity
8 | error ZeroAddress()
9 | ```
10 |
11 | ### VerifierInvalid
12 |
13 | ```solidity
14 | error VerifierInvalid()
15 | ```
16 |
17 | ### VerifierNotFound
18 |
19 | ```solidity
20 | error VerifierNotFound()
21 | ```
22 |
23 | ### s_verifier
24 |
25 | ```solidity
26 | address s_verifier
27 | ```
28 |
29 | ### s_feeManager
30 |
31 | ```solidity
32 | contract IVerifierFeeManager s_feeManager
33 | ```
34 |
35 | ### VerifierInitialized
36 |
37 | ```solidity
38 | event VerifierInitialized(address verifierAddress)
39 | ```
40 |
41 | ### onlyValidVerifier
42 |
43 | ```solidity
44 | modifier onlyValidVerifier(address verifierAddress)
45 | ```
46 |
47 | ### verify
48 |
49 | ```solidity
50 | function verify(bytes payload, bytes parameterPayload) external payable returns (bytes)
51 | ```
52 |
53 | ### verifyBulk
54 |
55 | ```solidity
56 | function verifyBulk(bytes[] payloads, bytes parameterPayload) external payable returns (bytes[] verifiedReports)
57 | ```
58 |
59 | ### \_verify
60 |
61 | ```solidity
62 | function _verify(bytes payload) internal returns (bytes)
63 | ```
64 |
65 | ### initializeVerifier
66 |
67 | ```solidity
68 | function initializeVerifier(address verifierAddress) external
69 | ```
70 |
71 | ### getVerifier
72 |
73 | ```solidity
74 | function getVerifier(bytes32) external view returns (address)
75 | ```
76 |
77 | ### setFeeManager
78 |
79 | ```solidity
80 | function setFeeManager(contract IVerifierFeeManager feeManager) external
81 | ```
82 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/Register.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## Register
4 |
5 | This contract allows storing and retrieving network details for various chains.
6 |
7 | _Stores network details in a mapping based on chain IDs._
8 |
9 | ### NetworkDetails
10 |
11 | ```solidity
12 | struct NetworkDetails {
13 | address verifierProxyAddress;
14 | address linkAddress;
15 | }
16 | ```
17 |
18 | ### s_networkDetails
19 |
20 | ```solidity
21 | mapping(uint256 => struct Register.NetworkDetails) s_networkDetails
22 | ```
23 |
24 | Mapping to store network details based on chain ID.
25 |
26 | ### constructor
27 |
28 | ```solidity
29 | constructor() public
30 | ```
31 |
32 | Constructor to initialize the network details for various chains.
33 |
34 | ### getNetworkDetails
35 |
36 | ```solidity
37 | function getNetworkDetails(uint256 chainId) external view returns (struct Register.NetworkDetails networkDetails)
38 | ```
39 |
40 | Retrieves network details for a given chain ID.
41 |
42 | #### Parameters
43 |
44 | | Name | Type | Description |
45 | | ------- | ------- | --------------------------------------------- |
46 | | chainId | uint256 | - The ID of the chain to get the details for. |
47 |
48 | #### Return Values
49 |
50 | | Name | Type | Description |
51 | | -------------- | ------------------------------ | ------------------------------------------------- |
52 | | networkDetails | struct Register.NetworkDetails | - The network details for the specified chain ID. |
53 |
54 | ### setNetworkDetails
55 |
56 | ```solidity
57 | function setNetworkDetails(uint256 chainId, struct Register.NetworkDetails networkDetails) external
58 | ```
59 |
60 | Sets the network details for a given chain ID.
61 |
62 | #### Parameters
63 |
64 | | Name | Type | Description |
65 | | -------------- | ------------------------------ | -------------------------------------------------------- |
66 | | chainId | uint256 | - The ID of the chain to set the details for. |
67 | | networkDetails | struct Register.NetworkDetails | - The network details to set for the specified chain ID. |
68 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/ReportVersions.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## ReportVersions
4 |
5 | ### ReportV2
6 |
7 | _Represents a data report from a Data Streams stream for v2 schema (crypto
8 | streams). The `price` value is carried to either 8 or 18 decimal places,
9 | depending on the stream. For more information, see
10 | https://docs.chain.link/data-streams/crypto-streams and
11 | https://docs.chain.link/data-streams/reference/report-schema_
12 |
13 | ```solidity
14 | struct ReportV2 {
15 | bytes32 feedId;
16 | uint32 validFromTimestamp;
17 | uint32 observationsTimestamp;
18 | uint192 nativeFee;
19 | uint192 linkFee;
20 | uint32 expiresAt;
21 | int192 benchmarkPrice;
22 | }
23 | ```
24 |
25 | ### ReportV3
26 |
27 | _Represents a data report from a Data Streams stream for v3 schema (crypto
28 | streams). The `price`, `bid`, and `ask` values are carried to either 8 or 18
29 | decimal places, depending on the stream. For more information, see
30 | https://docs.chain.link/data-streams/crypto-streams and
31 | https://docs.chain.link/data-streams/reference/report-schema_
32 |
33 | ```solidity
34 | struct ReportV3 {
35 | bytes32 feedId;
36 | uint32 validFromTimestamp;
37 | uint32 observationsTimestamp;
38 | uint192 nativeFee;
39 | uint192 linkFee;
40 | uint32 expiresAt;
41 | int192 price;
42 | int192 bid;
43 | int192 ask;
44 | }
45 | ```
46 |
47 | ### ReportV4
48 |
49 | _Represents a data report from a Data Streams stream for v4 schema (RWA stream).
50 | The `price` value is carried to either 8 or 18 decimal places, depending on the
51 | stream. The `marketStatus` indicates whether the market is currently open.
52 | Possible values: `0` (`Unknown`), `1` (`Closed`), `2` (`Open`). For more
53 | information, see https://docs.chain.link/data-streams/rwa-streams and
54 | https://docs.chain.link/data-streams/reference/report-schema-v4_
55 |
56 | ```solidity
57 | struct ReportV4 {
58 | bytes32 feedId;
59 | uint32 validFromTimestamp;
60 | uint32 observationsTimestamp;
61 | uint192 nativeFee;
62 | uint192 linkFee;
63 | uint32 expiresAt;
64 | int192 price;
65 | uint32 marketStatus;
66 | }
67 | ```
68 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/index.mdx:
--------------------------------------------------------------------------------
1 | # Data-streams API Reference
2 |
3 | - [DataStreamsLocalSimulator](DataStreamsLocalSimulator.mdx)
4 | - [DataStreamsLocalSimulatorFork](DataStreamsLocalSimulatorFork.mdx)
5 | - [MockFeeManager](MockFeeManager.mdx)
6 | - [MockReportGenerator](MockReportGenerator.mdx)
7 | - [MockRewardManager](MockRewardManager.mdx)
8 | - [MockVerifier](MockVerifier.mdx)
9 | - [MockVerifierProxy](MockVerifierProxy.mdx)
10 | - [Register](Register.mdx)
11 | - [ReportVersions](ReportVersions.mdx)
12 | - [interfaces](interfaces/index.mdx)
13 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/interfaces/IRewardManager.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## IRewardManager
4 |
5 | ### onFeePaid
6 |
7 | ```solidity
8 | function onFeePaid(struct IRewardManager.FeePayment[] payments, address payee) external
9 | ```
10 |
11 | ### claimRewards
12 |
13 | ```solidity
14 | function claimRewards(bytes32[] poolIds) external
15 | ```
16 |
17 | ### setFeeManager
18 |
19 | ```solidity
20 | function setFeeManager(address newFeeManager) external
21 | ```
22 |
23 | ### FeePayment
24 |
25 | ```solidity
26 | struct FeePayment {
27 | bytes32 poolId;
28 | uint192 amount;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/interfaces/IVerifier.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## IVerifier
4 |
5 | ### verify
6 |
7 | ```solidity
8 | function verify(bytes signedReport, address sender) external returns (bytes verifierResponse)
9 | ```
10 |
11 | ### activateConfig
12 |
13 | ```solidity
14 | function activateConfig(bytes32 feedId, bytes32 configDigest) external
15 | ```
16 |
17 | ### deactivateConfig
18 |
19 | ```solidity
20 | function deactivateConfig(bytes32 feedId, bytes32 configDigest) external
21 | ```
22 |
23 | ### activateFeed
24 |
25 | ```solidity
26 | function activateFeed(bytes32 feedId) external
27 | ```
28 |
29 | ### deactivateFeed
30 |
31 | ```solidity
32 | function deactivateFeed(bytes32 feedId) external
33 | ```
34 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/interfaces/IVerifierFeeManager.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## IVerifierFeeManager
4 |
5 | ### processFee
6 |
7 | ```solidity
8 | function processFee(bytes payload, bytes parameterPayload, address subscriber) external payable
9 | ```
10 |
11 | ### processFeeBulk
12 |
13 | ```solidity
14 | function processFeeBulk(bytes[] payloads, bytes parameterPayload, address subscriber) external payable
15 | ```
16 |
--------------------------------------------------------------------------------
/api_reference/solidity/data-streams/interfaces/index.mdx:
--------------------------------------------------------------------------------
1 | # Interfaces API Reference
2 |
3 | - [IRewardManager](IRewardManager.mdx)
4 | - [IVerifier](IVerifier.mdx)
5 | - [IVerifierFeeManager](IVerifierFeeManager.mdx)
6 |
--------------------------------------------------------------------------------
/api_reference/solidity/index.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API Reference
2 |
3 | - [ccip](ccip/index.mdx)
4 | - [data-feeds](data-feeds/index.mdx)
5 | - [data-streams](data-streams/index.mdx)
6 | - [shared](shared/index.mdx)
7 |
--------------------------------------------------------------------------------
/api_reference/solidity/shared/LinkToken.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## LinkToken
4 |
5 | This contract implements the ChainLink Token (LINK) using the ERC677 standard.
6 |
7 | _Inherits from the ERC677 token contract and initializes with a fixed total
8 | supply and standard token details._
9 |
10 | ### constructor
11 |
12 | ```solidity
13 | constructor() public
14 | ```
15 |
16 | Constructor to initialize the LinkToken contract with a fixed total supply,
17 | name, and symbol.
18 |
19 | _Calls the ERC677 constructor with the name and symbol, and then mints the total
20 | supply to the contract deployer._
21 |
22 | ### \_onCreate
23 |
24 | ```solidity
25 | function _onCreate() internal virtual
26 | ```
27 |
28 | Hook that is called when this contract is created.
29 |
30 | _Useful to override constructor behaviour in child contracts (e.g., LINK bridge
31 | tokens). The default implementation mints 10\*\*27 tokens to the contract
32 | deployer._
33 |
--------------------------------------------------------------------------------
/api_reference/solidity/shared/WETH9.mdx:
--------------------------------------------------------------------------------
1 | # Solidity API
2 |
3 | ## WETH9
4 |
5 | ### name
6 |
7 | ```solidity
8 | string name
9 | ```
10 |
11 | ### symbol
12 |
13 | ```solidity
14 | string symbol
15 | ```
16 |
17 | ### decimals
18 |
19 | ```solidity
20 | uint8 decimals
21 | ```
22 |
23 | ### Approval
24 |
25 | ```solidity
26 | event Approval(address src, address guy, uint256 wad)
27 | ```
28 |
29 | ### Transfer
30 |
31 | ```solidity
32 | event Transfer(address src, address dst, uint256 wad)
33 | ```
34 |
35 | ### Deposit
36 |
37 | ```solidity
38 | event Deposit(address dst, uint256 wad)
39 | ```
40 |
41 | ### Withdrawal
42 |
43 | ```solidity
44 | event Withdrawal(address src, uint256 wad)
45 | ```
46 |
47 | ### balanceOf
48 |
49 | ```solidity
50 | mapping(address => uint256) balanceOf
51 | ```
52 |
53 | ### allowance
54 |
55 | ```solidity
56 | mapping(address => mapping(address => uint256)) allowance
57 | ```
58 |
59 | ### receive
60 |
61 | ```solidity
62 | receive() external payable
63 | ```
64 |
65 | ### \_deposit
66 |
67 | ```solidity
68 | function _deposit() internal
69 | ```
70 |
71 | ### deposit
72 |
73 | ```solidity
74 | function deposit() external payable
75 | ```
76 |
77 | ### withdraw
78 |
79 | ```solidity
80 | function withdraw(uint256 wad) external
81 | ```
82 |
83 | ### totalSupply
84 |
85 | ```solidity
86 | function totalSupply() public view returns (uint256)
87 | ```
88 |
89 | ### approve
90 |
91 | ```solidity
92 | function approve(address guy, uint256 wad) public returns (bool)
93 | ```
94 |
95 | ### transfer
96 |
97 | ```solidity
98 | function transfer(address dst, uint256 wad) public returns (bool)
99 | ```
100 |
101 | ### transferFrom
102 |
103 | ```solidity
104 | function transferFrom(address src, address dst, uint256 wad) public returns (bool)
105 | ```
106 |
--------------------------------------------------------------------------------
/api_reference/solidity/shared/index.mdx:
--------------------------------------------------------------------------------
1 | # Shared API Reference
2 |
3 | - [LinkToken](LinkToken.mdx)
4 | - [WETH9](WETH9.mdx)
5 |
--------------------------------------------------------------------------------
/assets/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartcontractkit/chainlink-local/e06bc4085c6f3607f6a83fa19c2d24033e949993/assets/thumbnail.png
--------------------------------------------------------------------------------
/foundry.toml:
--------------------------------------------------------------------------------
1 | [profile.default]
2 | src = 'src'
3 | solc = '0.8.24'
4 | evm_version = 'paris'
5 | out = 'out'
6 | libs = ['lib']
7 | test = 'test'
8 | cache_path = 'cache_forge'
9 | remappings = [
10 | '@chainlink/contracts-ccip/=lib/ccip/contracts/',
11 | '@chainlink/contracts/=lib/chainlink-brownie-contracts/contracts/',
12 | '@chainlink/local/src/=src/',
13 | ]
14 |
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | import * as dotenv from "dotenv";
2 | import "solidity-docgen";
3 |
4 | import { HardhatUserConfig } from "hardhat/config";
5 | import "@nomicfoundation/hardhat-toolbox";
6 | import "@nomicfoundation/hardhat-foundry";
7 |
8 | dotenv.config();
9 |
10 | const config: HardhatUserConfig = {
11 | solidity: {
12 | compilers: [
13 | {
14 | version: "0.8.24",
15 | settings: {
16 | evmVersion: "paris"
17 | },
18 | }
19 | ]
20 | },
21 | paths: {
22 | sources: "./src",
23 | },
24 | docgen: {
25 | pages: "files",
26 | pageExtension: ".mdx",
27 | exclude: ["test"],
28 | outputDir: "api_reference/solidity",
29 | },
30 | };
31 |
32 | export default config;
33 |
--------------------------------------------------------------------------------
/helper_doc/generate-index-files.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs-extra";
2 | import path from "path";
3 |
4 | const MDX_EXTENSION = ".mdx";
5 | const rootDir = path.join(process.cwd(), "api_reference");
6 |
7 | const generateIndexFile = async (dir: string, header: string) => {
8 | const files = await fs.readdir(dir);
9 | const mdxFiles = files.filter(
10 | (file) => file.endsWith(MDX_EXTENSION) && file !== `index${MDX_EXTENSION}`
11 | );
12 | const subDirs = files.filter((file) =>
13 | fs.lstatSync(path.join(dir, file)).isDirectory()
14 | );
15 |
16 | let content = `# ${header}\n\n`;
17 |
18 | const allEntries = [...mdxFiles, ...subDirs];
19 |
20 | if (allEntries.length > 0) {
21 | allEntries.forEach((entry) => {
22 | const fileNameWithoutExtension = path.basename(entry, MDX_EXTENSION);
23 | const linkName = entry.endsWith(MDX_EXTENSION)
24 | ? fileNameWithoutExtension
25 | : entry;
26 | const linkPath = entry.endsWith(MDX_EXTENSION)
27 | ? entry
28 | : `${entry}/index${MDX_EXTENSION}`;
29 | content += `- [${linkName}](${linkPath})\n`;
30 | });
31 | }
32 |
33 | await fs.writeFile(path.join(dir, `index${MDX_EXTENSION}`), content);
34 | };
35 |
36 | const traverseDirectory = async (dir: string, header: string) => {
37 | await generateIndexFile(dir, header);
38 |
39 | const files = await fs.readdir(dir);
40 | const subDirs = files.filter((file) =>
41 | fs.lstatSync(path.join(dir, file)).isDirectory()
42 | );
43 |
44 | for (const subDir of subDirs) {
45 | await traverseDirectory(
46 | path.join(dir, subDir),
47 | `${subDir.charAt(0).toUpperCase() + subDir.slice(1)} API Reference`
48 | );
49 | }
50 | };
51 |
52 | traverseDirectory(rootDir, "API Reference")
53 | .then(() => console.log("Index files generated successfully."))
54 | .catch((err) => console.error(err));
55 |
--------------------------------------------------------------------------------
/helper_doc/generate-jsdoc.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra';
2 | import path from 'path';
3 | import jsdoc2md from 'jsdoc-to-markdown';
4 |
5 | const MDX_EXTENSION = '.mdx';
6 | const outputDir = path.join(process.cwd(), 'api_reference/javascript');
7 |
8 | const jsFiles = ['scripts/CCIPLocalSimulatorFork.js'];
9 |
10 | const generateMarkdownDocs = async (
11 | files: string[],
12 | outputDirectory: string
13 | ) => {
14 | await fs.ensureDir(outputDirectory);
15 |
16 | for (const file of files) {
17 | const absoluteFilePath = path.join(process.cwd(), file);
18 | const fileName = path.basename(file, path.extname(file));
19 | const outputPath = path.join(
20 | outputDirectory,
21 | `${fileName}${MDX_EXTENSION}`
22 | );
23 | const markdown = await jsdoc2md.render({ files: absoluteFilePath });
24 | const fixedMarkdown = markdown.replace(/<\{/g, '<\\{');
25 | await fs.outputFile(outputPath, fixedMarkdown);
26 | }
27 | };
28 |
29 | generateMarkdownDocs(jsFiles, outputDir)
30 | .then(() => console.log('Markdown documentation generated successfully.'))
31 | .catch(err => console.error(err));
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@chainlink/local",
3 | "description": "Chainlink Local Simulator",
4 | "license": "MIT",
5 | "version": "0.2.4",
6 | "files": [
7 | "src/**/*.sol",
8 | "!src/test/**/*",
9 | "scripts/ccipLocalSimulatorFork.js",
10 | "scripts/data-streams/**/*.js",
11 | "abi/**/*.json"
12 | ],
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/smartcontractkit/chainlink-local.git"
16 | },
17 | "keywords": [
18 | "chainlink"
19 | ],
20 | "scripts": {
21 | "hardhat-compile": "npx hardhat compile",
22 | "forge-compile": "forge build",
23 | "hardhat-test": "npx hardhat test",
24 | "forge-test": "forge test",
25 | "test": "npm run hardhat-test && npm run forge-test",
26 | "prettier": "prettier --write 'api_reference/**/*'",
27 | "generate-solidity": "rimraf api_reference/solidity && npm run hardhat-compile && npm run forge-compile && npx hardhat docgen",
28 | "generate-jsdoc": "rimraf api_reference/javascript && npx ts-node helper_doc/generate-jsdoc.ts",
29 | "generate-index": "npx ts-node helper_doc/generate-index-files.ts",
30 | "generate-docs": "npm run generate-solidity && npm run generate-jsdoc && npm run generate-index && npm run prettier"
31 | },
32 | "devDependencies": {
33 | "@nomicfoundation/hardhat-foundry": "^1.1.1",
34 | "@nomicfoundation/hardhat-toolbox": "^4.0.0",
35 | "@types/fs-extra": "^11.0.4",
36 | "@types/jsdoc-to-markdown": "^7.0.6",
37 | "dotenv": "^16.4.5",
38 | "fs-extra": "^11.2.0",
39 | "hardhat": "^2.20.1",
40 | "jsdoc-to-markdown": "^8.0.1",
41 | "prettier": "^3.3.3",
42 | "rimraf": "^6.0.1",
43 | "solidity-docgen": "^0.6.0-beta.36"
44 | },
45 | "dependencies": {
46 | "@chainlink/contracts": "^1.3.0",
47 | "@chainlink/contracts-ccip": "^1.5.1-beta.0"
48 | }
49 | }
--------------------------------------------------------------------------------
/scripts/CCIPLocalSimulatorFork.js:
--------------------------------------------------------------------------------
1 | const { ethers } = require("hardhat");
2 | const { setBalance } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
3 |
4 | const RouterAbi = require("../abi/Router.json");
5 | const LinkTokenAbi = require("../abi/LinkToken.json");
6 | const EVM2EVMOnRampAbi = require("../abi/EVM2EVMOnRamp.json");
7 | const EVM2EVMOffRampAbi = require("../abi/EVM2EVMOffRamp.json");
8 |
9 | /**
10 | * Requests LINK tokens from the faucet and returns the transaction hash
11 | *
12 | * @param {string} linkAddress The address of the LINK contract on the current network
13 | * @param {string} to The address to send LINK to
14 | * @param {bigint} amount The amount of LINK to request
15 | * @returns {Promise} Promise resolving to the transaction hash of the fund transfer
16 | */
17 | async function requestLinkFromTheFaucet(linkAddress, to, amount) {
18 | const LINK_FAUCET_ADDRESS = `0x4281eCF07378Ee595C564a59048801330f3084eE`;
19 | const linkFaucetImpersonated = await ethers.getImpersonatedSigner(LINK_FAUCET_ADDRESS);
20 |
21 | const linkToken = new ethers.Contract(linkAddress, LinkTokenAbi, ethers.provider);
22 | const tx = await linkToken.connect(linkFaucetImpersonated).transfer(to, amount);
23 |
24 | return tx.hash;
25 | }
26 |
27 | /**
28 | * @typedef {Object} Evm2EvmMessage
29 | * @property {bigint} sourceChainSelector
30 | * @property {string} sender
31 | * @property {string} receiver
32 | * @property {bigint} sequenceNumber
33 | * @property {bigint} gasLimit
34 | * @property {boolean} strict
35 | * @property {bigint} nonce
36 | * @property {string} feeToken
37 | * @property {bigint} feeTokenAmount
38 | * @property {string} data
39 | * @property {Array<{token: string, amount: bigint}>} tokenAmounts
40 | * @property {Array} sourceTokenData
41 | * @property {string} messageId
42 | */
43 |
44 | /**
45 | * Parses a transaction receipt to extract the sent message
46 | * Scans through transaction logs to find a `CCIPSendRequested` event and then decodes it to an object
47 | *
48 | * @param {object} receipt - The transaction receipt from the `ccipSend` call
49 | * @returns {Evm2EvmMessage | null} Returns either the sent message or null if provided receipt does not contain `CCIPSendRequested` log
50 | */
51 | function getEvm2EvmMessage(receipt) {
52 | const evm2EvmOnRampInterface = new ethers.Interface(EVM2EVMOnRampAbi);
53 |
54 | for (const log of receipt.logs) {
55 | try {
56 | const parsedLog = evm2EvmOnRampInterface.parseLog(log);
57 | if (parsedLog?.name == `CCIPSendRequested`) {
58 | const [
59 | sourceChainSelector,
60 | sender,
61 | receiver,
62 | sequenceNumber,
63 | gasLimit,
64 | strict,
65 | nonce,
66 | feeToken,
67 | feeTokenAmount,
68 | data,
69 | tokenAmountsRaw,
70 | sourceTokenDataRaw,
71 | messageId,
72 | ] = parsedLog?.args[0];
73 | const tokenAmounts = tokenAmountsRaw.map(([token, amount]) => ({
74 | token,
75 | amount,
76 | }));
77 | const sourceTokenData = sourceTokenDataRaw.map(data => data);
78 | const evm2EvmMessage = {
79 | sourceChainSelector,
80 | sender,
81 | receiver,
82 | sequenceNumber,
83 | gasLimit,
84 | strict,
85 | nonce,
86 | feeToken,
87 | feeTokenAmount,
88 | data,
89 | tokenAmounts,
90 | sourceTokenData,
91 | messageId,
92 | };
93 | return evm2EvmMessage;
94 | }
95 | } catch (error) {
96 | return null;
97 | }
98 | }
99 |
100 | return null;
101 | }
102 |
103 | /**
104 | * Routes the sent message from the source network on the destination (current) network
105 | *
106 | * @param {string} routerAddress - Address of the destination Router
107 | * @param {Evm2EvmMessage} evm2EvmMessage - Sent cross-chain message
108 | * @returns {Promise} Either resolves with no value if the message is successfully routed, or reverts
109 | * @throws {Error} Fails if no off-ramp matches the message's source chain selector or if calling `router.getOffRamps()`
110 | */
111 | async function routeMessage(routerAddress, evm2EvmMessage) {
112 | const router = new ethers.Contract(routerAddress, RouterAbi, ethers.provider);
113 |
114 | let offRamps;
115 |
116 | try {
117 | const offRampsRaw = await router.getOffRamps();
118 | offRamps = offRampsRaw.map(([sourceChainSelector, offRamp]) => ({ sourceChainSelector, offRamp }));
119 | } catch (error) {
120 | throw new Error(`Calling router.getOffRamps threw the following error: ${error}`);
121 | }
122 |
123 | for (const offRamp of offRamps) {
124 | if (offRamp.sourceChainSelector == evm2EvmMessage.sourceChainSelector) {
125 | const evm2EvmOffRamp = new ethers.Contract(offRamp.offRamp, EVM2EVMOffRampAbi);
126 |
127 | const self = await ethers.getImpersonatedSigner(offRamp.offRamp);
128 | await setBalance(self.address, BigInt(100) ** BigInt(18));
129 |
130 | const offchainTokenData = new Array(evm2EvmMessage.tokenAmounts.length).fill("0x");
131 |
132 | await evm2EvmOffRamp.connect(self).executeSingleMessage(evm2EvmMessage, offchainTokenData);
133 |
134 | return;
135 | }
136 | }
137 |
138 | throw new Error(`No offRamp contract found, message has not been routed. Check your input parameters please`);
139 | }
140 |
141 | module.exports = {
142 | requestLinkFromTheFaucet,
143 | getEvm2EvmMessage,
144 | routeMessage
145 | };
146 |
--------------------------------------------------------------------------------
/scripts/data-streams/DataStreamsLocalSimulatorFork.js:
--------------------------------------------------------------------------------
1 | const { ethers } = require("hardhat");
2 | const { setBalance } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
3 |
4 | const LinkTokenAbi = require("../../abi/LinkToken.json");
5 |
6 | /**
7 | * Requests LINK tokens from the faucet and returns the transaction hash
8 | *
9 | * @param {string} linkAddress The address of the LINK contract on the current network
10 | * @param {string} to The address to send LINK to
11 | * @param {bigint} amount The amount of LINK to request
12 | * @returns {Promise} Promise resolving to the transaction hash of the fund transfer
13 | */
14 | async function requestLinkFromFaucet(linkAddress, to, amount) {
15 | const LINK_FAUCET_ADDRESS = `0x4281eCF07378Ee595C564a59048801330f3084eE`;
16 | const linkFaucetImpersonated = await ethers.getImpersonatedSigner(LINK_FAUCET_ADDRESS);
17 |
18 | const linkToken = new ethers.Contract(linkAddress, LinkTokenAbi, ethers.provider);
19 | const tx = await linkToken.connect(linkFaucetImpersonated).transfer(to, amount);
20 |
21 | return tx.hash;
22 | }
23 |
24 | /**
25 | * Requests native coins from the faucet
26 | *
27 | * @param {string} to The address to send coins to
28 | * @param {bigint} amount The amount of coins to request
29 | */
30 | async function requestNativeFromFaucet(to, amount) {
31 | await setBalance(to, amount);
32 | }
33 |
34 | module.exports = {
35 | requestLinkFromFaucet,
36 | requestNativeFromFaucet
37 | };
--------------------------------------------------------------------------------
/scripts/data-streams/ReportVersions.js:
--------------------------------------------------------------------------------
1 | class ReportV2 {
2 | constructor({
3 | feedId,
4 | validFromTimestamp,
5 | observationsTimestamp,
6 | nativeFee,
7 | linkFee,
8 | expiresAt,
9 | benchmarkPrice,
10 | }) {
11 | this.feedId = feedId; // (bytes32) The feed ID the report has data for
12 | this.validFromTimestamp = validFromTimestamp; // (uint32) Earliest timestamp for which price is applicable
13 | this.observationsTimestamp = observationsTimestamp; // (uint32) Latest timestamp for which price is applicable
14 | this.nativeFee = nativeFee; // (uint192) Base cost to validate a transaction using the report, denominated in the chain’s native token (WETH/ETH)
15 | this.linkFee = linkFee; // (uint192) Base cost to validate a transaction using the report, denominated in LINK
16 | this.expiresAt = expiresAt; // (uint32) Latest timestamp where the report can be verified on-chain
17 | this.benchmarkPrice = benchmarkPrice; // (int192) DON consensus median price, carried to 8 decimal places
18 | }
19 | }
20 |
21 | class ReportV3 {
22 | constructor({
23 | feedId,
24 | validFromTimestamp,
25 | observationsTimestamp,
26 | nativeFee,
27 | linkFee,
28 | expiresAt,
29 | price,
30 | bid,
31 | ask,
32 | }) {
33 | this.feedId = feedId; // (bytes32) The stream ID the report has data for
34 | this.validFromTimestamp = validFromTimestamp; // (uint32) Earliest timestamp for which price is applicable
35 | this.observationsTimestamp = observationsTimestamp; // (uint32) Latest timestamp for which price is applicable
36 | this.nativeFee = nativeFee; // (uint192) Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH)
37 | this.linkFee = linkFee; // (uint192) Base cost to validate a transaction using the report, denominated in LINK
38 | this.expiresAt = expiresAt; // (uint32) Latest timestamp where the report can be verified on-chain
39 | this.price = price; // (int192) DON consensus median price (8 or 18 decimals)
40 | this.bid = bid; // (int192) Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals)
41 | this.ask = ask; // (int192) Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18 decimals)
42 | }
43 | }
44 |
45 | class ReportV4 {
46 | constructor({
47 | feedId,
48 | validFromTimestamp,
49 | observationsTimestamp,
50 | nativeFee,
51 | linkFee,
52 | expiresAt,
53 | price,
54 | marketStatus,
55 | }) {
56 | this.feedId = feedId; // (bytes32) The stream ID the report has data for
57 | this.validFromTimestamp = validFromTimestamp; // (uint32) Earliest timestamp for which price is applicable
58 | this.observationsTimestamp = observationsTimestamp; // (uint32) Latest timestamp for which price is applicable
59 | this.nativeFee = nativeFee; // (uint192) Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH)
60 | this.linkFee = linkFee; // (uint192) Base cost to validate a transaction using the report, denominated in LINK
61 | this.expiresAt = expiresAt; // (uint32) Latest timestamp where the report can be verified on-chain
62 | this.price = price; // (int192) DON consensus median benchmark price (8 or 18 decimals)
63 | this.marketStatus = marketStatus; // (uint32) The DON's consensus on whether the market is currently open
64 | }
65 | }
66 |
67 | module.exports = {
68 | ReportV2,
69 | ReportV3,
70 | ReportV4
71 | }
--------------------------------------------------------------------------------
/scripts/examples/DataStreamsConsumerFork.ts:
--------------------------------------------------------------------------------
1 | import { ethers, network } from "hardhat";
2 | import { requestLinkFromFaucet, requestNativeFromFaucet } from "../data-streams/DataStreamsLocalSimulatorFork";
3 |
4 | // 1st Terminal: npx hardhat node
5 | // 2nd Terminal: npx hardhat run ./scripts/examples/DataStreamsConsumerFork.ts --network localhost
6 |
7 | async function main() {
8 | const verifierProxyInterface = new ethers.Interface([
9 | "function verify(bytes calldata payload, bytes calldata parameterPayload) external payable returns (bytes memory verifierResponse)",
10 | "function s_feeManager() external view returns (address)"
11 | ]);
12 |
13 | const feeManagerInterface = new ethers.Interface([
14 | "function getFeeAndReward(address subscriber, bytes memory unverifiedReport, address quoteAddress) external returns ((address token,uint256 amount) memory, (address token,uint256 amount) memory, uint256)",
15 | "function i_linkAddress() external view returns (address)",
16 | "function i_nativeAddress() external view returns (address)",
17 | "function i_rewardManager() external view returns (address)"
18 | ]);
19 |
20 | const erc20Interface = new ethers.Interface(["function approve(address spender, uint256 amount) external returns (bool)"]);
21 |
22 | const ARBITRUM_SEPOLIA_RPC_URL = process.env.ARBITRUM_SEPOLIA_RPC_URL;
23 |
24 | await network.provider.request({
25 | method: "hardhat_reset",
26 | params: [{
27 | forking: {
28 | jsonRpcUrl: ARBITRUM_SEPOLIA_RPC_URL,
29 | blockNumber: 99556570
30 | },
31 | }],
32 | });
33 |
34 | const [alice] = await ethers.getSigners();
35 |
36 | const UNVERIFIED_INPUT_REPORT = "0x0006f9b553e393ced311551efd30d1decedb63d76ad41737462e2cdbbdff1578000000000000000000000000000000000000000000000000000000004b0b4006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba7820000000000000000000000000000000000000000000000000000000067407f400000000000000000000000000000000000000000000000000000000067407f4000000000000000000000000000000000000000000000000000001b1fa2a2d6400000000000000000000000000000000000000000000000000016e76dd08b2c34000000000000000000000000000000000000000000000000000000006741d0c00000000000000000000000000000000000000000000000b5c654dd994866cb600000000000000000000000000000000000000000000000b5c6042e4f23a92c400000000000000000000000000000000000000000000000b5c7dc7c8e30df80000000000000000000000000000000000000000000000000000000000000000002f68eeb7e489bef244eb83d56e1b8af210a65363e5ad4407f374e5c06f46db98c36b4181b70329535011ba504fb8e09570c10a6de7f1f138cf322237c72cb0d650000000000000000000000000000000000000000000000000000000000000002648579956ffb863e4ea9470a87bb59b26e91ea893f96c2ab933786e531f2701a06d92b4e896a42d40d19a3b5ba1711d5f00bc262084d7afce44a5bbde3014fe5";
37 |
38 | const EXPECTED_REPORT_DATA = {
39 | feedId: "0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782",
40 | validFromTimestamp: 1732280128,
41 | observationsTimestamp: 1732280128,
42 | nativeFee: 29822686516800,
43 | linkFee: 6446908323867700,
44 | expiresAt: 1732366528,
45 | price: 3353151968509396700000n,
46 | bid: 3353129257778281000000n,
47 | ask: 3353262200000000000000n
48 | };
49 |
50 | const verifierProxyAddress = `0x2ff010DEbC1297f19579B4246cad07bd24F2488A`;
51 | const verifierProxy = new ethers.Contract(verifierProxyAddress, verifierProxyInterface, alice);
52 |
53 | const feeManagerAddress = await verifierProxy.s_feeManager();
54 | const feeManager = new ethers.Contract(feeManagerAddress, feeManagerInterface, alice);
55 |
56 | const rewardManagerAddress = await feeManager.i_rewardManager();
57 |
58 | const defaultAbiCoder = ethers.AbiCoder.defaultAbiCoder();
59 |
60 | const [, reportData] = defaultAbiCoder.decode(["bytes32[3]", "bytes"], UNVERIFIED_INPUT_REPORT);
61 |
62 | console.log(reportData);
63 |
64 | // Pay for verification in LINK
65 | let feeTokenAddress = await feeManager.i_linkAddress();
66 |
67 | await requestLinkFromFaucet(feeTokenAddress, alice.address, EXPECTED_REPORT_DATA.linkFee);
68 |
69 | const linkToken = new ethers.Contract(feeTokenAddress, erc20Interface, alice);
70 | await linkToken.approve(rewardManagerAddress, EXPECTED_REPORT_DATA.linkFee);
71 |
72 | let parameterPayload = defaultAbiCoder.encode(["address"], [feeTokenAddress]);
73 |
74 | await verifierProxy.verify(UNVERIFIED_INPUT_REPORT, parameterPayload); // this must not revert
75 |
76 | // Pay for verification in Native
77 | feeTokenAddress = await feeManager.i_nativeAddress();
78 | const gasCosts = ethers.parseEther("0.1");
79 | await requestNativeFromFaucet(alice.address, EXPECTED_REPORT_DATA.nativeFee + Number(gasCosts));
80 |
81 | parameterPayload = defaultAbiCoder.encode(["address"], [feeTokenAddress]);
82 |
83 | await verifierProxy.verify(UNVERIFIED_INPUT_REPORT, parameterPayload, { value: EXPECTED_REPORT_DATA.nativeFee }); // this must not revert
84 |
85 | // const txData = verifierProxy.interface.encodeFunctionData("verify", [UNVERIFIED_INPUT_REPORT, parameterPayload]);
86 | // const txResult = await alice.call({
87 | // to: verifierProxyAddress,
88 | // data: txData,
89 | // });
90 | // console.log(txResult);
91 | }
92 |
93 |
94 | main().catch((error) => {
95 | console.error(error);
96 | process.exitCode = 1;
97 | });
98 |
--------------------------------------------------------------------------------
/scripts/examples/UnsafeTokenAndDataTransfer.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "hardhat";
2 |
3 | // npx hardhat run ./scripts/examples/UnsafeTokenAndDataTransfer.ts
4 |
5 | async function main() {
6 | const localSimulatorFactory = await ethers.getContractFactory("CCIPLocalSimulator");
7 | const localSimulator = await localSimulatorFactory.deploy();
8 |
9 | const config: {
10 | chainSelector_: bigint;
11 | sourceRouter_: string;
12 | destinationRouter_: string;
13 | wrappedNative_: string;
14 | linkToken_: string;
15 | ccipBnM_: string;
16 | ccipLnM_: string;
17 | } = await localSimulator.configuration();
18 |
19 | const CCIPSender_UnsafeFactory = await ethers.getContractFactory("CCIPSender_Unsafe");
20 | const CCIPSender_Unsafe = await CCIPSender_UnsafeFactory.deploy(config.linkToken_, config.sourceRouter_);
21 |
22 | console.log("Deployed CCIPSender_Unsafe to: ", CCIPSender_Unsafe.target);
23 |
24 | const CCIPReceiver_UnsafeFactory = await ethers.getContractFactory("CCIPReceiver_Unsafe");
25 | const CCIPReceiver_Unsafe = await CCIPReceiver_UnsafeFactory.deploy(config.destinationRouter_);
26 |
27 | console.log("Deployed CCIPReceiver_Unsafe to: ", CCIPReceiver_Unsafe.target);
28 |
29 | console.log("-------------------------------------------")
30 |
31 | const ccipBnMFactory = await ethers.getContractFactory("BurnMintERC677Helper");
32 | const ccipBnM = ccipBnMFactory.attach(config.ccipBnM_);
33 |
34 | await ccipBnM.drip(CCIPSender_Unsafe.target);
35 |
36 | const textToSend = `Hello World`;
37 | const amountToSend = 100;
38 |
39 | console.log(`Balance of CCIPSender_Unsafe before: `, await ccipBnM.balanceOf(CCIPSender_Unsafe.target));
40 | console.log(`Balance of CCIPReceiver_Unsafe before: `, await ccipBnM.balanceOf(CCIPReceiver_Unsafe.target));
41 | console.log("-------------------------------------------")
42 |
43 | const tx = await CCIPSender_Unsafe.send(CCIPReceiver_Unsafe.target, textToSend, config.chainSelector_, config.ccipBnM_, amountToSend);
44 | console.log("Transaction hash: ", tx.hash);
45 |
46 | console.log("-------------------------------------------")
47 | console.log(`Balance of CCIPSender_Unsafe after: `, await ccipBnM.balanceOf(CCIPSender_Unsafe.target));
48 | console.log(`Balance of CCIPReceiver_Unsafe after: `, await ccipBnM.balanceOf(CCIPReceiver_Unsafe.target));
49 |
50 | console.log("-------------------------------------------")
51 | const received = await CCIPReceiver_Unsafe.text();
52 | console.log(`Received:`, received);
53 | }
54 |
55 | // We recommend this pattern to be able to use async/await everywhere
56 | // and properly handle errors.
57 | main().catch((error) => {
58 | console.error(error);
59 | process.exitCode = 1;
60 | });
61 |
--------------------------------------------------------------------------------
/scripts/examples/UnsafeTokenAndDataTransferFork.ts:
--------------------------------------------------------------------------------
1 | import { ethers, network } from "hardhat";
2 | import { getEvm2EvmMessage, requestLinkFromTheFaucet, routeMessage } from "../CCIPLocalSimulatorFork";
3 |
4 | // 1st Terminal: npx hardhat node
5 | // 2nd Terminal: npx hardhat run ./scripts/examples/UnsafeTokenAndDataTransferFork.ts --network localhost
6 |
7 | async function main() {
8 | const ETHEREUM_SEPOLIA_RPC_URL = process.env.ETHEREUM_SEPOLIA_RPC_URL;
9 | const ARBITRUM_SEPOLIA_RPC_URL = process.env.ARBITRUM_SEPOLIA_RPC_URL;
10 |
11 | await network.provider.request({
12 | method: "hardhat_reset",
13 | params: [{
14 | forking: {
15 | jsonRpcUrl: ARBITRUM_SEPOLIA_RPC_URL,
16 | blockNumber: 33079804
17 | },
18 | }],
19 | });
20 |
21 | const ccipRouterAddressArbSepolia = `0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165`;
22 | const ccipBnMTokenAddressArbSepolia = `0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D`
23 |
24 | const CCIPReceiver_UnsafeFactory = await ethers.getContractFactory("CCIPReceiver_Unsafe");
25 | let CCIPReceiver_Unsafe = await CCIPReceiver_UnsafeFactory.deploy(ccipRouterAddressArbSepolia);
26 |
27 | console.log("Deployed CCIPReceiver_Unsafe to: ", CCIPReceiver_Unsafe.target);
28 |
29 | const ccipBnMFactory = await ethers.getContractFactory("BurnMintERC677Helper");
30 | const ccipBnMArbSepolia = ccipBnMFactory.attach(ccipBnMTokenAddressArbSepolia);
31 |
32 | console.log(`Balance of CCIPReceiver_Unsafe before: `, await ccipBnMArbSepolia.balanceOf(CCIPReceiver_Unsafe.target));
33 |
34 | console.log("-------------------------------------------");
35 |
36 |
37 | await network.provider.request({
38 | method: "hardhat_reset",
39 | params: [{
40 | forking: {
41 | jsonRpcUrl: ETHEREUM_SEPOLIA_RPC_URL,
42 | blockNumber: 5663645
43 | },
44 | }],
45 | });
46 |
47 | const linkTokenAddressSepolia = `0x779877A7B0D9E8603169DdbD7836e478b4624789`;
48 | const ccipRouterAddressSepolia = `0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59`;
49 | const ccipBnMTokenAddressSepolia = `0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05`;
50 |
51 | const CCIPSender_UnsafeFactory = await ethers.getContractFactory("CCIPSender_Unsafe");
52 | const CCIPSender_Unsafe = await CCIPSender_UnsafeFactory.deploy(linkTokenAddressSepolia, ccipRouterAddressSepolia);
53 |
54 | console.log("Deployed CCIPSender_Unsafe to: ", CCIPSender_Unsafe.target);
55 |
56 | const ccipBnMSepolia = ccipBnMFactory.attach(ccipBnMTokenAddressSepolia);
57 |
58 | await ccipBnMSepolia.drip(CCIPSender_Unsafe.target);
59 |
60 | const linkAmountForFees = 5000000000000000000n; // 5 LINK
61 | await requestLinkFromTheFaucet(linkTokenAddressSepolia, await CCIPSender_Unsafe.getAddress(), linkAmountForFees);
62 |
63 | const textToSend = `Hello World`;
64 | const amountToSend = 100;
65 | const arbSepoliaChainSelector = 3478487238524512106n;
66 |
67 | console.log(`Balance of CCIPSender_Unsafe before: `, await ccipBnMSepolia.balanceOf(CCIPSender_Unsafe.target));
68 |
69 | const tx = await CCIPSender_Unsafe.send(CCIPReceiver_Unsafe.target, textToSend, arbSepoliaChainSelector, ccipBnMTokenAddressSepolia, amountToSend);
70 | console.log("Transaction hash: ", tx.hash);
71 | const receipt = await tx.wait();
72 | if (!receipt) return;
73 | const evm2EvmMessage = getEvm2EvmMessage(receipt);
74 |
75 | console.log(`Balance of CCIPSender_Unsafe after: `, await ccipBnMSepolia.balanceOf(CCIPSender_Unsafe.target));
76 |
77 | console.log("-------------------------------------------");
78 |
79 | await network.provider.request({
80 | method: "hardhat_reset",
81 | params: [{
82 | forking: {
83 | jsonRpcUrl: ARBITRUM_SEPOLIA_RPC_URL,
84 | blockNumber: 33079804
85 | },
86 | }],
87 | });
88 |
89 | // We must redeploy it because of the network reset but it will be deployed to the same address because of the CREATE opcode: address = keccak256(rlp([sender_address,sender_nonce]))[12:]
90 | CCIPReceiver_Unsafe = await CCIPReceiver_UnsafeFactory.deploy(ccipRouterAddressArbSepolia);
91 |
92 | if (!evm2EvmMessage) return;
93 | await routeMessage(ccipRouterAddressArbSepolia, evm2EvmMessage);
94 |
95 | const received = await CCIPReceiver_Unsafe.text();
96 | console.log(`Received:`, received);
97 |
98 | console.log(`Balance of CCIPReceiver_Unsafe after: `, await ccipBnMArbSepolia.balanceOf(CCIPReceiver_Unsafe.target));
99 | }
100 |
101 | // We recommend this pattern to be able to use async/await everywhere
102 | // and properly handle errors.
103 | main().catch((error) => {
104 | console.error(error);
105 | process.exitCode = 1;
106 | });
107 |
--------------------------------------------------------------------------------
/src/ccip/BurnMintERC677Helper.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: BUSL-1.1
2 | pragma solidity ^0.8.19;
3 |
4 | import {BurnMintERC677} from "@chainlink/contracts-ccip/src/v0.8/shared/token/ERC677/BurnMintERC677.sol";
5 |
6 | /// @title BurnMintERC677Helper
7 | /// @notice This contract extends the functionality of the BurnMintERC677 token contract to include a `drip` function that mints one full token to a specified address.
8 | /// @dev Inherits from the BurnMintERC677 contract and sets the token name, symbol, decimals, and initial supply in the constructor.
9 | contract BurnMintERC677Helper is BurnMintERC677 {
10 | /**
11 | * @notice Constructor to initialize the BurnMintERC677Helper contract with a name and symbol.
12 | * @dev Calls the parent constructor of BurnMintERC677 with fixed decimals (18) and initial supply (0).
13 | * @param name - The name of the token.
14 | * @param symbol - The symbol of the token.
15 | */
16 | constructor(
17 | string memory name,
18 | string memory symbol
19 | ) BurnMintERC677(name, symbol, 18, 0) {}
20 |
21 | /**
22 | * @notice Mints one full token (1e18) to the specified address.
23 | * @dev Calls the internal `_mint` function from the BurnMintERC677 contract.
24 | * @param to - The address to receive the minted token.
25 | */
26 | function drip(address to) external {
27 | _mint(to, 1e18);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/data-feeds/MockOffchainAggregator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | /// @title MockOffchainAggregator
5 | /// @notice This contract is a mock implementation of an offchain aggregator for testing purposes.
6 | /// @dev This contract simulates the behavior of an offchain aggregator and allows for updating answers and round data.
7 | contract MockOffchainAggregator {
8 | /// @notice The minimum possible answer the aggregator can report.
9 | int192 private constant MIN_ANSWER_POSSIBLE = 1;
10 |
11 | /// @notice The maximum possible answer the aggregator can report.
12 | int192 private constant MAX_ANSWER_POSSIBLE = 95780971304118053647396689196894323976171195136475135; // type(uint176).max
13 |
14 | /// @notice The number of decimals used by the aggregator.
15 | uint8 public decimals;
16 |
17 | /// @notice The latest answer reported by the aggregator.
18 | int256 public latestAnswer;
19 |
20 | /// @notice The timestamp of the latest answer.
21 | uint256 public latestTimestamp;
22 |
23 | /// @notice The latest round ID.
24 | uint256 public latestRound;
25 |
26 | /// @notice The minimum answer the aggregator is allowed to report.
27 | int192 public minAnswer;
28 |
29 | /// @notice The maximum answer the aggregator is allowed to report.
30 | int192 public maxAnswer;
31 |
32 | /// @notice Mapping to get the answer for a specific round ID.
33 | mapping(uint256 => int256) public getAnswer;
34 |
35 | /// @notice Mapping to get the timestamp for a specific round ID.
36 | mapping(uint256 => uint256) public getTimestamp;
37 |
38 | /// @notice Mapping to get the start time for a specific round ID.
39 | mapping(uint256 => uint256) private getStartedAt;
40 |
41 | /**
42 | * @notice Constructor to initialize the MockOffchainAggregator contract with initial parameters.
43 | * @param _decimals - The number of decimals for the aggregator.
44 | * @param _initialAnswer - The initial answer to be set in the mock aggregator.
45 | */
46 | constructor(uint8 _decimals, int256 _initialAnswer) {
47 | decimals = _decimals;
48 | updateAnswer(_initialAnswer);
49 | minAnswer = MIN_ANSWER_POSSIBLE;
50 | maxAnswer = MAX_ANSWER_POSSIBLE;
51 | }
52 |
53 | /**
54 | * @notice Updates the answer in the mock aggregator.
55 | * @param _answer - The new answer to be set.
56 | */
57 | function updateAnswer(int256 _answer) public {
58 | latestAnswer = _answer;
59 | latestTimestamp = block.timestamp;
60 | latestRound++;
61 | getAnswer[latestRound] = _answer;
62 | getTimestamp[latestRound] = block.timestamp;
63 | getStartedAt[latestRound] = block.timestamp;
64 | }
65 |
66 | /**
67 | * @notice Updates the round data in the mock aggregator.
68 | * @param _roundId - The round ID to be updated.
69 | * @param _answer - The new answer to be set.
70 | * @param _timestamp - The timestamp to be set.
71 | * @param _startedAt - The timestamp when the round started.
72 | */
73 | function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public {
74 | latestRound = _roundId;
75 | latestAnswer = _answer;
76 | latestTimestamp = _timestamp;
77 | getAnswer[latestRound] = _answer;
78 | getTimestamp[latestRound] = _timestamp;
79 | getStartedAt[latestRound] = _startedAt;
80 | }
81 |
82 | /**
83 | * @notice Gets the round data for a specific round ID.
84 | * @param _roundId - The round ID to get the data for.
85 | * @return roundId - The round ID.
86 | * @return answer - The answer for the round.
87 | * @return startedAt - The timestamp when the round started.
88 | * @return updatedAt - The timestamp when the round was updated.
89 | * @return answeredInRound - The round ID in which the answer was computed.
90 | */
91 | function getRoundData(uint80 _roundId)
92 | external
93 | view
94 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
95 | {
96 | return (_roundId, getAnswer[_roundId], getStartedAt[_roundId], getTimestamp[_roundId], _roundId);
97 | }
98 |
99 | /**
100 | * @notice Gets the latest round data.
101 | * @return roundId - The latest round ID.
102 | * @return answer - The latest answer.
103 | * @return startedAt - The timestamp when the latest round started.
104 | * @return updatedAt - The timestamp when the latest round was updated.
105 | * @return answeredInRound - The round ID in which the latest answer was computed.
106 | */
107 | function latestRoundData()
108 | external
109 | view
110 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
111 | {
112 | return (
113 | uint80(latestRound),
114 | getAnswer[latestRound],
115 | getStartedAt[latestRound],
116 | getTimestamp[latestRound],
117 | uint80(latestRound)
118 | );
119 | }
120 |
121 | /**
122 | * @notice Updates the minimum and maximum answers the aggregator can report.
123 | * @param _minAnswer - The new minimum answer.
124 | * @param _maxAnswer - The new maximum answer.
125 | */
126 | function updateMinAndMaxAnswers(int192 _minAnswer, int192 _maxAnswer) external {
127 | require(_minAnswer < _maxAnswer, "minAnswer must be less than maxAnswer");
128 | require(_minAnswer >= MIN_ANSWER_POSSIBLE, "minAnswer is too low");
129 | require(_maxAnswer <= MAX_ANSWER_POSSIBLE, "maxAnswer is too high");
130 |
131 | minAnswer = _minAnswer;
132 | maxAnswer = _maxAnswer;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/data-feeds/MockV3Aggregator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | import {
5 | AggregatorInterface,
6 | AggregatorV3Interface,
7 | AggregatorV2V3Interface
8 | } from "./interfaces/AggregatorV2V3Interface.sol";
9 | import {MockOffchainAggregator} from "./MockOffchainAggregator.sol";
10 |
11 | /// @title MockV3Aggregator
12 | /// @notice This contract is a mock implementation of the AggregatorV2V3Interface for testing purposes.
13 | /// @dev This contract interacts with a MockOffchainAggregator to simulate price feeds.
14 | contract MockV3Aggregator is AggregatorV2V3Interface {
15 | /// @notice The version of the aggregator.
16 | uint256 public constant override version = 0;
17 |
18 | /// @notice The address of the current aggregator.
19 | address public aggregator;
20 |
21 | /// @notice The address of the proposed aggregator.
22 | address public proposedAggregator;
23 |
24 | /**
25 | * @notice Constructor to initialize the MockV3Aggregator contract with initial parameters.
26 | * @param _decimals - The number of decimals for the aggregator.
27 | * @param _initialAnswer - The initial answer to be set in the mock aggregator.
28 | */
29 | constructor(uint8 _decimals, int256 _initialAnswer) {
30 | aggregator = address(new MockOffchainAggregator(_decimals, _initialAnswer));
31 | proposedAggregator = address(0);
32 | }
33 |
34 | /**
35 | * @inheritdoc AggregatorV3Interface
36 | */
37 | function decimals() external view override returns (uint8) {
38 | return AggregatorV2V3Interface(aggregator).decimals();
39 | }
40 |
41 | /**
42 | * @inheritdoc AggregatorInterface
43 | */
44 | function getAnswer(uint256 roundId) external view override returns (int256) {
45 | return AggregatorV2V3Interface(aggregator).getAnswer(roundId);
46 | }
47 |
48 | /**
49 | * @inheritdoc AggregatorV3Interface
50 | */
51 | function getRoundData(uint80 _roundId)
52 | external
53 | view
54 | override
55 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
56 | {
57 | return AggregatorV2V3Interface(aggregator).getRoundData(_roundId);
58 | }
59 |
60 | /**
61 | * @inheritdoc AggregatorV3Interface
62 | */
63 | function latestRoundData()
64 | external
65 | view
66 | override
67 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
68 | {
69 | return AggregatorV2V3Interface(aggregator).latestRoundData();
70 | }
71 |
72 | /**
73 | * @inheritdoc AggregatorInterface
74 | */
75 | function getTimestamp(uint256 roundId) external view override returns (uint256) {
76 | return AggregatorV2V3Interface(aggregator).getTimestamp(roundId);
77 | }
78 |
79 | /**
80 | * @inheritdoc AggregatorInterface
81 | */
82 | function latestAnswer() external view override returns (int256) {
83 | return AggregatorV2V3Interface(aggregator).latestAnswer();
84 | }
85 |
86 | /**
87 | * @inheritdoc AggregatorInterface
88 | */
89 | function latestTimestamp() external view override returns (uint256) {
90 | return AggregatorV2V3Interface(aggregator).latestTimestamp();
91 | }
92 |
93 | /**
94 | * @inheritdoc AggregatorInterface
95 | */
96 | function latestRound() external view override returns (uint256) {
97 | return AggregatorV2V3Interface(aggregator).latestRound();
98 | }
99 |
100 | /**
101 | * @notice Updates the answer in the mock aggregator.
102 | * @param _answer - The new answer to be set.
103 | */
104 | function updateAnswer(int256 _answer) public {
105 | MockOffchainAggregator(aggregator).updateAnswer(_answer);
106 | }
107 |
108 | /**
109 | * @notice Updates the round data in the mock aggregator.
110 | * @param _roundId - The round ID to be updated.
111 | * @param _answer - The new answer to be set.
112 | * @param _timestamp - The timestamp to be set.
113 | * @param _startedAt - The timestamp when the round started.
114 | */
115 | function updateRoundData(uint80 _roundId, int256 _answer, uint256 _timestamp, uint256 _startedAt) public {
116 | MockOffchainAggregator(aggregator).updateRoundData(_roundId, _answer, _timestamp, _startedAt);
117 | }
118 |
119 | /**
120 | * @notice Proposes a new aggregator.
121 | * @param _aggregator - The address of the proposed aggregator.
122 | */
123 | function proposeAggregator(AggregatorV2V3Interface _aggregator) external {
124 | require(address(_aggregator) != address(0), "Proposed aggregator cannot be zero address");
125 | require(address(_aggregator) != aggregator, "Proposed aggregator cannot be current aggregator");
126 | proposedAggregator = address(_aggregator);
127 | }
128 |
129 | /**
130 | * @notice Confirms the proposed aggregator.
131 | * @param _aggregator - The address of the proposed aggregator.
132 | */
133 | function confirmAggregator(address _aggregator) external {
134 | require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
135 | aggregator = proposedAggregator;
136 | proposedAggregator = address(0);
137 | }
138 |
139 | /**
140 | * @inheritdoc AggregatorV3Interface
141 | */
142 | function description() external pure override returns (string memory) {
143 | return "src/data-feeds/MockV3Aggregator.sol";
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/data-feeds/interfaces/AggregatorInterface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | /// @title AggregatorInterface
5 | /// @notice Interface for accessing data from an aggregator contract.
6 | /// @dev Provides methods to get the latest data and historical data for specific rounds.
7 | interface AggregatorInterface {
8 | /**
9 | * @notice Gets the latest answer from the aggregator.
10 | * @return int256 - The latest answer.
11 | */
12 | function latestAnswer() external view returns (int256);
13 |
14 | /**
15 | * @notice Gets the timestamp of the latest answer from the aggregator.
16 | * @return uint256 - The timestamp of the latest answer.
17 | */
18 | function latestTimestamp() external view returns (uint256);
19 |
20 | /**
21 | * @notice Gets the latest round ID from the aggregator.
22 | * @return uint256 - The latest round ID.
23 | */
24 | function latestRound() external view returns (uint256);
25 |
26 | /**
27 | * @notice Gets the answer for a specific round ID.
28 | * @param roundId - The round ID to get the answer for.
29 | * @return int256 - The answer for the given round ID.
30 | */
31 | function getAnswer(uint256 roundId) external view returns (int256);
32 |
33 | /**
34 | * @notice Gets the timestamp for a specific round ID.
35 | * @param roundId - The round ID to get the timestamp for.
36 | * @return uint256 - The timestamp for the given round ID.
37 | */
38 | function getTimestamp(uint256 roundId) external view returns (uint256);
39 |
40 | /**
41 | * @notice Emitted when the answer is updated.
42 | * @param current - The updated answer.
43 | * @param roundId - The round ID for which the answer was updated.
44 | * @param updatedAt - The timestamp when the answer was updated.
45 | */
46 | event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
47 |
48 | /**
49 | * @notice Emitted when a new round is started.
50 | * @param roundId - The round ID of the new round.
51 | * @param startedBy - The address of the account that started the round.
52 | * @param startedAt - The timestamp when the round was started.
53 | */
54 | event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
55 | }
56 |
--------------------------------------------------------------------------------
/src/data-feeds/interfaces/AggregatorV2V3Interface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | import {AggregatorInterface} from "./AggregatorInterface.sol";
5 | import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";
6 |
7 | /// @title AggregatorV2V3Interface
8 | /// @notice Interface that inherits from both AggregatorInterface and AggregatorV3Interface.
9 | interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
10 |
--------------------------------------------------------------------------------
/src/data-feeds/interfaces/AggregatorV3Interface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | /// @title AggregatorV3Interface
5 | /// @notice Interface for accessing detailed data from an aggregator contract, including round data and metadata.
6 | /// @dev Provides methods to get the latest data, historical data for specific rounds, and metadata such as decimals and description.
7 | interface AggregatorV3Interface {
8 | /**
9 | * @notice Gets the number of decimals used by the aggregator.
10 | * @return uint8 - The number of decimals.
11 | */
12 | function decimals() external view returns (uint8);
13 |
14 | /**
15 | * @notice Gets the description of the aggregator.
16 | * @return string memory - The description of the aggregator.
17 | */
18 | function description() external view returns (string memory);
19 |
20 | /**
21 | * @notice Gets the version of the aggregator.
22 | * @return uint256 - The version of the aggregator.
23 | */
24 | function version() external view returns (uint256);
25 |
26 | /**
27 | * @notice Gets the round data for a specific round ID.
28 | * @param _roundId - The round ID to get the data for.
29 | * @return roundId - The round ID.
30 | * @return answer - The answer for the round.
31 | * @return startedAt - The timestamp when the round started.
32 | * @return updatedAt - The timestamp when the round was updated.
33 | * @return answeredInRound - The round ID in which the answer was computed.
34 | * @dev This function should raise "No data present" if no data is available for the given round ID.
35 | */
36 | function getRoundData(uint80 _roundId)
37 | external
38 | view
39 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
40 |
41 | /**
42 | * @notice Gets the latest round data.
43 | * @return roundId - The latest round ID.
44 | * @return answer - The latest answer.
45 | * @return startedAt - The timestamp when the latest round started.
46 | * @return updatedAt - The timestamp when the latest round was updated.
47 | * @return answeredInRound - The round ID in which the latest answer was computed.
48 | * @dev This function should raise "No data present" if no data is available.
49 | */
50 | function latestRoundData()
51 | external
52 | view
53 | returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
54 | }
55 |
--------------------------------------------------------------------------------
/src/data-streams/DataStreamsLocalSimulator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {WETH9} from "../shared/WETH9.sol";
5 | import {LinkToken} from "../shared/LinkToken.sol";
6 | import {MockVerifier} from "./MockVerifier.sol";
7 | import {MockVerifierProxy} from "./MockVerifierProxy.sol";
8 | import {MockFeeManager} from "./MockFeeManager.sol";
9 | import {MockRewardManager} from "./MockRewardManager.sol";
10 |
11 | contract DataStreamsLocalSimulator {
12 | MockVerifier internal s_mockVerifier;
13 | MockVerifierProxy internal s_mockVerifierProxy;
14 | MockFeeManager internal s_mockFeeManager;
15 | MockRewardManager internal s_mockRewardManager;
16 |
17 | /// @notice The wrapped native token instance
18 | WETH9 internal immutable i_wrappedNative;
19 |
20 | /// @notice The LINK token instance
21 | LinkToken internal immutable i_linkToken;
22 |
23 | constructor() {
24 | i_wrappedNative = new WETH9();
25 | i_linkToken = new LinkToken();
26 |
27 | s_mockVerifierProxy = new MockVerifierProxy();
28 | s_mockVerifier = new MockVerifier(address(s_mockVerifierProxy));
29 | s_mockVerifierProxy.initializeVerifier(address(s_mockVerifier));
30 |
31 | s_mockRewardManager = new MockRewardManager(address(i_linkToken));
32 |
33 | s_mockFeeManager = new MockFeeManager(
34 | address(i_linkToken), address(i_wrappedNative), address(s_mockVerifierProxy), address(s_mockRewardManager)
35 | );
36 |
37 | s_mockVerifierProxy.setFeeManager(s_mockFeeManager);
38 | s_mockRewardManager.setFeeManager(address(s_mockFeeManager));
39 | }
40 |
41 | /**
42 | * @notice Requests LINK tokens from the faucet. The provided amount of tokens are transferred to provided destination address.
43 | *
44 | * @param to - The address to which LINK tokens are to be sent.
45 | * @param amount - The amount of LINK tokens to send.
46 | *
47 | * @return success - Returns `true` if the transfer of tokens was successful, otherwise `false`.
48 | */
49 | function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success) {
50 | success = i_linkToken.transfer(to, amount);
51 | }
52 |
53 | /**
54 | * @notice Returns configuration details for pre-deployed contracts and services needed for local Data Streams simulations.
55 | *
56 | * @return wrappedNative_ - The wrapped native token.
57 | * @return linkToken_ - The LINK token.
58 | * @return mockVerifier_ - The mock verifier contract.
59 | * @return mockVerifierProxy_ - The mock verifier proxy contract.
60 | * @return mockFeeManager_ - The mock fee manager contract.
61 | * @return mockRewardManager_ - The mock reward manager contract.
62 | */
63 | function configuration()
64 | public
65 | view
66 | returns (
67 | WETH9 wrappedNative_,
68 | LinkToken linkToken_,
69 | MockVerifier mockVerifier_,
70 | MockVerifierProxy mockVerifierProxy_,
71 | MockFeeManager mockFeeManager_,
72 | MockRewardManager mockRewardManager_
73 | )
74 | {
75 | return
76 | (i_wrappedNative, i_linkToken, s_mockVerifier, s_mockVerifierProxy, s_mockFeeManager, s_mockRewardManager);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/data-streams/DataStreamsLocalSimulatorFork.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, Vm} from "forge-std/Test.sol";
5 | import {Register} from "./Register.sol";
6 | import {IERC20} from "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol";
7 |
8 | contract DataStreamsLocalSimulatorFork is Test {
9 | /// @notice The immutable register instance
10 | Register immutable i_register;
11 |
12 | /// @notice The address of the LINK faucet
13 | address constant LINK_FAUCET = 0x4281eCF07378Ee595C564a59048801330f3084eE;
14 |
15 | /**
16 | * @notice Constructor to initialize the contract
17 | */
18 | constructor() {
19 | i_register = new Register();
20 | }
21 |
22 | /**
23 | * @notice Returns the default values for currently Data Streams supported networks. If network is not present or some of the values are changed, user can manually add new network details using the `setNetworkDetails` function.
24 | *
25 | * @param chainId - The blockchain network chain ID. For example 11155111 for Ethereum Sepolia.
26 | *
27 | * @return networkDetails - The tuple containing:
28 | * verifierProxyAddress - The address of the Verifier Proxy smart contract.
29 | * linkAddress - The address of the LINK token.
30 | */
31 | function getNetworkDetails(uint256 chainId) external view returns (Register.NetworkDetails memory) {
32 | return i_register.getNetworkDetails(chainId);
33 | }
34 |
35 | /**
36 | * @notice If network details are not present or some of the values are changed, user can manually add new network details using the `setNetworkDetails` function.
37 | *
38 | * @param chainId - The blockchain network chain ID. For example 11155111 for Ethereum Sepolia.
39 | * @param networkDetails - The tuple containing:
40 | * verifierProxyAddress - The address of the Verifier Proxy smart contract.
41 | * linkAddress - The address of the LINK token.
42 | */
43 | function setNetworkDetails(uint256 chainId, Register.NetworkDetails memory networkDetails) external {
44 | i_register.setNetworkDetails(chainId, networkDetails);
45 | }
46 |
47 | /**
48 | * @notice Requests LINK tokens from the faucet. The provided amount of tokens are transferred to provided destination address.
49 | *
50 | * @param to - The address to which LINK tokens are to be sent.
51 | * @param amount - The amount of LINK tokens to send.
52 | *
53 | * @return success - Returns `true` if the transfer of tokens was successful, otherwise `false`.
54 | */
55 | function requestLinkFromFaucet(address to, uint256 amount) external returns (bool success) {
56 | address linkAddress = i_register.getNetworkDetails(block.chainid).linkAddress;
57 |
58 | vm.startPrank(LINK_FAUCET);
59 | success = IERC20(linkAddress).transfer(to, amount);
60 | vm.stopPrank();
61 | }
62 |
63 | /**
64 | * @notice Requests native coints from the faucet.
65 | *
66 | * @param to - The address to which native coins are to be sent.
67 | * @param amount - The amount of native coins to send.
68 | */
69 | function requestNativeFromFaucet(address to, uint256 amount) external {
70 | vm.deal(to, amount);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/data-streams/MockFeeManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol";
5 | import {IERC20} from
6 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
7 | import {SafeERC20} from
8 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
9 | import {IWERC20} from "@chainlink/contracts/src/v0.8/shared/interfaces/IWERC20.sol";
10 | import {Math} from "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol";
11 |
12 | // import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol";
13 | // import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol";
14 | import {IRewardManager} from "./interfaces/IRewardManager.sol";
15 | import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol";
16 |
17 | library Common {
18 | // @notice The asset struct to hold the address of an asset and amount
19 | struct Asset {
20 | address assetAddress;
21 | uint256 amount;
22 | }
23 | }
24 |
25 | contract MockFeeManager is IVerifierFeeManager, OwnerIsCreator {
26 | using SafeERC20 for IERC20;
27 |
28 | error Unauthorized();
29 | error InvalidAddress();
30 | error InvalidQuote();
31 | error ExpiredReport();
32 | error InvalidDiscount();
33 | error InvalidSurcharge();
34 | error InvalidDeposit();
35 |
36 | /// @notice the total discount that can be applied to a fee, 1e18 = 100% discount
37 | uint64 private constant PERCENTAGE_SCALAR = 1e18;
38 |
39 | address public immutable i_linkAddress;
40 | address public immutable i_nativeAddress;
41 | address public immutable i_proxyAddress;
42 | IRewardManager public immutable i_rewardManager;
43 |
44 | uint256 public s_nativeSurcharge;
45 | mapping(address => uint256) public s_mockDiscounts;
46 |
47 | modifier onlyProxy() {
48 | if (msg.sender != i_proxyAddress) revert Unauthorized();
49 | _;
50 | }
51 |
52 | constructor(address linkAddress, address nativeAddress, address proxyAddress, address rewardManager) {
53 | i_linkAddress = linkAddress;
54 | i_nativeAddress = nativeAddress;
55 | i_proxyAddress = proxyAddress;
56 | i_rewardManager = IRewardManager(rewardManager);
57 |
58 | IERC20(i_linkAddress).approve(address(i_rewardManager), type(uint256).max);
59 | }
60 |
61 | function processFee(bytes calldata payload, bytes calldata parameterPayload, address subscriber)
62 | external
63 | payable
64 | override
65 | onlyProxy
66 | {
67 | _processFee(payload, parameterPayload, subscriber);
68 | }
69 |
70 | function processFeeBulk(bytes[] calldata payloads, bytes calldata parameterPayload, address subscriber)
71 | external
72 | payable
73 | override
74 | onlyProxy
75 | {
76 | for (uint256 i = 0; i < payloads.length; i++) {
77 | _processFee(payloads[i], parameterPayload, subscriber);
78 | }
79 | }
80 |
81 | function getFeeAndReward(address subscriber, bytes memory report, address quoteAddress)
82 | public
83 | view
84 | returns (Common.Asset memory, Common.Asset memory, uint256)
85 | {
86 | Common.Asset memory fee;
87 | Common.Asset memory reward;
88 |
89 | //verify the quote payload is a supported token
90 | if (quoteAddress != i_nativeAddress && quoteAddress != i_linkAddress) {
91 | revert InvalidQuote();
92 | }
93 |
94 | //decode the report depending on the version
95 | uint256 linkQuantity;
96 | uint256 nativeQuantity;
97 | uint256 expiresAt;
98 | (,,, nativeQuantity, linkQuantity, expiresAt) =
99 | abi.decode(report, (bytes32, uint32, uint32, uint192, uint192, uint32));
100 |
101 | //read the timestamp bytes from the report data and verify it has not expired
102 | if (expiresAt < block.timestamp) {
103 | revert ExpiredReport();
104 | }
105 |
106 | uint256 discount = s_mockDiscounts[subscriber];
107 |
108 | //the reward is always set in LINK
109 | reward.assetAddress = i_linkAddress;
110 | reward.amount = Math.ceilDiv(linkQuantity * (PERCENTAGE_SCALAR - discount), PERCENTAGE_SCALAR);
111 |
112 | //calculate either the LINK fee or native fee if it's within the report
113 | if (quoteAddress == i_linkAddress) {
114 | fee.assetAddress = i_linkAddress;
115 | fee.amount = reward.amount;
116 | } else {
117 | uint256 surchargedFee =
118 | Math.ceilDiv(nativeQuantity * (PERCENTAGE_SCALAR + s_nativeSurcharge), PERCENTAGE_SCALAR);
119 |
120 | fee.assetAddress = i_nativeAddress;
121 | fee.amount = Math.ceilDiv(surchargedFee * (PERCENTAGE_SCALAR - discount), PERCENTAGE_SCALAR);
122 | }
123 |
124 | return (fee, reward, discount);
125 | }
126 |
127 | function _processFee(bytes calldata payload, bytes calldata parameterPayload, address subscriber) internal {
128 | if (subscriber == address(this)) revert InvalidAddress();
129 | address quote = abi.decode(parameterPayload, (address));
130 |
131 | (, bytes memory report) = abi.decode(payload, (bytes32[3], bytes));
132 |
133 | (Common.Asset memory fee, /*Common.Asset memory reward*/, /*uint256 appliedDiscount*/ ) =
134 | getFeeAndReward(subscriber, report, quote);
135 |
136 | if (fee.assetAddress == i_linkAddress) {
137 | IRewardManager.FeePayment[] memory payments = new IRewardManager.FeePayment[](1);
138 | payments[0] = IRewardManager.FeePayment({poolId: bytes32(0), amount: uint192(fee.amount)});
139 | i_rewardManager.onFeePaid(payments, subscriber);
140 | } else {
141 | if (msg.value != 0) {
142 | if (fee.amount > msg.value) revert InvalidDeposit();
143 |
144 | IWERC20(i_nativeAddress).deposit{value: fee.amount}();
145 |
146 | uint256 change;
147 | unchecked {
148 | change = msg.value - fee.amount;
149 | }
150 |
151 | if (change > 0) {
152 | payable(subscriber).transfer(change);
153 | }
154 | } else {
155 | IERC20(i_nativeAddress).safeTransferFrom(subscriber, address(this), fee.amount);
156 | }
157 | }
158 | }
159 |
160 | function setNativeSurcharge(uint64 surcharge) external {
161 | if (surcharge > PERCENTAGE_SCALAR) revert InvalidSurcharge();
162 |
163 | s_nativeSurcharge = surcharge;
164 | }
165 |
166 | function setMockDiscount(address subscriber, uint256 discount) external {
167 | if (discount > PERCENTAGE_SCALAR) revert InvalidDiscount();
168 |
169 | s_mockDiscounts[subscriber] = discount;
170 | }
171 |
172 | function getMockDiscount(address subscriber) external view returns (uint256) {
173 | return s_mockDiscounts[subscriber];
174 | }
175 |
176 | function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
177 | return interfaceId == this.processFee.selector || interfaceId == this.processFeeBulk.selector;
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/data-streams/MockReportGenerator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test} from "forge-std/Test.sol";
5 | import {ReportVersions} from "./ReportVersions.sol";
6 |
7 | contract MockReportGenerator is Test, ReportVersions {
8 | address internal immutable i_donAddress;
9 | uint256 internal immutable i_donDigest;
10 |
11 | bytes32 internal immutable i_reportV2MockFeedId =
12 | hex"0002777777777777777777777777777777777777777777777777777777777777";
13 | bytes32 internal immutable i_reportV3MockFeedId =
14 | hex"0003777777777777777777777777777777777777777777777777777777777777";
15 | bytes32 internal immutable i_reportV4MockFeedId =
16 | hex"0004777777777777777777777777777777777777777777777777777777777777";
17 |
18 | int192 internal s_price;
19 | int192 internal s_bid;
20 | int192 internal s_ask;
21 | uint32 internal s_expiresPeriod;
22 | uint32 internal s_marketStatus;
23 | uint192 internal s_nativeFee; // 0 by default
24 | uint192 internal s_linkFee; // 0 by default
25 |
26 | error MockReportGenerator__InvalidBid();
27 | error MockReportGenerator__InvalidAsk();
28 | error MockReportGenerator__CastOverflow();
29 |
30 | constructor(int192 initialPrice) {
31 | updatePrice(initialPrice);
32 | s_expiresPeriod = 1 days;
33 | s_marketStatus = 2; // 0 (Unknown), 1 (Closed), 2 (Open)
34 | (i_donAddress, i_donDigest) = makeAddrAndKey("Mock Data Streams DON");
35 | }
36 |
37 | function generateReport(ReportV2 calldata report) external returns (bytes memory signedReport) {
38 | bytes memory reportData = abi.encode(report);
39 | signedReport = signReport(reportData);
40 | }
41 |
42 | function generateReport(ReportV3 calldata report) external returns (bytes memory signedReport) {
43 | bytes memory reportData = abi.encode(report);
44 | signedReport = signReport(reportData);
45 | }
46 |
47 | function generateReport(ReportV4 calldata report) external returns (bytes memory signedReport) {
48 | bytes memory reportData = abi.encode(report);
49 | signedReport = signReport(reportData);
50 | }
51 |
52 | function generateReportV2() external returns (bytes memory signedReport, ReportV2 memory report) {
53 | report = ReportV2({
54 | feedId: i_reportV2MockFeedId,
55 | validFromTimestamp: toUint32(block.timestamp),
56 | observationsTimestamp: toUint32(block.timestamp),
57 | nativeFee: s_nativeFee,
58 | linkFee: s_linkFee,
59 | expiresAt: toUint32(block.timestamp + s_expiresPeriod),
60 | benchmarkPrice: s_price
61 | });
62 | bytes memory reportData = abi.encode(report);
63 | signedReport = signReport(reportData);
64 | }
65 |
66 | function generateReportV3() external returns (bytes memory signedReport, ReportV3 memory report) {
67 | report = ReportV3({
68 | feedId: i_reportV3MockFeedId,
69 | validFromTimestamp: toUint32(block.timestamp),
70 | observationsTimestamp: toUint32(block.timestamp),
71 | nativeFee: s_nativeFee,
72 | linkFee: s_linkFee,
73 | expiresAt: toUint32(block.timestamp + s_expiresPeriod),
74 | price: s_price,
75 | bid: s_bid,
76 | ask: s_ask
77 | });
78 | bytes memory reportData = abi.encode(report);
79 | signedReport = signReport(reportData);
80 | }
81 |
82 | function generateReportV4() external returns (bytes memory signedReport, ReportV4 memory report) {
83 | report = ReportV4({
84 | feedId: i_reportV4MockFeedId,
85 | validFromTimestamp: toUint32(block.timestamp),
86 | observationsTimestamp: toUint32(block.timestamp),
87 | nativeFee: s_nativeFee,
88 | linkFee: s_linkFee,
89 | expiresAt: toUint32(block.timestamp + s_expiresPeriod),
90 | price: s_price,
91 | marketStatus: s_marketStatus
92 | });
93 | bytes memory reportData = abi.encode(report);
94 | signedReport = signReport(reportData);
95 | }
96 |
97 | function updatePrice(int192 price) public {
98 | s_price = price;
99 | int192 delta = price / 1000; // 0.1% = 1/1000
100 | s_bid = price - delta;
101 | s_ask = price + delta;
102 | }
103 |
104 | function updatePriceBidAndAsk(int192 price, int192 bid, int192 ask) external {
105 | // bid < price < ask
106 | if (bid >= price) revert MockReportGenerator__InvalidBid();
107 | if (ask <= price) revert MockReportGenerator__InvalidAsk();
108 |
109 | s_price = price;
110 | s_bid = bid;
111 | s_ask = ask;
112 | }
113 |
114 | function updateExpiresPeriod(uint32 period) external {
115 | s_expiresPeriod = period;
116 | }
117 |
118 | function updateMarketStatus(uint32 status) external {
119 | s_marketStatus = status;
120 | }
121 |
122 | function updateFees(uint192 nativeFee, uint192 linkFee) external {
123 | s_nativeFee = nativeFee;
124 | s_linkFee = linkFee;
125 | }
126 |
127 | function getMockDonAddress() external view returns (address) {
128 | return i_donAddress;
129 | }
130 |
131 | function signReport(bytes memory reportData) private returns (bytes memory signedReport) {
132 | bytes32[3] memory reportContext;
133 | bytes32[] memory rawRs = new bytes32[](1);
134 | bytes32[] memory rawSs = new bytes32[](1);
135 | bytes32 rawVs;
136 |
137 | reportContext[0] = bytes32(i_donDigest);
138 | reportContext[1] = ""; // not needed for mocks
139 | reportContext[2] = ""; // not needed for mocks
140 |
141 | vm.startPrank(i_donAddress);
142 | bytes32 hashedReport = keccak256(reportData);
143 | bytes32 h = keccak256(abi.encodePacked(hashedReport, reportContext));
144 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(i_donDigest, h);
145 | vm.stopPrank();
146 |
147 | rawRs[0] = r;
148 | rawSs[0] = s;
149 | rawVs = bytes32(uint256(v));
150 |
151 | return abi.encode(reportContext, reportData, rawRs, rawSs, rawVs);
152 | }
153 |
154 | function toUint32(uint256 timestamp) private pure returns (uint32) {
155 | if (timestamp > type(uint32).max) {
156 | revert MockReportGenerator__CastOverflow();
157 | }
158 | return uint32(timestamp);
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/data-streams/MockRewardManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol";
5 | import {IERC20} from
6 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
7 | import {SafeERC20} from
8 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
9 |
10 | // import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol";
11 | import {IRewardManager} from "./interfaces/IRewardManager.sol";
12 |
13 | contract MockRewardManager is IRewardManager {
14 | using SafeERC20 for IERC20;
15 |
16 | error Unauthorized();
17 |
18 | address public immutable i_linkAddress;
19 | address public s_feeManagerAddress;
20 |
21 | event FeePaid(IRewardManager.FeePayment[] payments, address payer);
22 |
23 | modifier onlyFeeManager() {
24 | if (msg.sender != s_feeManagerAddress) revert Unauthorized();
25 | _;
26 | }
27 |
28 | constructor(address linkAddress) {
29 | i_linkAddress = linkAddress;
30 | }
31 |
32 | function onFeePaid(FeePayment[] calldata payments, address payer) external override onlyFeeManager {
33 | uint256 totalFeeAmount;
34 | for (uint256 i; i < payments.length; ++i) {
35 | unchecked {
36 | // Tally the total payable fees
37 | totalFeeAmount += payments[i].amount;
38 | }
39 | }
40 |
41 | // Transfer fees to this contract
42 | IERC20(i_linkAddress).safeTransferFrom(payer, address(this), totalFeeAmount);
43 |
44 | emit FeePaid(payments, payer);
45 | }
46 |
47 | function claimRewards(bytes32[] memory /*poolIds*/ ) external pure override {
48 | revert("Not implemented");
49 | }
50 |
51 | function setFeeManager(address newFeeManager) external override {
52 | s_feeManagerAddress = newFeeManager;
53 | }
54 |
55 | function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
56 | return interfaceId == this.onFeePaid.selector;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/data-streams/MockVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {IERC165} from
5 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
6 |
7 | contract MockVerifier is IERC165 {
8 | error AccessForbidden();
9 | error InactiveFeed(bytes32 feedId);
10 | error DigestInactive(bytes32 feedId, bytes32 configDigest);
11 | error BadVerification();
12 | error InvalidV();
13 | error AlreadyUsedSignature();
14 | error InvalidSignatureLength();
15 | error InvalidS();
16 |
17 | // secp256k1 Curve Order N / 2 (to prevent signature malleability s <= N / 2)
18 | uint256 constant N_2 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0;
19 | // Generated from uint256(keccak256(abi.encodePacked("Mock Data Streams DON")));
20 | address constant MOCK_DATA_STREAM_DON_ADDRESS = 0x143f40b47ab222503b787e98940023c59fc29984;
21 |
22 | address internal immutable i_verifierProxy;
23 |
24 | mapping(bytes32 => mapping(bytes32 => bool)) internal s_inactiveDigests;
25 | mapping(bytes32 => bool) internal s_inactiveFeeds;
26 | mapping(bytes => bool) internal s_verifiedSignatures;
27 |
28 | event ReportVerified(bytes32 indexed feedId, address indexed sender);
29 |
30 | constructor(address verifierProxy) {
31 | i_verifierProxy = verifierProxy;
32 | }
33 |
34 | function verify(bytes calldata signedReport, address sender) external returns (bytes memory verifierResponse) {
35 | if (msg.sender != i_verifierProxy) revert AccessForbidden();
36 |
37 | (
38 | bytes32[3] memory reportContext,
39 | bytes memory reportData,
40 | bytes32[] memory rs,
41 | bytes32[] memory ss,
42 | bytes32 rawVs
43 | ) = abi.decode(signedReport, (bytes32[3], bytes, bytes32[], bytes32[], bytes32));
44 |
45 | // The feed ID is the first 32 bytes of the report data.
46 | bytes32 feedId = bytes32(reportData);
47 | if (s_inactiveFeeds[feedId]) revert InactiveFeed(feedId);
48 |
49 | bytes32 configDigest = reportContext[0];
50 | if (s_inactiveDigests[feedId][configDigest]) revert DigestInactive(feedId, configDigest);
51 |
52 | // Decoding recid (v) like this works only for the mock verifier. In fork mode, use Verifier.sol from @chainlink/contracts.
53 | uint8 v = uint8(uint256(rawVs));
54 | // Recid (v) can also be 0 or 1, however 0x01 precompile expects 27 or 28.
55 | if (v < 27) v += 27;
56 | // Prevents signature malleability
57 | if (v != 27 && v != 28) revert InvalidV();
58 |
59 | bytes memory signature = abi.encodePacked(rs[0], ss[0], v);
60 |
61 | // Prevents replay attacks
62 | if (s_verifiedSignatures[signature]) revert AlreadyUsedSignature();
63 |
64 | // MockReportGenerator will only generate standard "r,s,v" signatures. EIP-2098 and ERC-1271 are not supported.
65 | if (signature.length != 65) revert InvalidSignatureLength();
66 |
67 | // Prevents signature malleability using EIP-2
68 | if (uint256(ss[0]) > N_2) revert InvalidS();
69 |
70 | // Expired signatures are handled in MockVerifierProxy.sol
71 | // In local mode we don't care about cross-chain replay attacks, because everything is on one local network.
72 |
73 | bytes32 hashedReport = keccak256(reportData);
74 | bytes32 h = keccak256(abi.encodePacked(hashedReport, reportContext));
75 |
76 | address signer = ecrecover(h, v, rs[0], ss[0]);
77 | if (signer != MOCK_DATA_STREAM_DON_ADDRESS) revert BadVerification();
78 |
79 | s_verifiedSignatures[signature] = true;
80 | emit ReportVerified(feedId, sender);
81 |
82 | return reportData;
83 | }
84 |
85 | function deactivateConfig(bytes32 feedId, bytes32 configDigest) external {
86 | s_inactiveDigests[feedId][configDigest] = true;
87 | }
88 |
89 | function activateConfig(bytes32 feedId, bytes32 configDigest) external {
90 | s_inactiveDigests[feedId][configDigest] = false;
91 | }
92 |
93 | function deactivateFeed(bytes32 feedId) external {
94 | s_inactiveFeeds[feedId] = true;
95 | }
96 |
97 | function activateFeed(bytes32 feedId) external {
98 | s_inactiveFeeds[feedId] = false;
99 | }
100 |
101 | function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
102 | return interfaceId == this.verify.selector;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/data-streams/MockVerifierProxy.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol";
5 | import {IERC165} from
6 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
7 |
8 | // import {IVerifier} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol";
9 | // import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol";
10 | import {IVerifier} from "./interfaces/IVerifier.sol";
11 | import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol";
12 |
13 | contract MockVerifierProxy is OwnerIsCreator {
14 | error ZeroAddress();
15 | error VerifierInvalid();
16 | error VerifierNotFound();
17 |
18 | address internal s_verifier;
19 | IVerifierFeeManager public s_feeManager;
20 |
21 | event VerifierInitialized(address indexed verifierAddress);
22 |
23 | modifier onlyValidVerifier(address verifierAddress) {
24 | if (verifierAddress == address(0)) revert ZeroAddress();
25 | if (!IERC165(verifierAddress).supportsInterface(IVerifier.verify.selector)) revert VerifierInvalid();
26 | _;
27 | }
28 |
29 | function verify(bytes calldata payload, bytes calldata parameterPayload) external payable returns (bytes memory) {
30 | IVerifierFeeManager feeManager = s_feeManager;
31 |
32 | // Bill the verifier
33 | if (address(feeManager) != address(0)) {
34 | feeManager.processFee{value: msg.value}(payload, parameterPayload, msg.sender);
35 | }
36 |
37 | return _verify(payload);
38 | }
39 |
40 | function verifyBulk(bytes[] calldata payloads, bytes calldata parameterPayload)
41 | external
42 | payable
43 | returns (bytes[] memory verifiedReports)
44 | {
45 | IVerifierFeeManager feeManager = s_feeManager;
46 |
47 | // Bill the verifier
48 | if (address(feeManager) != address(0)) {
49 | feeManager.processFeeBulk{value: msg.value}(payloads, parameterPayload, msg.sender);
50 | }
51 |
52 | // Verify reports
53 | verifiedReports = new bytes[](payloads.length);
54 | for (uint256 i; i < payloads.length; ++i) {
55 | verifiedReports[i] = _verify(payloads[i]);
56 | }
57 |
58 | return verifiedReports;
59 | }
60 |
61 | function _verify(bytes calldata payload) internal returns (bytes memory) {
62 | if (s_verifier == address(0)) revert VerifierNotFound();
63 |
64 | return IVerifier(s_verifier).verify(payload, msg.sender);
65 | }
66 |
67 | function initializeVerifier(address verifierAddress) external onlyOwner onlyValidVerifier(verifierAddress) {
68 | s_verifier = verifierAddress;
69 | emit VerifierInitialized(verifierAddress);
70 | }
71 |
72 | function getVerifier(bytes32 /*configDigest*/ ) external view returns (address) {
73 | return s_verifier;
74 | }
75 |
76 | function setFeeManager(IVerifierFeeManager feeManager) external {
77 | s_feeManager = feeManager;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/data-streams/Register.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | /// @title Register Contract
5 | /// @notice This contract allows storing and retrieving network details for various chains.
6 | /// @dev Stores network details in a mapping based on chain IDs.
7 | contract Register {
8 | struct NetworkDetails {
9 | address verifierProxyAddress;
10 | address linkAddress;
11 | }
12 |
13 | /// @notice Mapping to store network details based on chain ID.
14 | mapping(uint256 chainId => NetworkDetails) internal s_networkDetails;
15 |
16 | /// @notice Constructor to initialize the network details for various chains.
17 | constructor() {
18 | // Arbitrum Sepolia
19 | s_networkDetails[421614] = NetworkDetails({
20 | verifierProxyAddress: 0x2ff010DEbC1297f19579B4246cad07bd24F2488A,
21 | linkAddress: 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E
22 | });
23 |
24 | // Avalanche Fuji
25 | s_networkDetails[43113] = NetworkDetails({
26 | verifierProxyAddress: 0x2bf612C65f5a4d388E687948bb2CF842FFb8aBB3,
27 | linkAddress: 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846
28 | });
29 |
30 | // Base Sepolia
31 | s_networkDetails[84532] = NetworkDetails({
32 | verifierProxyAddress: 0x8Ac491b7c118a0cdcF048e0f707247fD8C9575f9,
33 | linkAddress: 0xE4aB69C077896252FAFBD49EFD26B5D171A32410
34 | });
35 |
36 | // opBNB Testnet
37 | s_networkDetails[5611] =
38 | NetworkDetails({verifierProxyAddress: 0x001225Aca0efe49Dbb48233aB83a9b4d177b581A, linkAddress: address(0)});
39 |
40 | // Soneium Minato Testnet
41 | s_networkDetails[1946] = NetworkDetails({
42 | verifierProxyAddress: 0x26603bAC5CE09DAE5604700B384658AcA13AD6ae,
43 | linkAddress: 0x7ea13478Ea3961A0e8b538cb05a9DF0477c79Cd2
44 | });
45 | }
46 |
47 | /**
48 | * @notice Retrieves network details for a given chain ID.
49 | *
50 | * @param chainId - The ID of the chain to get the details for.
51 | * @return networkDetails - The network details for the specified chain ID.
52 | */
53 | function getNetworkDetails(uint256 chainId) external view returns (NetworkDetails memory networkDetails) {
54 | networkDetails = s_networkDetails[chainId];
55 | }
56 |
57 | /**
58 | * @notice Sets the network details for a given chain ID.
59 | *
60 | * @param chainId - The ID of the chain to set the details for.
61 | * @param networkDetails - The network details to set for the specified chain ID.
62 | */
63 | function setNetworkDetails(uint256 chainId, NetworkDetails memory networkDetails) external {
64 | s_networkDetails[chainId] = networkDetails;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/data-streams/ReportVersions.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | abstract contract ReportVersions {
5 | /**
6 | * @dev Represents a data report from a Data Streams stream for v2 schema (crypto streams).
7 | * The `price` value is carried to either 8 or 18 decimal places, depending on the stream.
8 | * For more information, see https://docs.chain.link/data-streams/crypto-streams and https://docs.chain.link/data-streams/reference/report-schema
9 | */
10 | struct ReportV2 {
11 | bytes32 feedId; // The feed ID the report has data for
12 | uint32 validFromTimestamp; // Earliest timestamp for which price is applicable
13 | uint32 observationsTimestamp; // Latest timestamp for which price is applicable
14 | uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (WETH/ETH)
15 | uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK
16 | uint32 expiresAt; // Latest timestamp where the report can be verified on-chain
17 | int192 benchmarkPrice; // DON consensus median price, carried to 8 decimal places
18 | }
19 |
20 | /**
21 | * @dev Represents a data report from a Data Streams stream for v3 schema (crypto streams).
22 | * The `price`, `bid`, and `ask` values are carried to either 8 or 18 decimal places, depending on the stream.
23 | * For more information, see https://docs.chain.link/data-streams/crypto-streams and https://docs.chain.link/data-streams/reference/report-schema
24 | */
25 | struct ReportV3 {
26 | bytes32 feedId; // The stream ID the report has data for.
27 | uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
28 | uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
29 | uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH).
30 | uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK.
31 | uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
32 | int192 price; // DON consensus median price (8 or 18 decimals).
33 | int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals).
34 | int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18 decimals).
35 | }
36 |
37 | /**
38 | * @dev Represents a data report from a Data Streams stream for v4 schema (RWA stream).
39 | * The `price` value is carried to either 8 or 18 decimal places, depending on the stream.
40 | * The `marketStatus` indicates whether the market is currently open. Possible values: `0` (`Unknown`), `1` (`Closed`), `2` (`Open`).
41 | * For more information, see https://docs.chain.link/data-streams/rwa-streams and https://docs.chain.link/data-streams/reference/report-schema-v4
42 | */
43 | struct ReportV4 {
44 | bytes32 feedId; // The stream ID the report has data for.
45 | uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
46 | uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
47 | uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH).
48 | uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK.
49 | uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
50 | int192 price; // DON consensus median benchmark price (8 or 18 decimals).
51 | uint32 marketStatus; // The DON's consensus on whether the market is currently open.
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/data-streams/interfaces/IRewardManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {IERC165} from
5 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
6 |
7 | interface IRewardManager is IERC165 {
8 | function onFeePaid(FeePayment[] calldata payments, address payee) external;
9 | function claimRewards(bytes32[] calldata poolIds) external;
10 | function setFeeManager(address newFeeManager) external;
11 |
12 | struct FeePayment {
13 | bytes32 poolId;
14 | uint192 amount;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/data-streams/interfaces/IVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | interface IVerifier {
5 | function verify(bytes calldata signedReport, address sender) external returns (bytes memory verifierResponse);
6 | function activateConfig(bytes32 feedId, bytes32 configDigest) external;
7 | function deactivateConfig(bytes32 feedId, bytes32 configDigest) external;
8 | function activateFeed(bytes32 feedId) external;
9 | function deactivateFeed(bytes32 feedId) external;
10 | }
11 |
--------------------------------------------------------------------------------
/src/data-streams/interfaces/IVerifierFeeManager.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {IERC165} from
5 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
6 |
7 | interface IVerifierFeeManager is IERC165 {
8 | function processFee(bytes calldata payload, bytes calldata parameterPayload, address subscriber) external payable;
9 | function processFeeBulk(bytes[] calldata payloads, bytes calldata parameterPayload, address subscriber)
10 | external
11 | payable;
12 | }
13 |
--------------------------------------------------------------------------------
/src/shared/LinkToken.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: UNLICENSED
2 | pragma solidity ^0.8.19;
3 |
4 | import {ERC677} from "@chainlink/contracts-ccip/src/v0.8/shared/token/ERC677/ERC677.sol";
5 |
6 | /// @title LinkToken
7 | /// @notice This contract implements the ChainLink Token (LINK) using the ERC677 standard.
8 | /// @dev Inherits from the ERC677 token contract and initializes with a fixed total supply and standard token details.
9 | contract LinkToken is ERC677 {
10 | /// @notice The total supply of LINK tokens.
11 | uint private constant TOTAL_SUPPLY = 10 ** 27;
12 |
13 | /// @notice The name of the LINK token.
14 | string private constant NAME = "ChainLink Token";
15 |
16 | /// @notice The symbol of the LINK token.
17 | string private constant SYMBOL = "LINK";
18 |
19 | /**
20 | * @notice Constructor to initialize the LinkToken contract with a fixed total supply, name, and symbol.
21 | * @dev Calls the ERC677 constructor with the name and symbol, and then mints the total supply to the contract deployer.
22 | */
23 | constructor() ERC677(NAME, SYMBOL) {
24 | _onCreate();
25 | }
26 |
27 | /**
28 | * @notice Hook that is called when this contract is created.
29 | * @dev Useful to override constructor behaviour in child contracts (e.g., LINK bridge tokens).
30 | * The default implementation mints 10**27 tokens to the contract deployer.
31 | */
32 | function _onCreate() internal virtual {
33 | _mint(msg.sender, TOTAL_SUPPLY);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/shared/WETH9.sol:
--------------------------------------------------------------------------------
1 | // Submitted for verification at Etherscan.io on 2017-12-12
2 |
3 | // Copyright (C) 2015, 2016, 2017 Dapphub
4 |
5 | // This program is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 |
10 | // This program is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 |
15 | // You should have received a copy of the GNU General Public License
16 | // along with this program. If not, see .
17 | pragma solidity ^0.8.19;
18 |
19 | contract WETH9 {
20 | string public name = "Wrapped Ether";
21 | string public symbol = "WETH";
22 | uint8 public decimals = 18;
23 |
24 | event Approval(address indexed src, address indexed guy, uint256 wad);
25 | event Transfer(address indexed src, address indexed dst, uint256 wad);
26 | event Deposit(address indexed dst, uint256 wad);
27 | event Withdrawal(address indexed src, uint256 wad);
28 |
29 | mapping(address => uint256) public balanceOf;
30 | mapping(address => mapping(address => uint256)) public allowance;
31 |
32 | receive() external payable {
33 | _deposit();
34 | }
35 |
36 | function _deposit() internal {
37 | balanceOf[msg.sender] += msg.value;
38 | emit Deposit(msg.sender, msg.value);
39 | }
40 |
41 | function deposit() external payable {
42 | _deposit();
43 | }
44 |
45 | function withdraw(uint256 wad) external {
46 | require(balanceOf[msg.sender] >= wad);
47 | balanceOf[msg.sender] -= wad;
48 | payable(msg.sender).transfer(wad);
49 | emit Withdrawal(msg.sender, wad);
50 | }
51 |
52 | function totalSupply() public view returns (uint256) {
53 | return address(this).balance;
54 | }
55 |
56 | function approve(address guy, uint256 wad) public returns (bool) {
57 | allowance[msg.sender][guy] = wad;
58 | emit Approval(msg.sender, guy, wad);
59 | return true;
60 | }
61 |
62 | function transfer(address dst, uint256 wad) public returns (bool) {
63 | return transferFrom(msg.sender, dst, wad);
64 | }
65 |
66 | function transferFrom(
67 | address src,
68 | address dst,
69 | uint256 wad
70 | ) public returns (bool) {
71 | require(balanceOf[src] >= wad);
72 |
73 | if (
74 | src != msg.sender && allowance[src][msg.sender] != type(uint128).max
75 | ) {
76 | require(allowance[src][msg.sender] >= wad);
77 | allowance[src][msg.sender] -= wad;
78 | }
79 |
80 | balanceOf[src] -= wad;
81 | balanceOf[dst] += wad;
82 |
83 | emit Transfer(src, dst, wad);
84 |
85 | return true;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/ccip/BasicTokenSender.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.24;
3 |
4 | import {IERC20} from
5 | "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
6 | import {SafeERC20} from
7 | "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
8 | import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
9 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
10 |
11 | /**
12 | * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
13 | * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
14 | * DO NOT USE THIS CODE IN PRODUCTION.
15 | */
16 | contract BasicTokenSender {
17 | using SafeERC20 for IERC20;
18 |
19 | address immutable i_router;
20 |
21 | event MessageSent(bytes32 messageId);
22 |
23 | constructor(address router) {
24 | i_router = router;
25 | }
26 |
27 | receive() external payable {}
28 |
29 | function send(uint64 destinationChainSelector, address receiver, Client.EVMTokenAmount[] memory tokensToSendDetails)
30 | external
31 | payable
32 | {
33 | uint256 length = tokensToSendDetails.length;
34 |
35 | for (uint256 i = 0; i < length;) {
36 | IERC20(tokensToSendDetails[i].token).safeTransferFrom(
37 | msg.sender, address(this), tokensToSendDetails[i].amount
38 | );
39 | IERC20(tokensToSendDetails[i].token).approve(i_router, tokensToSendDetails[i].amount);
40 |
41 | unchecked {
42 | ++i;
43 | }
44 | }
45 |
46 | Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
47 | receiver: abi.encode(receiver),
48 | data: "",
49 | tokenAmounts: tokensToSendDetails,
50 | extraArgs: "",
51 | feeToken: address(0)
52 | });
53 |
54 | uint256 fee = IRouterClient(i_router).getFee(destinationChainSelector, message);
55 |
56 | bytes32 messageId = IRouterClient(i_router).ccipSend{value: fee}(destinationChainSelector, message);
57 |
58 | emit MessageSent(messageId);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/ccip/CCIPReceiver_Unsafe.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.24;
3 |
4 | import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
5 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
6 |
7 | contract CCIPReceiver_Unsafe is CCIPReceiver {
8 | string public text;
9 |
10 | constructor(address router) CCIPReceiver(router) {}
11 |
12 | function _ccipReceive(Client.Any2EVMMessage memory message) internal override {
13 | text = abi.decode(message.data, (string));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/ccip/CCIPSender_Unsafe.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
5 | import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
6 | import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
7 |
8 | contract CCIPSender_Unsafe {
9 | address link;
10 | address router;
11 |
12 | constructor(address _link, address _router) {
13 | link = _link;
14 | router = _router;
15 | }
16 |
17 | function send(
18 | address receiver,
19 | string memory someText,
20 | uint64 destinationChainSelector,
21 | address _token,
22 | uint256 _amount
23 | ) external returns (bytes32 messageId) {
24 | Client.EVMTokenAmount[]
25 | memory tokenAmounts = new Client.EVMTokenAmount[](1);
26 | Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
27 | token: _token,
28 | amount: _amount
29 | });
30 | tokenAmounts[0] = tokenAmount;
31 |
32 | Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
33 | receiver: abi.encode(receiver),
34 | data: abi.encode(someText),
35 | tokenAmounts: tokenAmounts,
36 | extraArgs: "",
37 | feeToken: link
38 | });
39 |
40 | IERC20(_token).approve(address(router), _amount);
41 |
42 | uint256 fee = IRouterClient(router).getFee(
43 | destinationChainSelector,
44 | message
45 | );
46 | IERC20(link).approve(address(router), fee);
47 |
48 | messageId = IRouterClient(router).ccipSend(
49 | destinationChainSelector,
50 | message
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/ccip/Ping.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
5 | import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
6 | import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
7 | import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
8 |
9 | contract Ping is CCIPReceiver {
10 | address link;
11 | address router;
12 |
13 | string public PONG;
14 |
15 | constructor(address _link, address _router) CCIPReceiver(_router) {
16 | link = _link;
17 | router = _router;
18 | }
19 |
20 | function send(
21 | address receiver,
22 | uint64 destinationChainSelector
23 | ) external returns (bytes32 messageId) {
24 | Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
25 | receiver: abi.encode(receiver),
26 | data: abi.encode("Ping"),
27 | tokenAmounts: new Client.EVMTokenAmount[](0),
28 | extraArgs: Client._argsToBytes(
29 | // Additional arguments, setting gas limit
30 | Client.EVMExtraArgsV1({gasLimit: 500_000})
31 | ),
32 | feeToken: link
33 | });
34 |
35 | uint256 fee = IRouterClient(router).getFee(
36 | destinationChainSelector,
37 | message
38 | );
39 |
40 | IERC20(link).approve(address(router), fee);
41 |
42 | messageId = IRouterClient(router).ccipSend(
43 | destinationChainSelector,
44 | message
45 | );
46 | }
47 |
48 | function _ccipReceive(
49 | Client.Any2EVMMessage memory message
50 | ) internal override {
51 | PONG = abi.decode(message.data, (string));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/ccip/Pong.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
5 | import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol";
6 | import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
7 | import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol";
8 |
9 | contract Pong is CCIPReceiver {
10 | address link;
11 | address router;
12 |
13 | string public PING;
14 |
15 | constructor(address _link, address _router) CCIPReceiver(_router) {
16 | link = _link;
17 | router = _router;
18 | }
19 |
20 | function _ccipReceive(
21 | Client.Any2EVMMessage memory message
22 | ) internal override {
23 | PING = abi.decode(message.data, (string));
24 |
25 | Client.EVM2AnyMessage memory replyMessage = Client.EVM2AnyMessage({
26 | receiver: message.sender,
27 | data: abi.encode("Pong"),
28 | tokenAmounts: new Client.EVMTokenAmount[](0),
29 | extraArgs: "",
30 | feeToken: link
31 | });
32 |
33 | uint256 fee = IRouterClient(router).getFee(
34 | message.sourceChainSelector,
35 | replyMessage
36 | );
37 | IERC20(link).approve(address(router), fee);
38 |
39 | IRouterClient(router).ccipSend(
40 | message.sourceChainSelector,
41 | replyMessage
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/data-feeds/BasicDataConsumerV3.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.7;
3 |
4 | import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
5 |
6 | /**
7 | * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED
8 | * VALUES FOR CLARITY.
9 | * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
10 | * DO NOT USE THIS CODE IN PRODUCTION.
11 | */
12 |
13 | /**
14 | * If you are reading data feeds on L2 networks, you must
15 | * check the latest answer from the L2 Sequencer Uptime
16 | * Feed to ensure that the data is accurate in the event
17 | * of an L2 sequencer outage. See the
18 | * https://docs.chain.link/data-feeds/l2-sequencer-feeds
19 | * page for details.
20 | */
21 | contract BasicDataConsumerV3 {
22 | AggregatorV3Interface internal dataFeed;
23 |
24 | constructor(address dataFeedAddress) {
25 | dataFeed = AggregatorV3Interface(dataFeedAddress);
26 | }
27 |
28 | function getChainlinkDataFeedLatestAnswer() public view returns (int256) {
29 | (
30 | ,
31 | /* uint80 roundID */
32 | int256 answer /*uint startedAt*/ /*uint timeStamp*/ /*uint80 answeredInRound*/,
33 | ,
34 | ,
35 |
36 | ) = dataFeed.latestRoundData();
37 | return answer;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/data-streams/ChainlinkDataStreamProvider.sol:
--------------------------------------------------------------------------------
1 | // Simplifed version of https://github.com/gmx-io/gmx-synthetics/blob/main/contracts/oracle/ChainlinkDataStreamProvider.sol used for testing purposes
2 | // SPDX-License-Identifier: MIT
3 | pragma solidity ^0.8.0;
4 |
5 | interface IChainlinkDataStreamVerifier {
6 | /**
7 | * @notice Verifies that the data encoded has been signed
8 | * correctly by routing to the correct verifier, and bills the user if applicable.
9 | * @param payload The encoded data to be verified, including the signed
10 | * report.
11 | * @param parameterPayload fee metadata for billing. For the current implementation this is just the abi-encoded fee token ERC-20 address
12 | * @return verifierResponse The encoded report from the verifier.
13 | */
14 | function verify(bytes calldata payload, bytes calldata parameterPayload)
15 | external
16 | payable
17 | returns (bytes memory verifierResponse);
18 | }
19 |
20 | contract ChainlinkDataStreamProvider {
21 | bytes32 public constant DATA_STREAM_ID = keccak256(abi.encode("DATA_STREAM_ID"));
22 | bytes32 public constant CHAINLINK_PAYMENT_TOKEN = keccak256(abi.encode("CHAINLINK_PAYMENT_TOKEN"));
23 |
24 | IChainlinkDataStreamVerifier public immutable verifier;
25 |
26 | mapping(bytes32 => address) public addressValues;
27 | mapping(bytes32 => bytes32) public bytes32Values;
28 |
29 | // bid: min price, highest buy price
30 | // ask: max price, lowest sell price
31 | struct Report {
32 | bytes32 feedId; // The feed ID the report has data for
33 | uint32 validFromTimestamp; // Earliest timestamp for which price is applicable
34 | uint32 observationsTimestamp; // Latest timestamp for which price is applicable
35 | uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (WETH/ETH)
36 | uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK
37 | uint32 expiresAt; // Latest timestamp where the report can be verified onchain
38 | int192 price; // DON consensus median price, carried to 8 decimal places
39 | int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation
40 | int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation
41 | }
42 |
43 | error EmptyChainlinkPaymentToken();
44 | error EmptyDataStreamFeedId(address token);
45 | error InvalidDataStreamFeedId(address token, bytes32 reportFeedId, bytes32 expectedFeedId);
46 | error InvalidDataStreamPrices(address token, int192 bid, int192 ask);
47 | error InvalidDataStreamBidAsk(address token, int192 bid, int192 ask);
48 |
49 | constructor(address _verifier, address linkTokenAddress) {
50 | verifier = IChainlinkDataStreamVerifier(_verifier);
51 | addressValues[CHAINLINK_PAYMENT_TOKEN] = linkTokenAddress;
52 | }
53 |
54 | function setBytes32(address token, bytes32 feedId) external {
55 | bytes32Values[dataStreamIdKey(token)] = feedId;
56 | }
57 |
58 | // @dev key for data stream feed ID
59 | // @param token the token to get the key for
60 | // @return key for data stream feed ID
61 | function dataStreamIdKey(address token) internal pure returns (bytes32) {
62 | return keccak256(abi.encode(DATA_STREAM_ID, token));
63 | }
64 |
65 | function getOraclePrice(address token, bytes memory data)
66 | external
67 | returns (address token_, int192 bid, int192 ask, uint32 timestamp)
68 | {
69 | bytes32 feedId = bytes32Values[dataStreamIdKey(token)];
70 | if (feedId == bytes32(0)) {
71 | revert EmptyDataStreamFeedId(token);
72 | }
73 |
74 | bytes memory payloadParameter = _getPayloadParameter();
75 | bytes memory verifierResponse = verifier.verify(data, payloadParameter);
76 |
77 | Report memory report = abi.decode(verifierResponse, (Report));
78 |
79 | if (feedId != report.feedId) {
80 | revert InvalidDataStreamFeedId(token, report.feedId, feedId);
81 | }
82 |
83 | if (report.bid <= 0 || report.ask <= 0) {
84 | revert InvalidDataStreamPrices(token, report.bid, report.ask);
85 | }
86 |
87 | if (report.bid > report.ask) {
88 | revert InvalidDataStreamBidAsk(token, report.bid, report.ask);
89 | }
90 |
91 | return (token, report.bid, report.ask, report.observationsTimestamp);
92 | }
93 |
94 | function _getPayloadParameter() internal view returns (bytes memory) {
95 | // LINK token address
96 | address feeToken = addressValues[CHAINLINK_PAYMENT_TOKEN];
97 |
98 | if (feeToken == address(0)) {
99 | revert EmptyChainlinkPaymentToken();
100 | }
101 |
102 | return abi.encode(feeToken);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/test/data-streams/ClientReportsVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {IERC20} from
5 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
6 | import {SafeERC20} from
7 | "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
8 |
9 | library Common {
10 | struct Asset {
11 | address token;
12 | uint256 amount;
13 | }
14 | }
15 |
16 | interface IVerifierProxy {
17 | function verify(bytes calldata payload, bytes calldata parameterPayload)
18 | external
19 | payable
20 | returns (bytes memory verifierResponse);
21 |
22 | function verifyBulk(bytes[] calldata payloads, bytes calldata parameterPayload)
23 | external
24 | payable
25 | returns (bytes[] memory verifiedReports);
26 |
27 | function s_feeManager() external view returns (address);
28 | }
29 |
30 | interface IFeeManager {
31 | function getFeeAndReward(address subscriber, bytes memory unverifiedReport, address quoteAddress)
32 | external
33 | returns (Common.Asset memory, Common.Asset memory, uint256);
34 |
35 | function i_linkAddress() external view returns (address);
36 |
37 | function i_nativeAddress() external view returns (address);
38 |
39 | function i_rewardManager() external view returns (address);
40 | }
41 |
42 | /**
43 | * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES.
44 | * DO NOT USE THIS CODE IN PRODUCTION.
45 | */
46 | contract ClientReportsVerifier {
47 | using SafeERC20 for IERC20;
48 |
49 | error NothingToWithdraw();
50 | error NotOwner(address caller);
51 | error InvalidReportVersion(uint16 version);
52 |
53 | struct ReportV3 {
54 | bytes32 feedId; // The stream ID the report has data for.
55 | uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
56 | uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
57 | uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH).
58 | uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK.
59 | uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
60 | int192 price; // DON consensus median price (8 or 18 decimals).
61 | int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals).
62 | int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18 decimals).
63 | }
64 |
65 | struct ReportV4 {
66 | bytes32 feedId; // The stream ID the report has data for.
67 | uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
68 | uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
69 | uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (e.g., WETH/ETH).
70 | uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK.
71 | uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
72 | int192 price; // DON consensus median benchmark price (8 or 18 decimals).
73 | uint32 marketStatus; // The DON's consensus on whether the market is currently open.
74 | }
75 |
76 | IVerifierProxy public s_verifierProxy;
77 |
78 | address private s_owner;
79 | int192 public lastDecodedPrice;
80 |
81 | event DecodedPrice(int192 price);
82 |
83 | constructor(address _verifierProxy) {
84 | s_owner = msg.sender;
85 | s_verifierProxy = IVerifierProxy(_verifierProxy);
86 | }
87 |
88 | modifier onlyOwner() {
89 | if (msg.sender != s_owner) revert NotOwner(msg.sender);
90 | _;
91 | }
92 |
93 | function verifyReport(bytes memory unverifiedReport) external {
94 | IFeeManager feeManager = IFeeManager(s_verifierProxy.s_feeManager());
95 |
96 | address rewardManager = feeManager.i_rewardManager();
97 |
98 | // Decode unverified report to extract report data
99 | (, bytes memory reportData) = abi.decode(unverifiedReport, (bytes32[3], bytes));
100 |
101 | // Extract report version from reportData
102 | uint16 reportVersion = (uint16(uint8(reportData[0])) << 8) | uint16(uint8(reportData[1]));
103 |
104 | // Validate report version
105 | if (reportVersion != 3 && reportVersion != 4) {
106 | revert InvalidReportVersion(uint8(reportVersion));
107 | }
108 |
109 | // Set the fee token address (LINK in this case)
110 | address feeTokenAddress = feeManager.i_linkAddress();
111 |
112 | // Calculate the fee required for report verification
113 | (Common.Asset memory fee,,) = feeManager.getFeeAndReward(address(this), reportData, feeTokenAddress);
114 |
115 | // Approve rewardManager to spend this contract's balance in fees
116 | IERC20(feeTokenAddress).approve(rewardManager, fee.amount);
117 |
118 | // Verify the report through the VerifierProxy
119 | bytes memory verifiedReportData = s_verifierProxy.verify(unverifiedReport, abi.encode(feeTokenAddress));
120 |
121 | // Decode verified report data into the appropriate Report struct based on reportVersion
122 | if (reportVersion == 3) {
123 | // v3 report schema
124 | ReportV3 memory verifiedReport = abi.decode(verifiedReportData, (ReportV3));
125 |
126 | // Log price from the verified report
127 | emit DecodedPrice(verifiedReport.price);
128 |
129 | // Store the price from the report
130 | lastDecodedPrice = verifiedReport.price;
131 | } else if (reportVersion == 4) {
132 | // v4 report schema
133 | ReportV4 memory verifiedReport = abi.decode(verifiedReportData, (ReportV4));
134 |
135 | // Log price from the verified report
136 | emit DecodedPrice(verifiedReport.price);
137 |
138 | // Store the price from the report
139 | lastDecodedPrice = verifiedReport.price;
140 | }
141 | }
142 |
143 | function withdrawToken(address _beneficiary, address _token) external onlyOwner {
144 | uint256 amount = IERC20(_token).balanceOf(address(this));
145 |
146 | if (amount == 0) revert NothingToWithdraw();
147 |
148 | IERC20(_token).safeTransfer(_beneficiary, amount);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/test/e2e/ccip/PingPongFork.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, Vm} from "forge-std/Test.sol";
5 | import {Ping} from "../../../src/test/ccip/Ping.sol";
6 | import {Pong} from "../../../src/test/ccip/Pong.sol";
7 | import {CCIPLocalSimulatorFork, Register} from "../../../src/ccip/CCIPLocalSimulatorFork.sol";
8 |
9 | contract PingPongFork is Test {
10 | CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
11 | Ping public ping;
12 | Pong public pong;
13 |
14 | Register.NetworkDetails sepoliaNetworkDetails;
15 | Register.NetworkDetails arbSepoliaNetworkDetails;
16 |
17 | uint256 sepoliaFork;
18 | uint256 arbSepoliaFork;
19 |
20 | function setUp() public {
21 | string memory ETHEREUM_SEPOLIA_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
22 | string memory ARBITRUM_SEPOLIA_RPC_URL = vm.envString("ARBITRUM_SEPOLIA_RPC_URL");
23 | sepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
24 | arbSepoliaFork = vm.createFork(ARBITRUM_SEPOLIA_RPC_URL);
25 |
26 | ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
27 | vm.makePersistent(address(ccipLocalSimulatorFork));
28 | sepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
29 |
30 | ping = new Ping(sepoliaNetworkDetails.linkAddress, sepoliaNetworkDetails.routerAddress);
31 |
32 | ccipLocalSimulatorFork.requestLinkFromFaucet(address(ping), 1 ether);
33 |
34 | vm.selectFork(arbSepoliaFork);
35 | arbSepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
36 | pong = new Pong(arbSepoliaNetworkDetails.linkAddress, arbSepoliaNetworkDetails.routerAddress);
37 |
38 | ccipLocalSimulatorFork.requestLinkFromFaucet(address(pong), 1 ether);
39 | }
40 |
41 | function test_ForkPingPong() public {
42 | vm.selectFork(sepoliaFork);
43 |
44 | ping.send(address(pong), arbSepoliaNetworkDetails.chainSelector);
45 | ccipLocalSimulatorFork.switchChainAndRouteMessage(arbSepoliaFork);
46 | ccipLocalSimulatorFork.switchChainAndRouteMessage(sepoliaFork);
47 |
48 | assertEq(ping.PONG(), "Pong");
49 | vm.selectFork(arbSepoliaFork);
50 | assertEq(pong.PING(), "Ping");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/test/e2e/ccip/TokenTransferorFork.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, Vm} from "forge-std/Test.sol";
5 | import {TokenTransferor} from "../../../src/test/ccip/TokenTransferor.sol";
6 | import {BurnMintERC677Helper, IERC20} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
7 | import {CCIPLocalSimulatorFork, Register} from "../../../src/ccip/CCIPLocalSimulatorFork.sol";
8 |
9 | contract TokenTransferorFork is Test {
10 | CCIPLocalSimulatorFork public ccipLocalSimulatorFork;
11 | TokenTransferor public sender;
12 | BurnMintERC677Helper public ccipBnM;
13 | IERC20 public linkToken;
14 | address alice;
15 |
16 | uint256 sepoliaFork;
17 | uint256 arbSepoliaFork;
18 |
19 | function setUp() public {
20 | string memory ETHEREUM_SEPOLIA_RPC_URL = vm.envString("ETHEREUM_SEPOLIA_RPC_URL");
21 | string memory ARBITRUM_SEPOLIA_RPC_URL = vm.envString("ARBITRUM_SEPOLIA_RPC_URL");
22 | sepoliaFork = vm.createSelectFork(ETHEREUM_SEPOLIA_RPC_URL);
23 | arbSepoliaFork = vm.createFork(ARBITRUM_SEPOLIA_RPC_URL);
24 |
25 | ccipLocalSimulatorFork = new CCIPLocalSimulatorFork();
26 | vm.makePersistent(address(ccipLocalSimulatorFork));
27 |
28 | Register.NetworkDetails memory sepoliaNetworkDetails = ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
29 |
30 | sender = new TokenTransferor(sepoliaNetworkDetails.routerAddress, sepoliaNetworkDetails.linkAddress);
31 |
32 | ccipBnM = BurnMintERC677Helper(sepoliaNetworkDetails.ccipBnMAddress);
33 |
34 | linkToken = IERC20(sepoliaNetworkDetails.linkAddress);
35 |
36 | alice = makeAddr("alice");
37 |
38 | ccipLocalSimulatorFork.requestLinkFromFaucet(address(sender), 25 ether);
39 | }
40 |
41 | function test_forkTokenTransfer() external {
42 | uint256 amountToSend = 100;
43 | ccipBnM.drip(address(sender));
44 |
45 | uint64 arbSepoliaChainSelector = 3478487238524512106;
46 | sender.allowlistDestinationChain(arbSepoliaChainSelector, true);
47 |
48 | uint256 balanceBefore = ccipBnM.balanceOf(address(sender));
49 |
50 | sender.transferTokensPayLINK(arbSepoliaChainSelector, alice, address(ccipBnM), amountToSend);
51 |
52 | uint256 balanceAfer = ccipBnM.balanceOf(address(sender));
53 | assertEq(balanceAfer, balanceBefore - amountToSend);
54 |
55 | ccipLocalSimulatorFork.switchChainAndRouteMessage(arbSepoliaFork);
56 |
57 | Register.NetworkDetails memory arbSepoliaNetworkDetails =
58 | ccipLocalSimulatorFork.getNetworkDetails(block.chainid);
59 | BurnMintERC677Helper ccipBnMArbSepolia = BurnMintERC677Helper(arbSepoliaNetworkDetails.ccipBnMAddress);
60 |
61 | assertEq(ccipBnMArbSepolia.balanceOf(alice), amountToSend);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/test/e2e/data-feeds/BasicDataConsumerV3.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 |
6 | import {BasicDataConsumerV3} from "../../../src/test/data-feeds/BasicDataConsumerV3.sol";
7 |
8 | contract BasicDataConsumerV3Test is Test {
9 | BasicDataConsumerV3 public consumer;
10 | address constant ETH_USD_AGGREGATOR_ADDRESS = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419;
11 | uint256 constant BLOCK_NUMBER = 20163485;
12 | int256 constant EXPECTED_ANSWER_AT_PINNED_BLOCK_NUMBER = 329182212045; // retrieved from Etherscan
13 |
14 | uint256 ethereumMainnetForkId;
15 |
16 | function setUp() public {
17 | string memory ETHEREUM_MAINNET_RPC_URL = vm.envString("ETHEREUM_MAINNET_RPC_URL");
18 | ethereumMainnetForkId = vm.createSelectFork(ETHEREUM_MAINNET_RPC_URL, BLOCK_NUMBER);
19 |
20 | consumer = new BasicDataConsumerV3(ETH_USD_AGGREGATOR_ADDRESS);
21 | }
22 |
23 | function test_forkSmoke() public {
24 | assertEq(vm.activeFork(), ethereumMainnetForkId);
25 |
26 | int256 answer = consumer.getChainlinkDataFeedLatestAnswer();
27 | assertEq(answer, EXPECTED_ANSWER_AT_PINNED_BLOCK_NUMBER);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/smoke/ccip/PayWithNative.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.24;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {BasicTokenSender} from "../../../src/test/ccip/BasicTokenSender.sol";
6 | import {
7 | CCIPLocalSimulator, IRouterClient, BurnMintERC677Helper
8 | } from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
9 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
10 |
11 | contract PayWithNativeTest is Test {
12 | CCIPLocalSimulator public ccipLocalSimulator;
13 | BasicTokenSender public sender;
14 | uint64 chainSelector;
15 | BurnMintERC677Helper ccipBnM;
16 | IRouterClient sourceRouter;
17 | address alice;
18 | address bob;
19 |
20 | function setUp() public {
21 | ccipLocalSimulator = new CCIPLocalSimulator();
22 | (uint64 chainSelector_, IRouterClient sourceRouter_,,,, BurnMintERC677Helper ccipBnM_,) =
23 | ccipLocalSimulator.configuration();
24 |
25 | sender = new BasicTokenSender(address(sourceRouter_));
26 |
27 | chainSelector = chainSelector_;
28 | ccipBnM = ccipBnM_;
29 | sourceRouter = sourceRouter_;
30 |
31 | alice = makeAddr("alice");
32 | bob = makeAddr("bob");
33 | }
34 |
35 | function test_shouldPayInNativeCoin() external {
36 | vm.startPrank(alice);
37 |
38 | ccipBnM.drip(alice);
39 | uint256 amountToSend = 100;
40 |
41 | Client.EVMTokenAmount[] memory tokensToSendDetails = new Client.EVMTokenAmount[](1);
42 | Client.EVMTokenAmount memory tokenToSend =
43 | Client.EVMTokenAmount({token: address(ccipBnM), amount: amountToSend});
44 | tokensToSendDetails[0] = tokenToSend;
45 |
46 | Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
47 | receiver: abi.encode(alice),
48 | data: "",
49 | tokenAmounts: tokensToSendDetails,
50 | extraArgs: "",
51 | feeToken: address(0)
52 | });
53 |
54 | uint256 fee = sourceRouter.getFee(chainSelector, message);
55 |
56 | uint256 balanceOfAliceBefore = ccipBnM.balanceOf(alice);
57 | uint256 balanceOfBobBefore = ccipBnM.balanceOf(bob);
58 |
59 | ccipBnM.increaseApproval(address(sender), amountToSend);
60 | assertEq(ccipBnM.allowance(alice, address(sender)), amountToSend);
61 |
62 | sender.send{value: fee}(chainSelector, bob, tokensToSendDetails);
63 |
64 | uint256 balanceOfAliceAfter = ccipBnM.balanceOf(alice);
65 | uint256 balanceOfBobAfter = ccipBnM.balanceOf(bob);
66 | assertEq(balanceOfAliceAfter, balanceOfAliceBefore - amountToSend);
67 | assertEq(balanceOfBobAfter, balanceOfBobBefore + amountToSend);
68 |
69 | vm.stopPrank();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/smoke/ccip/PingPong.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {Ping} from "../../../src/test/ccip/Ping.sol";
6 | import {Pong} from "../../../src/test/ccip/Pong.sol";
7 | import {CCIPLocalSimulator, IRouterClient, LinkToken} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
8 |
9 | contract PingPongTest is Test {
10 | CCIPLocalSimulator public ccipLocalSimulator;
11 |
12 | Ping public ping;
13 | Pong public pong;
14 |
15 | uint64 chainSelector;
16 |
17 | function setUp() public {
18 | ccipLocalSimulator = new CCIPLocalSimulator();
19 | (
20 | uint64 chainSelector_,
21 | IRouterClient sourceRouter,
22 | IRouterClient destinationRouter,
23 | ,
24 | LinkToken linkToken,
25 | ,
26 |
27 | ) = ccipLocalSimulator.configuration();
28 |
29 | ping = new Ping(address(linkToken), address(sourceRouter));
30 | pong = new Pong(address(linkToken), address(destinationRouter));
31 |
32 | chainSelector = chainSelector_;
33 | }
34 |
35 | function test_pingPong() external {
36 | uint256 amountForFees = 1 ether;
37 | ccipLocalSimulator.requestLinkFromFaucet(address(ping), amountForFees);
38 | ccipLocalSimulator.requestLinkFromFaucet(address(pong), amountForFees);
39 |
40 | ping.send(address(pong), chainSelector);
41 |
42 | console2.log(pong.PING());
43 | console2.log(ping.PONG());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/smoke/ccip/ProgrammableDefensiveTokenTransfers.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {ProgrammableDefensiveTokenTransfers} from "../../../src/test/ccip/ProgrammableDefensiveTokenTransfers.sol";
6 | import {CCIPLocalSimulator, IRouterClient, LinkToken, BurnMintERC677Helper} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
7 |
8 | contract ProgrammableDefensiveTokenTransfersTest is Test {
9 | ProgrammableDefensiveTokenTransfers public sender;
10 | ProgrammableDefensiveTokenTransfers public receiver;
11 | CCIPLocalSimulator public ccipLocalSimulator;
12 |
13 | uint64 chainSelector;
14 | BurnMintERC677Helper ccipBnM;
15 |
16 | function setUp() public {
17 | ccipLocalSimulator = new CCIPLocalSimulator();
18 | (
19 | uint64 chainSelector_,
20 | IRouterClient sourceRouter,
21 | IRouterClient destinationRouter,
22 | ,
23 | LinkToken linkToken,
24 | BurnMintERC677Helper ccipBnM_,
25 |
26 | ) = ccipLocalSimulator.configuration();
27 |
28 | sender = new ProgrammableDefensiveTokenTransfers(
29 | address(sourceRouter),
30 | address(linkToken)
31 | );
32 |
33 | receiver = new ProgrammableDefensiveTokenTransfers(
34 | address(destinationRouter),
35 | address(linkToken)
36 | );
37 |
38 | chainSelector = chainSelector_;
39 | ccipBnM = ccipBnM_;
40 | }
41 |
42 | function prepareScenario()
43 | private
44 | returns (uint256 amountToSend, string memory textToSend)
45 | {
46 | amountToSend = 0.001 ether;
47 | uint256 amountForFees = 1 ether;
48 | textToSend = "Hello World";
49 |
50 | ccipBnM.drip(address(sender));
51 |
52 | ccipLocalSimulator.requestLinkFromFaucet(
53 | address(sender),
54 | amountForFees
55 | );
56 |
57 | sender.allowlistDestinationChain(chainSelector, true);
58 |
59 | receiver.allowlistSourceChain(chainSelector, true);
60 | receiver.allowlistSender(address(sender), true);
61 | }
62 |
63 | function test_regularTransfer() public {
64 | (uint256 amountToSend, string memory textToSend) = prepareScenario();
65 |
66 | bytes32 messageId = sender.sendMessagePayLINK(
67 | chainSelector,
68 | address(receiver),
69 | textToSend,
70 | address(ccipBnM),
71 | amountToSend
72 | );
73 |
74 | (
75 | bytes32 _messageId,
76 | string memory _text,
77 | address _tokenAddress,
78 | uint256 _tokenAmount
79 | ) = receiver.getLastReceivedMessageDetails();
80 |
81 | assertEq(_messageId, messageId);
82 | assertEq(_text, textToSend);
83 | assertEq(_tokenAddress, address(ccipBnM));
84 | assertEq(_tokenAmount, amountToSend);
85 | }
86 |
87 | function test_tokenRecovery() public {
88 | (uint256 amountToSend, string memory textToSend) = prepareScenario();
89 |
90 | receiver.setSimRevert(true);
91 |
92 | uint256 senderBalanceBefore = ccipBnM.balanceOf(address(sender));
93 | uint256 receiverBalanceBefore = ccipBnM.balanceOf(address(receiver));
94 |
95 | bytes32 messageId = sender.sendMessagePayLINK(
96 | chainSelector,
97 | address(receiver),
98 | textToSend,
99 | address(ccipBnM),
100 | amountToSend
101 | );
102 |
103 | ProgrammableDefensiveTokenTransfers.FailedMessage[]
104 | memory failedMessages = receiver.getFailedMessages(0, 1);
105 |
106 | assertEq(failedMessages[0].messageId, messageId);
107 | assertEq(
108 | uint8(failedMessages[0].errorCode),
109 | uint8(ProgrammableDefensiveTokenTransfers.ErrorCode.FAILED)
110 | );
111 |
112 | receiver.retryFailedMessage(messageId, msg.sender);
113 |
114 | ProgrammableDefensiveTokenTransfers.FailedMessage[]
115 | memory failedMessagesAfter = receiver.getFailedMessages(0, 1);
116 | assertEq(
117 | uint8(failedMessagesAfter[0].errorCode),
118 | uint8(ProgrammableDefensiveTokenTransfers.ErrorCode.RESOLVED)
119 | );
120 |
121 | uint256 senderBalanceAfter = ccipBnM.balanceOf(address(sender));
122 | uint256 receiverBalanceAfter = ccipBnM.balanceOf(address(receiver));
123 |
124 | assertEq(senderBalanceAfter, senderBalanceBefore - amountToSend);
125 | assertEq(receiverBalanceAfter, receiverBalanceBefore);
126 | assertEq(
127 | ccipBnM.balanceOf(msg.sender),
128 | receiverBalanceBefore + amountToSend
129 | );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/test/smoke/ccip/ProgrammableTokenTransfers.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {ProgrammableTokenTransfers} from "../../../src/test/ccip/ProgrammableTokenTransfers.sol";
6 | import {CCIPLocalSimulator, IRouterClient, LinkToken, BurnMintERC677Helper} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
7 |
8 | contract ProgrammableTokenTransfersTest is Test {
9 | ProgrammableTokenTransfers public sender;
10 | ProgrammableTokenTransfers public receiver;
11 | CCIPLocalSimulator public ccipLocalSimulator;
12 |
13 | uint64 chainSelector;
14 | BurnMintERC677Helper ccipBnM;
15 |
16 | function setUp() public {
17 | ccipLocalSimulator = new CCIPLocalSimulator();
18 | (
19 | uint64 chainSelector_,
20 | IRouterClient sourceRouter,
21 | IRouterClient destinationRouter,
22 | ,
23 | LinkToken linkToken,
24 | BurnMintERC677Helper ccipBnM_,
25 |
26 | ) = ccipLocalSimulator.configuration();
27 |
28 | sender = new ProgrammableTokenTransfers(
29 | address(sourceRouter),
30 | address(linkToken)
31 | );
32 |
33 | receiver = new ProgrammableTokenTransfers(
34 | address(destinationRouter),
35 | address(linkToken)
36 | );
37 |
38 | chainSelector = chainSelector_;
39 | ccipBnM = ccipBnM_;
40 | }
41 |
42 | function testProgrammableTokenTransfer() public {
43 | uint256 amountToSend = 0.001 ether;
44 | uint256 amountForFees = 1 ether;
45 | string memory textToSend = "Hello World";
46 |
47 | ccipBnM.drip(address(sender));
48 |
49 | ccipLocalSimulator.requestLinkFromFaucet(
50 | address(sender),
51 | amountForFees
52 | );
53 |
54 | sender.allowlistDestinationChain(chainSelector, true);
55 |
56 | receiver.allowlistSourceChain(chainSelector, true);
57 | receiver.allowlistSender(address(sender), true);
58 |
59 | uint256 senderBalanceBefore = ccipBnM.balanceOf(address(sender));
60 | uint256 receiverBalanceBefore = ccipBnM.balanceOf(address(receiver));
61 |
62 | bytes32 messageId = sender.sendMessagePayLINK(
63 | chainSelector,
64 | address(receiver),
65 | textToSend,
66 | address(ccipBnM),
67 | amountToSend
68 | );
69 |
70 | (
71 | bytes32 _messageId,
72 | string memory _text,
73 | address _tokenAddress,
74 | uint256 _tokenAmount
75 | ) = receiver.getLastReceivedMessageDetails();
76 |
77 | assertEq(_messageId, messageId);
78 | assertEq(_text, textToSend);
79 | assertEq(_tokenAddress, address(ccipBnM));
80 | assertEq(_tokenAmount, amountToSend);
81 |
82 | uint256 senderBalanceAfter = ccipBnM.balanceOf(address(sender));
83 | uint256 receiverBalanceAfter = ccipBnM.balanceOf(address(receiver));
84 |
85 | assertEq(senderBalanceAfter, senderBalanceBefore - amountToSend);
86 | assertEq(receiverBalanceAfter, receiverBalanceBefore + amountToSend);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/test/smoke/ccip/TokenTransferor.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.24;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {TokenTransferor} from "../../../src/test/ccip/TokenTransferor.sol";
6 | import {
7 | CCIPLocalSimulator,
8 | IRouterClient,
9 | LinkToken,
10 | BurnMintERC677Helper
11 | } from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
12 |
13 | contract TokenTransferorTest is Test {
14 | TokenTransferor public tokenTransferor;
15 | CCIPLocalSimulator public ccipLocalSimulator;
16 |
17 | uint64 chainSelector;
18 | BurnMintERC677Helper ccipBnM;
19 |
20 | function setUp() public {
21 | ccipLocalSimulator = new CCIPLocalSimulator();
22 | (uint64 chainSelector_, IRouterClient sourceRouter,,, LinkToken linkToken, BurnMintERC677Helper ccipBnM_,) =
23 | ccipLocalSimulator.configuration();
24 |
25 | tokenTransferor = new TokenTransferor(address(sourceRouter), address(linkToken));
26 |
27 | chainSelector = chainSelector_;
28 | ccipBnM = ccipBnM_;
29 | }
30 |
31 | function testTokenTransfer() public {
32 | uint256 amountToSend = 0.001 ether;
33 | uint256 amountForFees = 1 ether;
34 | address receiver = msg.sender;
35 |
36 | ccipBnM.drip(address(tokenTransferor));
37 |
38 | ccipLocalSimulator.requestLinkFromFaucet(address(tokenTransferor), amountForFees);
39 |
40 | tokenTransferor.allowlistDestinationChain(chainSelector, true);
41 |
42 | uint256 receiverBalanceBefore = ccipBnM.balanceOf(receiver);
43 | uint256 tokenTransferorBalanceBefore = ccipBnM.balanceOf(address(tokenTransferor));
44 |
45 | tokenTransferor.transferTokensPayLINK(chainSelector, receiver, address(ccipBnM), amountToSend);
46 |
47 | uint256 receiverBalanceAfter = ccipBnM.balanceOf(receiver);
48 | uint256 tokenTransferorBalanceAfter = ccipBnM.balanceOf(address(tokenTransferor));
49 |
50 | assertEq(receiverBalanceAfter, receiverBalanceBefore + amountToSend);
51 | assertEq(tokenTransferorBalanceAfter, tokenTransferorBalanceBefore - amountToSend);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/test/smoke/ccip/UnsafeTokenAndDataTransfer.spec.ts:
--------------------------------------------------------------------------------
1 | import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
2 | import { ethers } from "hardhat";
3 | import { expect } from "chai";
4 |
5 | describe("CCIPSender_Unsafe", function () {
6 |
7 | async function deploy() {
8 | const localSimulatorFactory = await ethers.getContractFactory("CCIPLocalSimulator");
9 | const localSimulator = await localSimulatorFactory.deploy();
10 |
11 | const config: {
12 | chainSelector_: bigint;
13 | sourceRouter_: string;
14 | destinationRouter_: string;
15 | wrappedNative_: string;
16 | linkToken_: string;
17 | ccipBnM_: string;
18 | ccipLnM_: string;
19 | } = await localSimulator.configuration();
20 |
21 | const CCIPSender_UnsafeFactory = await ethers.getContractFactory("CCIPSender_Unsafe");
22 | const CCIPSender_Unsafe = await CCIPSender_UnsafeFactory.deploy(config.linkToken_, config.sourceRouter_);
23 |
24 | const CCIPReceiver_UnsafeFactory = await ethers.getContractFactory("CCIPReceiver_Unsafe");
25 | const CCIPReceiver_Unsafe = await CCIPReceiver_UnsafeFactory.deploy(config.destinationRouter_);
26 |
27 | const ccipBnMFactory = await ethers.getContractFactory("BurnMintERC677Helper");
28 | const ccipBnM = ccipBnMFactory.attach(config.ccipBnM_);
29 |
30 | return { localSimulator, CCIPSender_Unsafe, CCIPReceiver_Unsafe, config, ccipBnM };
31 | }
32 |
33 | it("should transfer Hello World and 100 CCIP_BnM tokens", async function () {
34 | const { CCIPSender_Unsafe, CCIPReceiver_Unsafe, config, ccipBnM } = await loadFixture(deploy);
35 |
36 | const ONE_ETHER = 1_000_000_000_000_000_000n;
37 |
38 | await ccipBnM.drip(CCIPSender_Unsafe.target);
39 | expect(await ccipBnM.totalSupply()).to.deep.equal(ONE_ETHER);
40 |
41 | const ccipSenderUnsafeBalanceBefore = await ccipBnM.balanceOf(CCIPSender_Unsafe.target);
42 | const ccipReceiverUnsafeBalanceBefore = await ccipBnM.balanceOf(CCIPReceiver_Unsafe.target);
43 |
44 | const textToSend = `Hello World`;
45 | const amountToSend = 100n;
46 |
47 | await CCIPSender_Unsafe.send(CCIPReceiver_Unsafe.target, textToSend, config.chainSelector_, config.ccipBnM_, amountToSend);
48 |
49 | const ccipSenderUnsafeBalanceAfter = await ccipBnM.balanceOf(CCIPSender_Unsafe.target);
50 | const ccipReceiverUnsafeBalanceAfter = await ccipBnM.balanceOf(CCIPReceiver_Unsafe.target);
51 | expect(ccipSenderUnsafeBalanceAfter).to.deep.equal(ccipSenderUnsafeBalanceBefore - amountToSend);
52 | expect(ccipReceiverUnsafeBalanceAfter).to.deep.equal(ccipReceiverUnsafeBalanceBefore + amountToSend);
53 |
54 | const received = await CCIPReceiver_Unsafe.text();
55 | expect(received).to.equal(textToSend);
56 | });
57 | });
58 |
59 |
--------------------------------------------------------------------------------
/test/smoke/ccip/UnsafeTokenAndDataTransfer.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {CCIPSender_Unsafe} from "../../../src/test/ccip/CCIPSender_Unsafe.sol";
6 | import {CCIPReceiver_Unsafe} from "../../../src/test/ccip/CCIPReceiver_Unsafe.sol";
7 | import {CCIPLocalSimulator, IRouterClient, LinkToken, BurnMintERC677Helper} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
8 |
9 | contract UnsafeTokenAndDataTransferTest is Test {
10 | CCIPSender_Unsafe public sender;
11 | CCIPReceiver_Unsafe public receiver;
12 |
13 | uint64 chainSelector;
14 | BurnMintERC677Helper ccipBnM;
15 |
16 | function setUp() public {
17 | CCIPLocalSimulator ccipLocalSimulator = new CCIPLocalSimulator();
18 |
19 | (
20 | uint64 chainSelector_,
21 | IRouterClient sourceRouter_,
22 | IRouterClient destinationRouter_,
23 | ,
24 | LinkToken linkToken_,
25 | BurnMintERC677Helper ccipBnM_,
26 |
27 | ) = ccipLocalSimulator.configuration();
28 |
29 | chainSelector = chainSelector_;
30 | ccipBnM = ccipBnM_;
31 | address sourceRouter = address(sourceRouter_);
32 | address linkToken = address(linkToken_);
33 | address destinationRouter = address(destinationRouter_);
34 |
35 | sender = new CCIPSender_Unsafe(linkToken, sourceRouter);
36 | receiver = new CCIPReceiver_Unsafe(destinationRouter);
37 | }
38 |
39 | function testSend() public {
40 | ccipBnM.drip(address(sender)); // 1e18
41 | assertEq(ccipBnM.totalSupply(), 1 ether);
42 |
43 | string memory textToSend = "Hello World";
44 | uint256 amountToSend = 100;
45 |
46 | bytes32 messageId = sender.send(
47 | address(receiver),
48 | textToSend,
49 | chainSelector,
50 | address(ccipBnM),
51 | amountToSend
52 | );
53 | console2.logBytes32(messageId);
54 |
55 | string memory receivedText = receiver.text();
56 |
57 | assertEq(receivedText, textToSend);
58 |
59 | assertEq(ccipBnM.balanceOf(address(sender)), 1 ether - amountToSend);
60 | assertEq(ccipBnM.balanceOf(address(receiver)), amountToSend);
61 | assertEq(ccipBnM.totalSupply(), 1 ether);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/test/smoke/data-feeds/BasicDataConsumerV3.spec.ts:
--------------------------------------------------------------------------------
1 | import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
2 | import { ethers } from "hardhat";
3 | import { expect } from "chai";
4 |
5 | describe("BasicDataConsumerV3", function () {
6 |
7 | async function deploy() {
8 | const decimals = 8;
9 | const initialAnswer = 100000000000;
10 |
11 | const mockV3AggregatorFactory = await ethers.getContractFactory("MockV3Aggregator");
12 | const mockV3Aggregator = await mockV3AggregatorFactory.deploy(decimals, initialAnswer);
13 |
14 | const basicDataConsumerV3Factory = await ethers.getContractFactory("BasicDataConsumerV3");
15 | const basicDataConsumerV3 = await basicDataConsumerV3Factory.deploy(mockV3Aggregator.target);
16 |
17 | return { initialAnswer, basicDataConsumerV3 };
18 | }
19 |
20 | it("should return answer from mock aggregator", async function () {
21 | const { initialAnswer, basicDataConsumerV3 } = await loadFixture(deploy);
22 |
23 | const answer = await basicDataConsumerV3.getChainlinkDataFeedLatestAnswer();
24 |
25 | expect(answer).to.deep.equal(initialAnswer);
26 | });
27 | });
28 |
29 |
--------------------------------------------------------------------------------
/test/smoke/data-feeds/BasicDataConsumerV3.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {MockV3Aggregator} from "@chainlink/local/src/data-feeds/MockV3Aggregator.sol";
6 |
7 | import {BasicDataConsumerV3} from "../../../src/test/data-feeds/BasicDataConsumerV3.sol";
8 |
9 | contract BasicDataConsumerV3Test is Test {
10 | BasicDataConsumerV3 public consumer;
11 | MockV3Aggregator public mockEthUsdAggregator;
12 |
13 | uint8 public decimals;
14 | int256 public initialAnswer;
15 |
16 | function setUp() public {
17 | decimals = 8;
18 | initialAnswer = 100000000000;
19 | mockEthUsdAggregator = new MockV3Aggregator(decimals, initialAnswer);
20 | consumer = new BasicDataConsumerV3(address(mockEthUsdAggregator));
21 | }
22 |
23 | function test_smoke() public {
24 | int256 answer = consumer.getChainlinkDataFeedLatestAnswer();
25 | assertEq(answer, initialAnswer, "answer should be equal to initialAnswer");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/smoke/data-streams/ChainlinkDataStreamProvider.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {
6 | DataStreamsLocalSimulator,
7 | MockVerifierProxy,
8 | MockFeeManager,
9 | LinkToken
10 | } from "@chainlink/local/src/data-streams/DataStreamsLocalSimulator.sol";
11 | import {MockReportGenerator} from "@chainlink/local/src/data-streams/MockReportGenerator.sol";
12 |
13 | import {ChainlinkDataStreamProvider} from "../../../src/test/data-streams/ChainlinkDataStreamProvider.sol";
14 |
15 | contract ChainlinkDataStreamProviderTest is Test {
16 | DataStreamsLocalSimulator public dataStreamsLocalSimulator;
17 | MockReportGenerator public mockReportGenerator;
18 | MockFeeManager public mockFeeManager;
19 |
20 | ChainlinkDataStreamProvider public consumer;
21 | int192 public initialPrice;
22 |
23 | function setUp() public {
24 | dataStreamsLocalSimulator = new DataStreamsLocalSimulator();
25 | (, LinkToken linkToken_,, MockVerifierProxy mockVerifierProxy_, MockFeeManager mockFeeManager_,) =
26 | dataStreamsLocalSimulator.configuration();
27 |
28 | mockFeeManager = mockFeeManager_;
29 |
30 | initialPrice = 1 ether;
31 | mockReportGenerator = new MockReportGenerator(initialPrice);
32 |
33 | consumer = new ChainlinkDataStreamProvider(address(mockVerifierProxy_), address(linkToken_));
34 | }
35 |
36 | function test_smoke() public {
37 | mockFeeManager.setMockDiscount(address(consumer), 1e18); // 1e18 => 100% discount on fees
38 |
39 | int192 wantPrice = 200;
40 | int192 wantBid = 100;
41 | int192 wantAsk = 300;
42 | mockReportGenerator.updatePriceBidAndAsk(wantPrice, wantBid, wantAsk);
43 |
44 | bytes32 feedId = hex"0003777777777777777777777777777777777777777777777777777777777777";
45 | address token = 0x7777777777777777777777777777777777777777;
46 | consumer.setBytes32(token, feedId);
47 |
48 | (bytes memory signedReportV3,) = mockReportGenerator.generateReportV3();
49 |
50 | (address gotToken, int192 gotBid, int192 gotAsk, /*uint32 gotObservationsTimestamp*/ ) =
51 | consumer.getOraclePrice(token, signedReportV3);
52 |
53 | assertEq(gotToken, token);
54 | assertEq(gotBid, wantBid);
55 | assertEq(gotAsk, wantAsk);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/test/smoke/data-streams/ClientReportsVerifier.spec.ts:
--------------------------------------------------------------------------------
1 | import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
2 | import { ethers } from "hardhat";
3 | import { assert } from "chai";
4 | import { MockReportGenerator } from "../../../scripts/data-streams/MockReportGenerator";
5 |
6 | describe("ClientReportsVerifier", function () {
7 |
8 | async function deploy() {
9 | const localSimulatorFactory = await ethers.getContractFactory("DataStreamsLocalSimulator");
10 | const localSimulator = await localSimulatorFactory.deploy();
11 |
12 | const config: {
13 | wrappedNative_: string;
14 | linkToken_: string;
15 | mockVerifier_: string;
16 | mockVerifierProxy_: string;
17 | mockFeeManager_: string;
18 | mockRewardManager_: string;
19 | } = await localSimulator.configuration();
20 |
21 | const initialPrice = ethers.parseEther("1");
22 | const mockReportGenerator = new MockReportGenerator(initialPrice);
23 |
24 | const consumerFactory = await ethers.getContractFactory("ClientReportsVerifier");
25 | const consumer = await consumerFactory.deploy(config.mockVerifierProxy_);
26 |
27 | await mockReportGenerator.updateFees(ethers.parseEther("1"), ethers.parseEther("0.5"));
28 |
29 | await localSimulator.requestLinkFromFaucet(consumer.target, ethers.parseEther("1"));
30 |
31 | // const mockFeeManager = await ethers.getContractAt("MockFeeManager", config.mockFeeManager_);
32 | // await mockFeeManager.setMockDiscount(consumer.target, ethers.parseEther("1")); // 1e18 => 100% discount on fees
33 |
34 | return { consumer, initialPrice, mockReportGenerator };
35 | }
36 |
37 | it("should verify Data Streams report", async function () {
38 | const { consumer, initialPrice, mockReportGenerator } = await loadFixture(deploy);
39 |
40 | const { signedReport, report } = await mockReportGenerator.generateReportV3();
41 |
42 | await consumer.verifyReport(signedReport);
43 |
44 | const lastDecodedPrice = await consumer.lastDecodedPrice();
45 | assert(lastDecodedPrice === initialPrice);
46 | });
47 | });
48 |
49 |
--------------------------------------------------------------------------------
/test/smoke/data-streams/ClientReportsVerifier.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.19;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {
6 | DataStreamsLocalSimulator,
7 | MockVerifierProxy
8 | } from "@chainlink/local/src/data-streams/DataStreamsLocalSimulator.sol";
9 | import {MockReportGenerator} from "@chainlink/local/src/data-streams/MockReportGenerator.sol";
10 |
11 | import {ClientReportsVerifier} from "../../../src/test/data-streams/ClientReportsVerifier.sol";
12 |
13 | contract ClientReportsVerifierTest is Test {
14 | DataStreamsLocalSimulator public dataStreamsLocalSimulator;
15 | MockReportGenerator public mockReportGenerator;
16 |
17 | ClientReportsVerifier public consumer;
18 | int192 initialPrice;
19 |
20 | function setUp() public {
21 | dataStreamsLocalSimulator = new DataStreamsLocalSimulator();
22 | (,,, MockVerifierProxy mockVerifierProxy_,,) = dataStreamsLocalSimulator.configuration();
23 |
24 | initialPrice = 1 ether;
25 | mockReportGenerator = new MockReportGenerator(initialPrice);
26 |
27 | consumer = new ClientReportsVerifier(address(mockVerifierProxy_));
28 | }
29 |
30 | function test_smoke() public {
31 | mockReportGenerator.updateFees(1 ether, 0.5 ether);
32 | (bytes memory signedReportV3,) = mockReportGenerator.generateReportV3();
33 |
34 | dataStreamsLocalSimulator.requestLinkFromFaucet(address(consumer), 1 ether);
35 |
36 | consumer.verifyReport(signedReportV3);
37 |
38 | int192 lastDecodedPrice = consumer.lastDecodedPrice();
39 | assertEq(lastDecodedPrice, initialPrice);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/test/smoke/data-streams/ERC7412Compatible.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.24;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {
6 | DataStreamsLocalSimulator,
7 | MockVerifierProxy
8 | } from "@chainlink/local/src/data-streams/DataStreamsLocalSimulator.sol";
9 | import {MockReportGenerator} from "@chainlink/local/src/data-streams/MockReportGenerator.sol";
10 |
11 | import {DataStreamsERC7412Compatible} from "../../../src/test/data-streams/ERC7412Compatible.sol";
12 |
13 | contract ERC7412CompatibleTest is Test {
14 | DataStreamsLocalSimulator public dataStreamsLocalSimulator;
15 | MockReportGenerator public mockReportGenerator;
16 |
17 | DataStreamsERC7412Compatible public consumer;
18 |
19 | string public constant STRING_DATASTREAMS_FEEDLABEL = "feedIDs";
20 | string public constant STRING_DATASTREAMS_QUERYLABEL = "timestamp";
21 |
22 | function setUp() public {
23 | dataStreamsLocalSimulator = new DataStreamsLocalSimulator();
24 | (,,, MockVerifierProxy mockVerifierProxy_,,) = dataStreamsLocalSimulator.configuration();
25 |
26 | int192 initialPrice = 1 ether;
27 | mockReportGenerator = new MockReportGenerator(initialPrice);
28 |
29 | consumer = new DataStreamsERC7412Compatible(address(mockVerifierProxy_));
30 | }
31 |
32 | function test_smoke() public {
33 | mockReportGenerator.updateFees(1 ether, 0.5 ether);
34 | dataStreamsLocalSimulator.requestLinkFromFaucet(address(consumer), 0.5 ether);
35 |
36 | bytes32 feedId = hex"0002777777777777777777777777777777777777777777777777777777777777";
37 | uint32 stalenessTolerance = 5 minutes;
38 |
39 | try consumer.generate7412CompatibleCall(feedId, stalenessTolerance) {}
40 | catch (bytes memory lowLevelData) {
41 | if (bytes4(abi.encodeWithSignature("OracleDataRequired(address,bytes)")) == bytes4(lowLevelData)) {
42 | uint256 length = lowLevelData.length;
43 | bytes memory revertData = new bytes(length - 4);
44 | for (uint256 i = 4; i < length; ++i) {
45 | revertData[i - 4] = lowLevelData[i];
46 | }
47 |
48 | (address oracleContract, bytes memory oracleQuery) = abi.decode(revertData, (address, bytes));
49 | assertEq(oracleContract, address(consumer));
50 |
51 | (, bytes32 revertedFeedId,,,) = abi.decode(oracleQuery, (string, bytes32, string, uint256, string));
52 | assertEq(feedId, revertedFeedId);
53 |
54 | (bytes memory signedReportV2,) = mockReportGenerator.generateReportV2();
55 |
56 | consumer.fulfillOracleQuery(signedReportV2);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/test/unit/ccip/CCIPLocalSimulatorUnit.t.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | import {Test, console2} from "forge-std/Test.sol";
5 | import {CCIPLocalSimulator, IRouterClient} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
6 |
7 | import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";
8 | import {ERC20} from
9 | "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v5.0.2/contracts/token/ERC20/ERC20.sol";
10 | import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol";
11 | import {AccessControl} from
12 | "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v5.0.2/contracts/access/AccessControl.sol";
13 |
14 | contract MockERC20TokenOwner is ERC20, OwnerIsCreator {
15 | constructor() ERC20("MockERC20Token", "MTK") {}
16 |
17 | function mint(address account, uint256 amount) public onlyOwner {
18 | _mint(account, amount);
19 | }
20 | }
21 |
22 | contract MockERC20TokenGetCCIPAdmin is ERC20 {
23 | address immutable i_CCIPAdmin;
24 |
25 | constructor() ERC20("MockERC20Token", "MTK") {
26 | i_CCIPAdmin = msg.sender;
27 | }
28 |
29 | function mint(address account, uint256 amount) public {
30 | require(msg.sender == i_CCIPAdmin, "Only CCIP Admin can mint");
31 | _mint(account, amount);
32 | }
33 |
34 | function getCCIPAdmin() public view returns (address) {
35 | return (i_CCIPAdmin);
36 | }
37 | }
38 |
39 | contract MockERC20AccessControl is ERC20, AccessControl {
40 | bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
41 |
42 | constructor() ERC20("MockERC20Token", "MTK") {
43 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
44 | }
45 |
46 | function mint(address account, uint256 amount) external {
47 | require(hasRole(MINTER_ROLE, msg.sender));
48 | _mint(account, amount);
49 | }
50 | }
51 |
52 | contract CCIPLocalSimulatorUnitTest is Test {
53 | CCIPLocalSimulator public ccipLocalSimulator;
54 | IRouterClient public router;
55 |
56 | MockERC20TokenOwner public mockERC20TokenOwner;
57 | MockERC20TokenGetCCIPAdmin public mockERC20TokenGetCCIPAdmin;
58 | MockERC20AccessControl public mockERC20AccessControl;
59 |
60 | address alice;
61 | address bob;
62 | uint64 chainSelector;
63 |
64 | function setUp() public {
65 | ccipLocalSimulator = new CCIPLocalSimulator();
66 |
67 | (uint64 chainSelector_, IRouterClient sourceRouter,,,,,) = ccipLocalSimulator.configuration();
68 | chainSelector = chainSelector_;
69 | router = sourceRouter;
70 |
71 | alice = makeAddr("alice");
72 | bob = makeAddr("bob");
73 |
74 | vm.startPrank(alice);
75 | mockERC20TokenOwner = new MockERC20TokenOwner();
76 | mockERC20TokenGetCCIPAdmin = new MockERC20TokenGetCCIPAdmin();
77 | mockERC20AccessControl = new MockERC20AccessControl();
78 | vm.stopPrank();
79 |
80 | assertEq(mockERC20TokenOwner.owner(), alice);
81 | assertEq(mockERC20TokenGetCCIPAdmin.getCCIPAdmin(), alice);
82 | }
83 |
84 | function test_shouldSupportNewTokenIfCalledByOwner() public {
85 | address[] memory supportedTokensBefore = ccipLocalSimulator.getSupportedTokens(chainSelector);
86 |
87 | vm.startPrank(alice);
88 | ccipLocalSimulator.supportNewTokenViaOwner(address(mockERC20TokenOwner));
89 | vm.stopPrank();
90 |
91 | address[] memory supportedTokensAfter = ccipLocalSimulator.getSupportedTokens(chainSelector);
92 | assertEq(supportedTokensAfter.length, supportedTokensBefore.length + 1);
93 | assertEq(supportedTokensAfter[supportedTokensAfter.length - 1], address(mockERC20TokenOwner));
94 | }
95 |
96 | function test_shouldRevertIfSupportNewTokenIsNotCalledByOwner() public {
97 | vm.startPrank(bob);
98 | vm.expectRevert(
99 | abi.encodeWithSelector(CCIPLocalSimulator.CCIPLocalSimulator__MsgSenderIsNotTokenOwner.selector)
100 | );
101 | ccipLocalSimulator.supportNewTokenViaOwner(address(mockERC20TokenOwner));
102 | vm.stopPrank();
103 | }
104 |
105 | function test_shouldSupportNewTokenIfCalledByCCIPAdmin() public {
106 | address[] memory supportedTokensBefore = ccipLocalSimulator.getSupportedTokens(chainSelector);
107 |
108 | vm.startPrank(alice);
109 | ccipLocalSimulator.supportNewTokenViaGetCCIPAdmin(address(mockERC20TokenGetCCIPAdmin));
110 | vm.stopPrank();
111 |
112 | address[] memory supportedTokensAfter = ccipLocalSimulator.getSupportedTokens(chainSelector);
113 | assertEq(supportedTokensAfter.length, supportedTokensBefore.length + 1);
114 | assertEq(supportedTokensAfter[supportedTokensAfter.length - 1], address(mockERC20TokenGetCCIPAdmin));
115 | }
116 |
117 | function test_shouldRevertIfSupportNewTokenIsNotCalledByCCIPAdmin() public {
118 | vm.startPrank(bob);
119 | vm.expectRevert(
120 | abi.encodeWithSelector(CCIPLocalSimulator.CCIPLocalSimulator__MsgSenderIsNotTokenOwner.selector)
121 | );
122 | ccipLocalSimulator.supportNewTokenViaGetCCIPAdmin(address(mockERC20TokenGetCCIPAdmin));
123 | vm.stopPrank();
124 | }
125 |
126 | function test_shouldSupportNewTokenIfCalledByAccessControlDefaultAdmin() public {
127 | address[] memory supportedTokensBefore = ccipLocalSimulator.getSupportedTokens(chainSelector);
128 |
129 | vm.startPrank(alice);
130 | ccipLocalSimulator.supportNewTokenViaAccessControlDefaultAdmin(address(mockERC20AccessControl));
131 | vm.stopPrank();
132 |
133 | address[] memory supportedTokensAfter = ccipLocalSimulator.getSupportedTokens(chainSelector);
134 | assertEq(supportedTokensAfter.length, supportedTokensBefore.length + 1);
135 | assertEq(supportedTokensAfter[supportedTokensAfter.length - 1], address(mockERC20AccessControl));
136 | }
137 |
138 | function test_shouldRevertIfSupportNewTokenIsNotCalledByAccessControlDefaultAdmin() public {
139 | vm.startPrank(bob);
140 | vm.expectRevert(
141 | abi.encodeWithSelector(
142 | CCIPLocalSimulator.CCIPLocalSimulator__RequiredRoleNotFound.selector,
143 | bob,
144 | mockERC20AccessControl.DEFAULT_ADMIN_ROLE(),
145 | address(mockERC20AccessControl)
146 | )
147 | );
148 | ccipLocalSimulator.supportNewTokenViaAccessControlDefaultAdmin(address(mockERC20AccessControl));
149 | vm.stopPrank();
150 | }
151 |
152 | function test_shouldSendCCIPMessageWithEvmExtraArgsV1() public {
153 | vm.startPrank(alice);
154 | Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
155 | receiver: abi.encode(bob),
156 | data: "",
157 | tokenAmounts: new Client.EVMTokenAmount[](0),
158 | extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 500_000})),
159 | feeToken: address(0)
160 | });
161 | router.ccipSend(chainSelector, message);
162 | vm.stopPrank();
163 | }
164 |
165 | function test_shouldSendCCIPMessageWithEvmExtraArgsV2() public {
166 | vm.startPrank(alice);
167 | Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
168 | receiver: abi.encode(bob),
169 | data: "",
170 | tokenAmounts: new Client.EVMTokenAmount[](0),
171 | extraArgs: Client._argsToBytes(Client.EVMExtraArgsV2({gasLimit: 500_000, allowOutOfOrderExecution: true})),
172 | feeToken: address(0)
173 | });
174 | router.ccipSend(chainSelector, message);
175 | vm.stopPrank();
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "skipLibCheck": true,
9 | "resolveJsonModule": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------