diff --git a/.github/e2e-tests.yml b/.github/e2e-tests.yml index 5c4f7e6c82e..f811dce8b36 100644 --- a/.github/e2e-tests.yml +++ b/.github/e2e-tests.yml @@ -962,6 +962,20 @@ runner-test-matrix: test_env_vars: E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2 E2E_JD_VERSION: 0.4.0 + + - id: smoke/ccip_batching_test.go:* + path: integration-tests/smoke/ccip_batching_test.go + test_env_type: docker + runs_on: ubuntu-latest + triggers: + - PR E2E Core Tests + - Merge Queue E2E Core Tests + - Nightly E2E Tests + test_cmd: cd integration-tests/ && go test smoke/ccip_batching_test.go -timeout 12m -test.parallel=1 -count=1 -json + pyroscope_env: ci-smoke-ccipv1_6-evm-simulated + test_env_vars: + E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3 + E2E_JD_VERSION: 0.4.0 - id: smoke/fee_boosting_test.go:* path: integration-tests/smoke/fee_boosting_test.go diff --git a/contracts/scripts/native_solc_compile_all_ccip b/contracts/scripts/native_solc_compile_all_ccip index 5d5b8f73115..bc51fe54067 100755 --- a/contracts/scripts/native_solc_compile_all_ccip +++ b/contracts/scripts/native_solc_compile_all_ccip @@ -79,6 +79,7 @@ compileContract ccip/test/mocks/MockE2EUSDCTokenMessenger.sol compileContract ccip/test/mocks/MockE2EUSDCTransmitter.sol compileContract ccip/test/WETH9.sol compileContract ccip/test/helpers/CCIPReaderTester.sol +compileContract ccip/test/helpers/Multicall3.sol # Encoding Utils compileContract ccip/interfaces/encodingutils/ICCIPEncodingUtils.sol diff --git a/contracts/src/v0.8/ccip/test/helpers/Multicall3.sol b/contracts/src/v0.8/ccip/test/helpers/Multicall3.sol new file mode 100644 index 00000000000..60b8c72b5ef --- /dev/null +++ b/contracts/src/v0.8/ccip/test/helpers/Multicall3.sol @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: MIT +// cribbed from https://github.com/mds1/multicall/commit/ebd8b64457454fc10037b3a3ea858f9c08dad4d3 +// changed solidity version to 0.8.24 from 0.8.12 +pragma solidity 0.8.24; + +/// @title Multicall3 +/// @notice Aggregate results from multiple function calls +/// @dev Multicall & Multicall2 backwards-compatible +/// @dev Aggregate methods are marked `payable` to save 24 gas per call +/// @author Michael Elliot +/// @author Joshua Levine +/// @author Nick Johnson +/// @author Andreas Bigger +/// @author Matt Solomon +contract Multicall3 { + struct Call { + address target; + bytes callData; + } + + struct Call3 { + address target; + bool allowFailure; + bytes callData; + } + + struct Call3Value { + address target; + bool allowFailure; + uint256 value; + bytes callData; + } + + struct Result { + bool success; + bytes returnData; + } + + /// @notice Backwards-compatible call aggregation with Multicall + /// @param calls An array of Call structs + /// @return blockNumber The block number where the calls were executed + /// @return returnData An array of bytes containing the responses + function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData) { + blockNumber = block.number; + uint256 length = calls.length; + returnData = new bytes[](length); + Call calldata call; + for (uint256 i = 0; i < length;) { + bool success; + call = calls[i]; + (success, returnData[i]) = call.target.call(call.callData); + require(success, "Multicall3: call failed"); + unchecked { ++i; } + } + } + + /// @notice Backwards-compatible with Multicall2 + /// @notice Aggregate calls without requiring success + /// @param requireSuccess If true, require all calls to succeed + /// @param calls An array of Call structs + /// @return returnData An array of Result structs + function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData) { + uint256 length = calls.length; + returnData = new Result[](length); + Call calldata call; + for (uint256 i = 0; i < length;) { + Result memory result = returnData[i]; + call = calls[i]; + (result.success, result.returnData) = call.target.call(call.callData); + if (requireSuccess) require(result.success, "Multicall3: call failed"); + unchecked { ++i; } + } + } + + /// @notice Backwards-compatible with Multicall2 + /// @notice Aggregate calls and allow failures using tryAggregate + /// @param calls An array of Call structs + /// @return blockNumber The block number where the calls were executed + /// @return blockHash The hash of the block where the calls were executed + /// @return returnData An array of Result structs + function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) { + blockNumber = block.number; + blockHash = blockhash(block.number); + returnData = tryAggregate(requireSuccess, calls); + } + + /// @notice Backwards-compatible with Multicall2 + /// @notice Aggregate calls and allow failures using tryAggregate + /// @param calls An array of Call structs + /// @return blockNumber The block number where the calls were executed + /// @return blockHash The hash of the block where the calls were executed + /// @return returnData An array of Result structs + function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) { + (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); + } + + /// @notice Aggregate calls, ensuring each returns success if required + /// @param calls An array of Call3 structs + /// @return returnData An array of Result structs + function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData) { + uint256 length = calls.length; + returnData = new Result[](length); + Call3 calldata calli; + for (uint256 i = 0; i < length;) { + Result memory result = returnData[i]; + calli = calls[i]; + (result.success, result.returnData) = calli.target.call(calli.callData); + assembly { + // Revert if the call fails and failure is not allowed + // `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)` + if iszero(or(calldataload(add(calli, 0x20)), mload(result))) { + // set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // set data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // set length of revert string + mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017) + // set revert string: bytes32(abi.encodePacked("Multicall3: call failed")) + mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000) + revert(0x00, 0x64) + } + } + unchecked { ++i; } + } + } + + /// @notice Aggregate calls with a msg value + /// @notice Reverts if msg.value is less than the sum of the call values + /// @param calls An array of Call3Value structs + /// @return returnData An array of Result structs + function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData) { + uint256 valAccumulator; + uint256 length = calls.length; + returnData = new Result[](length); + Call3Value calldata calli; + for (uint256 i = 0; i < length;) { + Result memory result = returnData[i]; + calli = calls[i]; + uint256 val = calli.value; + // Humanity will be a Type V Kardashev Civilization before this overflows - andreas + // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256 + unchecked { valAccumulator += val; } + (result.success, result.returnData) = calli.target.call{value: val}(calli.callData); + assembly { + // Revert if the call fails and failure is not allowed + // `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)` + if iszero(or(calldataload(add(calli, 0x20)), mload(result))) { + // set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // set data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // set length of revert string + mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017) + // set revert string: bytes32(abi.encodePacked("Multicall3: call failed")) + mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000) + revert(0x00, 0x84) + } + } + unchecked { ++i; } + } + // Finally, make sure the msg.value = SUM(call[0...i].value) + require(msg.value == valAccumulator, "Multicall3: value mismatch"); + } + + /// @notice Returns the block hash for the given block number + /// @param blockNumber The block number + function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { + blockHash = blockhash(blockNumber); + } + + /// @notice Returns the block number + function getBlockNumber() public view returns (uint256 blockNumber) { + blockNumber = block.number; + } + + /// @notice Returns the block coinbase + function getCurrentBlockCoinbase() public view returns (address coinbase) { + coinbase = block.coinbase; + } + + /// @notice Returns the block difficulty + function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { + difficulty = block.difficulty; + } + + /// @notice Returns the block gas limit + function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { + gaslimit = block.gaslimit; + } + + /// @notice Returns the block timestamp + function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { + timestamp = block.timestamp; + } + + /// @notice Returns the (ETH) balance of a given address + function getEthBalance(address addr) public view returns (uint256 balance) { + balance = addr.balance; + } + + /// @notice Returns the block hash of the last block + function getLastBlockHash() public view returns (bytes32 blockHash) { + unchecked { + blockHash = blockhash(block.number - 1); + } + } + + /// @notice Gets the base fee of the given block + /// @notice Can revert if the BASEFEE opcode is not implemented by the given chain + function getBasefee() public view returns (uint256 basefee) { + basefee = block.basefee; + } + + /// @notice Returns the chain id + function getChainId() public view returns (uint256 chainid) { + chainid = block.chainid; + } +} diff --git a/core/gethwrappers/ccip/generated/multicall3/multicall3.go b/core/gethwrappers/ccip/generated/multicall3/multicall3.go new file mode 100644 index 00000000000..95cbe6fe3c5 --- /dev/null +++ b/core/gethwrappers/ccip/generated/multicall3/multicall3.go @@ -0,0 +1,525 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package multicall3 + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type Multicall3Call struct { + Target common.Address + CallData []byte +} + +type Multicall3Call3 struct { + Target common.Address + AllowFailure bool + CallData []byte +} + +type Multicall3Call3Value struct { + Target common.Address + AllowFailure bool + Value *big.Int + CallData []byte +} + +type Multicall3Result struct { + Success bool + ReturnData []byte +} + +var Multicall3MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowFailure\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call3[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate3\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowFailure\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call3Value[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate3Value\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"blockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBasefee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockCoinbase\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockDifficulty\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockGasLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryAggregate\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryBlockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610eb2806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bb7565b61014d610148366004610a85565b6104ef565b604051610111929190610bd1565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c5b565b610690565b60405161011193929190610cb5565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610cdd565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c5b565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d13565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d2c565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d5b565b6020026020010151905087878381811061035d5761035d610d5b565b905060200281019061036f9190610d8a565b6040810135958601959093506103886020850185610cdd565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dc8565b6040516103ba929190610e2d565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d2c565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d5b565b90506020028101906105749190610e3d565b92506105836020840184610cdd565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dc8565b6040516105b4929190610e2d565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d5b565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d2c565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d5b565b6020026020010151905086868381811061074c5761074c610d5b565b905060200281019061075e9190610e71565b925061076d6020840184610cdd565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dc8565b60405161079e929190610e2d565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d2c565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d5b565b602002602001015190508686838181106108fb576108fb610d5b565b905060200281019061090d9190610e3d565b925061091c6020840184610cdd565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dc8565b60405161094d929190610e2d565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600082825180855260208086019550808260051b84010181860160005b84811015610baa578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9681860183610ac7565b9a86019a9450505090830190600101610b48565b5090979650505050505050565b602081526000610bca6020830184610b2b565b9392505050565b60006040820184835260206040602085015281855180845260608601915060608160051b87010193506020870160005b82811015610c4d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c3b868351610ac7565b95509284019290840190600101610c01565b509398975050505050505050565b600080600060408486031215610c7057600080fd5b83358015158114610c8057600080fd5b9250602084013567ffffffffffffffff811115610c9c57600080fd5b610ca886828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd46060830184610b2b565b95945050505050565b600060208284031215610cef57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bca57600080fd5b600060208284031215610d2557600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dbe57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610dfd57600080fd5b83018035915067ffffffffffffffff821115610e1857600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dbe57600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dbe57600080fdfea164736f6c6343000818000a", +} + +var Multicall3ABI = Multicall3MetaData.ABI + +var Multicall3Bin = Multicall3MetaData.Bin + +func DeployMulticall3(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Multicall3, error) { + parsed, err := Multicall3MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(Multicall3Bin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Multicall3{address: address, abi: *parsed, Multicall3Caller: Multicall3Caller{contract: contract}, Multicall3Transactor: Multicall3Transactor{contract: contract}, Multicall3Filterer: Multicall3Filterer{contract: contract}}, nil +} + +type Multicall3 struct { + address common.Address + abi abi.ABI + Multicall3Caller + Multicall3Transactor + Multicall3Filterer +} + +type Multicall3Caller struct { + contract *bind.BoundContract +} + +type Multicall3Transactor struct { + contract *bind.BoundContract +} + +type Multicall3Filterer struct { + contract *bind.BoundContract +} + +type Multicall3Session struct { + Contract *Multicall3 + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type Multicall3CallerSession struct { + Contract *Multicall3Caller + CallOpts bind.CallOpts +} + +type Multicall3TransactorSession struct { + Contract *Multicall3Transactor + TransactOpts bind.TransactOpts +} + +type Multicall3Raw struct { + Contract *Multicall3 +} + +type Multicall3CallerRaw struct { + Contract *Multicall3Caller +} + +type Multicall3TransactorRaw struct { + Contract *Multicall3Transactor +} + +func NewMulticall3(address common.Address, backend bind.ContractBackend) (*Multicall3, error) { + abi, err := abi.JSON(strings.NewReader(Multicall3ABI)) + if err != nil { + return nil, err + } + contract, err := bindMulticall3(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Multicall3{address: address, abi: abi, Multicall3Caller: Multicall3Caller{contract: contract}, Multicall3Transactor: Multicall3Transactor{contract: contract}, Multicall3Filterer: Multicall3Filterer{contract: contract}}, nil +} + +func NewMulticall3Caller(address common.Address, caller bind.ContractCaller) (*Multicall3Caller, error) { + contract, err := bindMulticall3(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &Multicall3Caller{contract: contract}, nil +} + +func NewMulticall3Transactor(address common.Address, transactor bind.ContractTransactor) (*Multicall3Transactor, error) { + contract, err := bindMulticall3(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &Multicall3Transactor{contract: contract}, nil +} + +func NewMulticall3Filterer(address common.Address, filterer bind.ContractFilterer) (*Multicall3Filterer, error) { + contract, err := bindMulticall3(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &Multicall3Filterer{contract: contract}, nil +} + +func bindMulticall3(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := Multicall3MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_Multicall3 *Multicall3Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Multicall3.Contract.Multicall3Caller.contract.Call(opts, result, method, params...) +} + +func (_Multicall3 *Multicall3Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Multicall3.Contract.Multicall3Transactor.contract.Transfer(opts) +} + +func (_Multicall3 *Multicall3Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Multicall3.Contract.Multicall3Transactor.contract.Transact(opts, method, params...) +} + +func (_Multicall3 *Multicall3CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Multicall3.Contract.contract.Call(opts, result, method, params...) +} + +func (_Multicall3 *Multicall3TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Multicall3.Contract.contract.Transfer(opts) +} + +func (_Multicall3 *Multicall3TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Multicall3.Contract.contract.Transact(opts, method, params...) +} + +func (_Multicall3 *Multicall3Caller) GetBasefee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getBasefee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetBasefee() (*big.Int, error) { + return _Multicall3.Contract.GetBasefee(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetBasefee() (*big.Int, error) { + return _Multicall3.Contract.GetBasefee(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetBlockHash(opts *bind.CallOpts, blockNumber *big.Int) ([32]byte, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getBlockHash", blockNumber) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetBlockHash(blockNumber *big.Int) ([32]byte, error) { + return _Multicall3.Contract.GetBlockHash(&_Multicall3.CallOpts, blockNumber) +} + +func (_Multicall3 *Multicall3CallerSession) GetBlockHash(blockNumber *big.Int) ([32]byte, error) { + return _Multicall3.Contract.GetBlockHash(&_Multicall3.CallOpts, blockNumber) +} + +func (_Multicall3 *Multicall3Caller) GetBlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getBlockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetBlockNumber() (*big.Int, error) { + return _Multicall3.Contract.GetBlockNumber(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetBlockNumber() (*big.Int, error) { + return _Multicall3.Contract.GetBlockNumber(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetChainId(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getChainId") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetChainId() (*big.Int, error) { + return _Multicall3.Contract.GetChainId(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetChainId() (*big.Int, error) { + return _Multicall3.Contract.GetChainId(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetCurrentBlockCoinbase(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getCurrentBlockCoinbase") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetCurrentBlockCoinbase() (common.Address, error) { + return _Multicall3.Contract.GetCurrentBlockCoinbase(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetCurrentBlockCoinbase() (common.Address, error) { + return _Multicall3.Contract.GetCurrentBlockCoinbase(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetCurrentBlockDifficulty(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getCurrentBlockDifficulty") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetCurrentBlockDifficulty() (*big.Int, error) { + return _Multicall3.Contract.GetCurrentBlockDifficulty(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetCurrentBlockDifficulty() (*big.Int, error) { + return _Multicall3.Contract.GetCurrentBlockDifficulty(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetCurrentBlockGasLimit(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getCurrentBlockGasLimit") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetCurrentBlockGasLimit() (*big.Int, error) { + return _Multicall3.Contract.GetCurrentBlockGasLimit(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetCurrentBlockGasLimit() (*big.Int, error) { + return _Multicall3.Contract.GetCurrentBlockGasLimit(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetCurrentBlockTimestamp(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getCurrentBlockTimestamp") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetCurrentBlockTimestamp() (*big.Int, error) { + return _Multicall3.Contract.GetCurrentBlockTimestamp(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetCurrentBlockTimestamp() (*big.Int, error) { + return _Multicall3.Contract.GetCurrentBlockTimestamp(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Caller) GetEthBalance(opts *bind.CallOpts, addr common.Address) (*big.Int, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getEthBalance", addr) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetEthBalance(addr common.Address) (*big.Int, error) { + return _Multicall3.Contract.GetEthBalance(&_Multicall3.CallOpts, addr) +} + +func (_Multicall3 *Multicall3CallerSession) GetEthBalance(addr common.Address) (*big.Int, error) { + return _Multicall3.Contract.GetEthBalance(&_Multicall3.CallOpts, addr) +} + +func (_Multicall3 *Multicall3Caller) GetLastBlockHash(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Multicall3.contract.Call(opts, &out, "getLastBlockHash") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_Multicall3 *Multicall3Session) GetLastBlockHash() ([32]byte, error) { + return _Multicall3.Contract.GetLastBlockHash(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3CallerSession) GetLastBlockHash() ([32]byte, error) { + return _Multicall3.Contract.GetLastBlockHash(&_Multicall3.CallOpts) +} + +func (_Multicall3 *Multicall3Transactor) Aggregate(opts *bind.TransactOpts, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.contract.Transact(opts, "aggregate", calls) +} + +func (_Multicall3 *Multicall3Session) Aggregate(calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.Aggregate(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3TransactorSession) Aggregate(calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.Aggregate(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3Transactor) Aggregate3(opts *bind.TransactOpts, calls []Multicall3Call3) (*types.Transaction, error) { + return _Multicall3.contract.Transact(opts, "aggregate3", calls) +} + +func (_Multicall3 *Multicall3Session) Aggregate3(calls []Multicall3Call3) (*types.Transaction, error) { + return _Multicall3.Contract.Aggregate3(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3TransactorSession) Aggregate3(calls []Multicall3Call3) (*types.Transaction, error) { + return _Multicall3.Contract.Aggregate3(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3Transactor) Aggregate3Value(opts *bind.TransactOpts, calls []Multicall3Call3Value) (*types.Transaction, error) { + return _Multicall3.contract.Transact(opts, "aggregate3Value", calls) +} + +func (_Multicall3 *Multicall3Session) Aggregate3Value(calls []Multicall3Call3Value) (*types.Transaction, error) { + return _Multicall3.Contract.Aggregate3Value(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3TransactorSession) Aggregate3Value(calls []Multicall3Call3Value) (*types.Transaction, error) { + return _Multicall3.Contract.Aggregate3Value(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3Transactor) BlockAndAggregate(opts *bind.TransactOpts, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.contract.Transact(opts, "blockAndAggregate", calls) +} + +func (_Multicall3 *Multicall3Session) BlockAndAggregate(calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.BlockAndAggregate(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3TransactorSession) BlockAndAggregate(calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.BlockAndAggregate(&_Multicall3.TransactOpts, calls) +} + +func (_Multicall3 *Multicall3Transactor) TryAggregate(opts *bind.TransactOpts, requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.contract.Transact(opts, "tryAggregate", requireSuccess, calls) +} + +func (_Multicall3 *Multicall3Session) TryAggregate(requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.TryAggregate(&_Multicall3.TransactOpts, requireSuccess, calls) +} + +func (_Multicall3 *Multicall3TransactorSession) TryAggregate(requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.TryAggregate(&_Multicall3.TransactOpts, requireSuccess, calls) +} + +func (_Multicall3 *Multicall3Transactor) TryBlockAndAggregate(opts *bind.TransactOpts, requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.contract.Transact(opts, "tryBlockAndAggregate", requireSuccess, calls) +} + +func (_Multicall3 *Multicall3Session) TryBlockAndAggregate(requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.TryBlockAndAggregate(&_Multicall3.TransactOpts, requireSuccess, calls) +} + +func (_Multicall3 *Multicall3TransactorSession) TryBlockAndAggregate(requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) { + return _Multicall3.Contract.TryBlockAndAggregate(&_Multicall3.TransactOpts, requireSuccess, calls) +} + +func (_Multicall3 *Multicall3) Address() common.Address { + return _Multicall3.address +} + +type Multicall3Interface interface { + GetBasefee(opts *bind.CallOpts) (*big.Int, error) + + GetBlockHash(opts *bind.CallOpts, blockNumber *big.Int) ([32]byte, error) + + GetBlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetChainId(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentBlockCoinbase(opts *bind.CallOpts) (common.Address, error) + + GetCurrentBlockDifficulty(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentBlockGasLimit(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentBlockTimestamp(opts *bind.CallOpts) (*big.Int, error) + + GetEthBalance(opts *bind.CallOpts, addr common.Address) (*big.Int, error) + + GetLastBlockHash(opts *bind.CallOpts) ([32]byte, error) + + Aggregate(opts *bind.TransactOpts, calls []Multicall3Call) (*types.Transaction, error) + + Aggregate3(opts *bind.TransactOpts, calls []Multicall3Call3) (*types.Transaction, error) + + Aggregate3Value(opts *bind.TransactOpts, calls []Multicall3Call3Value) (*types.Transaction, error) + + BlockAndAggregate(opts *bind.TransactOpts, calls []Multicall3Call) (*types.Transaction, error) + + TryAggregate(opts *bind.TransactOpts, requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) + + TryBlockAndAggregate(opts *bind.TransactOpts, requireSuccess bool, calls []Multicall3Call) (*types.Transaction, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 47aeb6db587..ff857028cac 100644 --- a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -15,6 +15,7 @@ mock_usdc_token_transmitter: ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmit mock_v3_aggregator_contract: ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.abi ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.bin 518e19efa2ff52b0fefd8e597b05765317ee7638189bfe34ca43de2f6599faf4 multi_aggregate_rate_limiter: ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.abi ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.bin c3cac2010c2815b484055bf981363a2bd04e7fbe7bb502dc8fd29a16165d221c multi_ocr3_helper: ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.abi ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.bin 79bfbd1f7d3c2aeee6301ae1275c39924a0b41f16b051d1c0046d3fc4265093d +multicall3: ../../../contracts/solc/v0.8.24/Multicall3/Multicall3.abi ../../../contracts/solc/v0.8.24/Multicall3/Multicall3.bin e4e20dcf1c46cb5877ebf7cf02665d2d03faddf96de750cc005f37f1f8abb360 nonce_manager: ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.abi ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.bin e6008490d916826cefd1903612db39621d51617300fc9bb42b68c6c117958198 offramp: ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.abi ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.bin d20e6c0baf08926b341c31ed0018983e135a75b7d120591de49ca4ece3824d0b onramp: ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.abi ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.bin 2bf74188a997218502031f177cb2df505b272d66b25fd341a741289e77380c59 diff --git a/core/gethwrappers/ccip/go_generate.go b/core/gethwrappers/ccip/go_generate.go index 9d6fa7c4645..7af98dba9e2 100644 --- a/core/gethwrappers/ccip/go_generate.go +++ b/core/gethwrappers/ccip/go_generate.go @@ -36,6 +36,7 @@ package ccip //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.bin MockE2EUSDCTokenMessenger mock_usdc_token_messenger //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.bin MockE2EUSDCTransmitter mock_usdc_token_transmitter //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.abi ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderTester.bin CCIPReaderTester ccip_reader_tester +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/Multicall3/Multicall3.abi ../../../contracts/solc/v0.8.24/Multicall3/Multicall3.bin Multicall3 multicall3 // EncodingUtils //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.abi ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.bin EncodingUtils ccip_encoding_utils diff --git a/deployment/ccip/deploy.go b/deployment/ccip/deploy.go index 5a4ad1bb394..b2d25146321 100644 --- a/deployment/ccip/deploy.go +++ b/deployment/ccip/deploy.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/multicall3" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/nonce_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" @@ -57,6 +58,7 @@ var ( PriceFeed deployment.ContractType = "PriceFeed" // Note test router maps to a regular router contract. TestRouter deployment.ContractType = "TestRouter" + Multicall3 deployment.ContractType = "Multicall3" CCIPReceiver deployment.ContractType = "CCIPReceiver" BurnMintToken deployment.ContractType = "BurnMintToken" BurnMintTokenPool deployment.ContractType = "BurnMintTokenPool" @@ -89,6 +91,7 @@ func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.Address var registryModule *registry_module_owner_custom.RegistryModuleOwnerCustom var rmnProxy *rmn_proxy_contract.RMNProxyContract var r *router.Router + var mc3 *multicall3.Multicall3 if chainExists { weth9Contract = chainState.Weth9 linkTokenContract = chainState.LinkToken @@ -96,6 +99,7 @@ func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.Address registryModule = chainState.RegistryModule rmnProxy = chainState.RMNProxyExisting r = chainState.Router + mc3 = chainState.Multicall3 } if rmnProxy == nil { // we want to replicate the mainnet scenario where RMNProxy is already deployed with some existing RMN @@ -233,7 +237,6 @@ func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.Address return err } lggr.Infow("deployed linkToken", "addr", linkToken.Address) - linkTokenContract = linkToken.Contract } else { lggr.Infow("linkToken already deployed", "addr", linkTokenContract.Address) } @@ -256,10 +259,28 @@ func DeployPrerequisiteContracts(e deployment.Environment, ab deployment.Address return err } e.Logger.Infow("deployed router", "addr", routerContract.Address) - r = routerContract.Contract } else { e.Logger.Infow("router already deployed", "addr", chainState.Router.Address) } + if mc3 == nil { + multicall3Contract, err := deployment.DeployContract(e.Logger, chain, ab, + func(chain deployment.Chain) deployment.ContractDeploy[*multicall3.Multicall3] { + multicall3Addr, tx2, multicall3Wrapper, err2 := multicall3.DeployMulticall3( + chain.DeployerKey, + chain.Client, + ) + return deployment.ContractDeploy[*multicall3.Multicall3]{ + multicall3Addr, multicall3Wrapper, tx2, deployment.NewTypeAndVersion(Multicall3, deployment.Version1_0_0), err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy ccip multicall", "err", err) + return err + } + e.Logger.Infow("deployed ccip multicall", "addr", multicall3Contract.Address) + } else { + e.Logger.Info("ccip multicall already deployed", "addr", mc3.Address) + } return nil } @@ -535,6 +556,9 @@ func DeployChainContracts( if chainState.Router == nil { return fmt.Errorf("router not found for chain %d, deploy the prerequisites first", chain.Selector) } + if chainState.Multicall3 == nil { + return fmt.Errorf("ccip multicall not found for chain %d, deploy the prerequisites first", chain.Selector) + } if chainState.Receiver == nil { ccipReceiver, err := deployment.DeployContract(e.Logger, chain, ab, func(chain deployment.Chain) deployment.ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { diff --git a/deployment/ccip/deploy_home_chain.go b/deployment/ccip/deploy_home_chain.go index d17a501783d..a1c3e6a8beb 100644 --- a/deployment/ccip/deploy_home_chain.go +++ b/deployment/ccip/deploy_home_chain.go @@ -260,27 +260,6 @@ func DeployHomeChain( return capReg, nil } -// getNodeOperatorIDMap returns a map of node operator names to their IDs -// If maxNops is greater than the number of node operators, it will return all node operators -func getNodeOperatorIDMap(capReg *capabilities_registry.CapabilitiesRegistry, maxNops uint32) (map[string]uint32, error) { - nopIdByName := make(map[string]uint32) - operators, err := capReg.GetNodeOperators(nil) - if err != nil { - return nil, err - } - if len(operators) < int(maxNops) { - maxNops = uint32(len(operators)) - } - for i := uint32(1); i <= maxNops; i++ { - operator, err := capReg.GetNodeOperator(nil, i) - if err != nil { - return nil, err - } - nopIdByName[operator.Name] = i - } - return nopIdByName, nil -} - func isEqualCapabilitiesRegistryNodeParams(a, b capabilities_registry.CapabilitiesRegistryNodeParams) (bool, error) { aBytes, err := json.Marshal(a) if err != nil { diff --git a/deployment/ccip/state.go b/deployment/ccip/state.go index cbcdcc1e116..3035214e636 100644 --- a/deployment/ccip/state.go +++ b/deployment/ccip/state.go @@ -18,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_rmn_contract" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/multicall3" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/registry_module_owner_custom" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_home" @@ -82,6 +83,7 @@ type CCIPChainState struct { // Test contracts Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver TestRouter *router.Router + Multicall3 *multicall3.Multicall3 } func (c CCIPChainState) GenerateView() (view.ChainView, error) { @@ -391,6 +393,12 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.Receiver = mr + case deployment.NewTypeAndVersion(Multicall3, deployment.Version1_0_0).String(): + mc, err := multicall3.NewMulticall3(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.Multicall3 = mc case deployment.NewTypeAndVersion(PriceFeed, deployment.Version1_0_0).String(): feed, err := aggregator_v3_interface.NewAggregatorV3Interface(common.HexToAddress(address), chain.Client) if err != nil { diff --git a/deployment/ccip/test_helpers.go b/deployment/ccip/test_helpers.go index 4c348f46920..091362ffa22 100644 --- a/deployment/ccip/test_helpers.go +++ b/deployment/ccip/test_helpers.go @@ -20,8 +20,6 @@ import ( commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" - "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -46,6 +44,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/erc20" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ) const ( @@ -56,6 +57,9 @@ const ( var ( // bytes4 public constant EVM_EXTRA_ARGS_V2_TAG = 0x181dcf10; evmExtraArgsV2Tag = hexutil.MustDecode("0x181dcf10") + + erc20ABI = abihelpers.MustParseABI(erc20.ERC20ABI) + routerABI = abihelpers.MustParseABI(router.RouterABI) ) // Context returns a context with the test's deadline, if available. @@ -275,6 +279,44 @@ func CCIPSendRequest( return tx, blockNum, nil } +// CCIPSendCalldata packs the calldata for the Router's ccipSend method. +// This is expected to be used in Multicall scenarios (i.e multiple ccipSend calls +// in a single transaction). +// Multicalls can't be made with fee token 0x0 due to the Multicall itself not +// being payable. So the appropriate fee token amount must be approved before +// doing the CCIP send (either in a separate transaction or a separate call in +// the multicall). +func CCIPSendCalldata( + t *testing.T, + destChainSelector uint64, + evm2AnyMessage router.ClientEVM2AnyMessage, +) []byte { + calldata, err := routerABI.Methods["ccipSend"].Inputs.Pack( + destChainSelector, + evm2AnyMessage, + ) + require.NoError(t, err) + + calldata = append(routerABI.Methods["ccipSend"].ID, calldata...) + return calldata +} + +// ApproveTokenCalldata packs the calldata for the ERC20's approve method. +func ApproveTokenCalldata( + t *testing.T, + spender common.Address, + amount *big.Int, +) []byte { + calldata, err := erc20ABI.Methods["approve"].Inputs.Pack( + spender, + amount, + ) + require.NoError(t, err) + + calldata = append(erc20ABI.Methods["approve"].ID, calldata...) + return calldata +} + func TestSendRequest( t *testing.T, e deployment.Environment, diff --git a/integration-tests/smoke/ccip_batching_test.go b/integration-tests/smoke/ccip_batching_test.go new file mode 100644 index 00000000000..96bc7e8f68f --- /dev/null +++ b/integration-tests/smoke/ccip_batching_test.go @@ -0,0 +1,161 @@ +package smoke + +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job" + "github.com/smartcontractkit/chainlink/deployment" + ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/multicall3" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" +) + +func Test_CCIPBatching(t *testing.T) { + // Setup 3 chains, with 2 lanes going to the dest. + lggr := logger.TestLogger(t) + ctx := ccdeploy.Context(t) + // Will load 3 chains when specified by the overrides.toml or env vars (E2E_TEST_SELECTED_NETWORK). + // See e2e-tests.yml. + e, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr) + + state, err := ccdeploy.LoadOnchainState(e.Env) + require.NoError(t, err) + + allChainSelectors := maps.Keys(e.Env.Chains) + require.Len(t, allChainSelectors, 3, "this test expects 3 chains") + sourceChain1 := allChainSelectors[0] + sourceChain2 := allChainSelectors[1] + destChain := allChainSelectors[2] + t.Log("All chain selectors:", allChainSelectors, + ", home chain selector:", e.HomeChainSel, + ", feed chain selector:", e.FeedChainSel, + ", source chain selector 1:", sourceChain1, + ", source chain selector 2:", sourceChain2, + ", dest chain selector:", destChain, + ) + output, err := changeset.DeployPrerequisites(e.Env, changeset.DeployPrerequisiteConfig{ + ChainSelectors: e.Env.AllChainSelectors(), + }) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) + + tokenConfig := ccdeploy.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds) + // Apply migration + output, err = changeset.InitialDeploy(e.Env, ccdeploy.DeployCCIPContractConfig{ + HomeChainSel: e.HomeChainSel, + FeedChainSel: e.FeedChainSel, + ChainsToDeploy: allChainSelectors, + TokenConfig: tokenConfig, + MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e.Env), + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + }) + require.NoError(t, err) + require.NoError(t, e.Env.ExistingAddresses.Merge(output.AddressBook)) + // Get new state after migration. + state, err = ccdeploy.LoadOnchainState(e.Env) + require.NoError(t, err) + + // Ensure capreg logs are up to date. + ccdeploy.ReplayLogs(t, e.Env.Offchain, e.ReplayBlocks) + + // Apply the jobs. + for nodeID, jobs := range output.JobSpecs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Env.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + require.NoError(t, err) + } + } + + // connect sourceChain1 and sourceChain2 to destChain + require.NoError(t, ccdeploy.AddLaneWithDefaultPrices(e.Env, state, sourceChain1, destChain)) + require.NoError(t, ccdeploy.AddLaneWithDefaultPrices(e.Env, state, sourceChain2, destChain)) + + // var ( + // replayed bool + // nonce map[uint64]uint64 + // senderSource1 = common.LeftPadBytes(e.Env.Chains[sourceChain1].DeployerKey.From.Bytes(), 32) + // senderSource2 = common.LeftPadBytes(e.Env.Chains[sourceChain2].DeployerKey.From.Bytes(), 32) + // outSource1 messagingTestCaseOutput + // outSource2 messagingTestCaseOutput + // setupSource1 = testCaseSetup{ + // t: t, + // sender: senderSource1, + // deployedEnv: e, + // onchainState: state, + // sourceChain: sourceChain1, + // destChain: destChain, + // } + // setupSource2 = testCaseSetup{ + // t: t, + // sender: senderSource2, + // deployedEnv: e, + // onchainState: state, + // sourceChain: sourceChain2, + // destChain: destChain, + // } + // ) + + t.Run("batch data only messages from multiple sources", func(t *testing.T) { + // Generate some messages, on each source. + // Send them from a multicall contract, i.e multiple ccip messages in the same tx. + // assert they are committed in the same batch. + msg := router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), + Data: []byte("hello world"), + TokenAmounts: nil, + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, + } + fee, err := state.Chains[sourceChain1].Router.GetFee(&bind.CallOpts{ + Context: ctx, + }, destChain, msg) + require.NoError(t, err) + + // Send the tx with the message through the multicall + calldata := ccdeploy.CCIPSendCalldata(t, destChain, msg) + tx, err := state.Chains[sourceChain1].Multicall3.Aggregate3Value( + &bind.TransactOpts{ + From: e.Env.Chains[sourceChain1].DeployerKey.From, + Signer: e.Env.Chains[sourceChain1].DeployerKey.Signer, + Value: fee, + }, + []multicall3.Multicall3Call3Value{ + { + Target: state.Chains[sourceChain1].Router.Address(), + AllowFailure: false, + CallData: calldata, + Value: fee, + }, + }, + ) + require.NoError(t, err) + _, err = e.Env.Chains[sourceChain1].Confirm(tx) + require.NoError(t, err) + + // check that the message was emitted + iter, err := state.Chains[sourceChain1].OnRamp.FilterCCIPMessageSent( + nil, []uint64{destChain}, nil, + ) + require.NoError(t, err) + require.True(t, iter.Next()) + require.Equal(t, msg.Receiver, iter.Event.Message.Receiver) + }) + + t.Run("batch mix of data only messages and token messages from multiple sources", func(t *testing.T) { + // Generate some messages, on each source. + // Send them from a multicall contract, i.e multiple ccip messages in the same tx. + // assert they are committed in the same batch. + }) +} diff --git a/integration-tests/testconfig/ccip/ccip.toml b/integration-tests/testconfig/ccip/ccip.toml index 5ccda6ab4e3..85e645ed0b9 100644 --- a/integration-tests/testconfig/ccip/ccip.toml +++ b/integration-tests/testconfig/ccip/ccip.toml @@ -39,6 +39,23 @@ evm_supports_eip1559 = true evm_default_gas_limit = 6000000 evm_finality_depth = 1 +[Network.EVMNetworks.SIMULATED_3] +evm_name = 'chain-3337' +evm_chain_id = 3337 +evm_keys = [ + "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", +] +evm_simulated = true +client_implementation = 'Ethereum' +evm_chainlink_transaction_limit = 50000 +evm_transaction_timeout = '2m' +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 1000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_finality_depth = 1 + [NodeConfig] BaseConfigTOML = """ [Feature] @@ -151,6 +168,21 @@ addresses_to_fund = [ "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", ] +[CCIP.PrivateEthereumNetworks.SIMULATED_3] +ethereum_version = "eth1" +execution_layer = "geth" + +[CCIP.PrivateEthereumNetworks.SIMULATED_3.EthereumChainConfig] +seconds_per_slot = 3 +slots_per_epoch = 2 +genesis_delay = 15 +validator_count = 4 +chain_id = 3337 +addresses_to_fund = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", +] + [Seth] # Seth specific configuration, no need for generating ephemeral addresses for ccip-tests. -ephemeral_addresses_number = 0 \ No newline at end of file +ephemeral_addresses_number = 0