From b47918991ff2aa6d52a98ee4b9a501701a897b7d Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 00:41:26 +0700 Subject: [PATCH 01/17] chore: simplify read examples --- .../oapp-read/contracts/ExampleContract.sol | 15 ++ examples/oapp-read/contracts/MyOAppRead.sol | 173 ----------------- examples/oapp-read/contracts/ReadPublic.sol | 174 ++++++++++++++++++ .../oapp-read/test/foundry/MyOAppRead.t.sol | 94 ---------- .../oapp-read/test/foundry/ReadPublic.t.sol | 113 ++++++++++++ 5 files changed, 302 insertions(+), 267 deletions(-) create mode 100644 examples/oapp-read/contracts/ExampleContract.sol delete mode 100644 examples/oapp-read/contracts/MyOAppRead.sol create mode 100644 examples/oapp-read/contracts/ReadPublic.sol delete mode 100644 examples/oapp-read/test/foundry/MyOAppRead.t.sol create mode 100644 examples/oapp-read/test/foundry/ReadPublic.t.sol diff --git a/examples/oapp-read/contracts/ExampleContract.sol b/examples/oapp-read/contracts/ExampleContract.sol new file mode 100644 index 000000000..b0df9eb29 --- /dev/null +++ b/examples/oapp-read/contracts/ExampleContract.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title ExampleContract + * @notice A simple contract with a public state variable. + */ +contract ExampleContract { + // a public data variable on the target data chain to read from + uint256 public data; + + constructor(uint256 _data) { + data = _data; + } +} \ No newline at end of file diff --git a/examples/oapp-read/contracts/MyOAppRead.sol b/examples/oapp-read/contracts/MyOAppRead.sol deleted file mode 100644 index 2f61e1e3e..000000000 --- a/examples/oapp-read/contracts/MyOAppRead.sol +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.22; - -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { MessagingFee, Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; -import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; -import { MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; -import { IOAppMapper } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMapper.sol"; -import { IOAppReducer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReducer.sol"; -import { ReadCodecV1, EVMCallComputeV1, EVMCallRequestV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; - -contract MyOAppRead is OAppRead, IOAppMapper, IOAppReducer { - struct EvmReadRequest { - uint16 appRequestLabel; - uint32 targetEid; - bool isBlockNum; - uint64 blockNumOrTimestamp; - uint16 confirmations; - address to; - } - - struct EvmComputeRequest { - uint8 computeSetting; - uint32 targetEid; - bool isBlockNum; - uint64 blockNumOrTimestamp; - uint16 confirmations; - address to; - } - - uint8 internal constant COMPUTE_SETTING_MAP_ONLY = 0; - uint8 internal constant COMPUTE_SETTING_REDUCE_ONLY = 1; - uint8 internal constant COMPUTE_SETTING_MAP_REDUCE = 2; - uint8 internal constant COMPUTE_SETTING_NONE = 3; - - constructor( - address _endpoint, - address _delegate, - string memory _identifier - ) OAppRead(_endpoint, _delegate) Ownable(_delegate) { - identifier = _identifier; - } - - string public identifier; - bytes public data = abi.encode("Nothing received yet."); - - /** - * @notice Send a read command in loopback through channelId - * @param _channelId Read Channel ID to be used for the message. - * @param _appLabel The application label to use for the message. - * @param _requests An array of `EvmReadRequest` structs containing the read requests to be made. - * @param _computeRequest A `EvmComputeRequest` struct containing the compute request to be made. - * @param _options Message execution options (e.g., for sending gas to destination). - * @dev Encodes the message as bytes and sends it using the `_lzSend` internal function. - * @return receipt A `MessagingReceipt` struct containing details of the message sent. - */ - function send( - uint32 _channelId, - uint16 _appLabel, - EvmReadRequest[] memory _requests, - EvmComputeRequest memory _computeRequest, - bytes calldata _options - ) external payable returns (MessagingReceipt memory receipt) { - bytes memory cmd = buildCmd(_appLabel, _requests, _computeRequest); - receipt = _lzSend(_channelId, cmd, _options, MessagingFee(msg.value, 0), payable(msg.sender)); - } - - /** - * @notice Quotes the gas needed to pay for the full read command in native gas or ZRO token. - * @param _channelId Read Channel ID to be used for the message. - * @param _appLabel The application label to use for the message. - * @param _requests An array of `EvmReadRequest` structs containing the read requests to be made. - * @param _computeRequest A `EvmComputeRequest` struct containing the compute request to be made. - * @param _options Message execution options (e.g., for sending gas to destination). - * @param _payInLzToken Whether to return fee in ZRO token. - * @return fee A `MessagingFee` struct containing the calculated gas fee in either the native token or ZRO token. - */ - function quote( - uint32 _channelId, - uint16 _appLabel, - EvmReadRequest[] memory _requests, - EvmComputeRequest memory _computeRequest, - bytes calldata _options, - bool _payInLzToken - ) public view returns (MessagingFee memory fee) { - bytes memory cmd = buildCmd(_appLabel, _requests, _computeRequest); - fee = _quote(_channelId, cmd, _options, _payInLzToken); - } - - /** - * @notice Builds the command to be sent - * @param appLabel The application label to use for the message. - * @param _readRequests An array of `EvmReadRequest` structs containing the read requests to be made. - * @param _computeRequest A `EvmComputeRequest` struct containing the compute request to be made. - * @return cmd The encoded command to be sent to to the channel. - */ - function buildCmd( - uint16 appLabel, - EvmReadRequest[] memory _readRequests, - EvmComputeRequest memory _computeRequest - ) public pure returns (bytes memory) { - require(_readRequests.length > 0, "LzReadCounter: empty requests"); - // build read requests - EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](_readRequests.length); - for (uint256 i = 0; i < _readRequests.length; i++) { - EvmReadRequest memory req = _readRequests[i]; - readRequests[i] = EVMCallRequestV1({ - appRequestLabel: req.appRequestLabel, - targetEid: req.targetEid, - isBlockNum: req.isBlockNum, - blockNumOrTimestamp: req.blockNumOrTimestamp, - confirmations: req.confirmations, - to: req.to, - callData: abi.encodeWithSelector(this.myInformation.selector) - }); - } - - require(_computeRequest.computeSetting <= COMPUTE_SETTING_NONE, "LzReadCounter: invalid compute type"); - EVMCallComputeV1 memory evmCompute = EVMCallComputeV1({ - computeSetting: _computeRequest.computeSetting, - targetEid: _computeRequest.computeSetting == COMPUTE_SETTING_NONE ? 0 : _computeRequest.targetEid, - isBlockNum: _computeRequest.isBlockNum, - blockNumOrTimestamp: _computeRequest.blockNumOrTimestamp, - confirmations: _computeRequest.confirmations, - to: _computeRequest.to - }); - bytes memory cmd = ReadCodecV1.encode(appLabel, readRequests, evmCompute); - - return cmd; - } - - /** - * @dev Internal function override to handle incoming messages from another chain. - * @param payload The encoded message payload being received. This is the resolved command from the DVN - * - * @dev The following params are unused in the current implementation of the OApp. - * @dev _origin A struct containing information about the message sender. - * @dev _guid A unique global packet identifier for the message. - * @dev _executor The address of the Executor responsible for processing the message. - * @dev _extraData Arbitrary data appended by the Executor to the message. - * - * Decodes the received payload and processes it as per the business logic defined in the function. - */ - function _lzReceive( - Origin calldata /*_origin*/, - bytes32 /*_guid*/, - bytes calldata payload, - address /*_executor*/, - bytes calldata /*_extraData*/ - ) internal override { - data = payload; - } - - function myInformation() public view returns (bytes memory) { - return abi.encodePacked("_id:", identifier, "_blockNumber:", block.number); - } - - function lzMap(bytes calldata _request, bytes calldata _response) external pure returns (bytes memory) { - uint16 requestLabel = ReadCodecV1.decodeRequestV1AppRequestLabel(_request); - return abi.encodePacked(_response, "_mapped_requestLabel:", requestLabel); - } - - function lzReduce(bytes calldata _cmd, bytes[] calldata _responses) external pure returns (bytes memory) { - uint16 appLabel = ReadCodecV1.decodeCmdAppLabel(_cmd); - bytes memory concatenatedResponses; - - for (uint256 i = 0; i < _responses.length; i++) { - concatenatedResponses = abi.encodePacked(concatenatedResponses, _responses[i]); - } - return abi.encodePacked(concatenatedResponses, "_reduced_appLabel:", appLabel); - } -} diff --git a/examples/oapp-read/contracts/ReadPublic.sol b/examples/oapp-read/contracts/ReadPublic.sol new file mode 100644 index 000000000..397b0e420 --- /dev/null +++ b/examples/oapp-read/contracts/ReadPublic.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Import necessary interfaces and contracts +import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; +import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { ReadCodecV1, EVMCallRequestV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; +import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/// @title IExampleContract +/// @notice Interface for the ExampleContract's `data()` function. +interface IExampleContract { + function data() external view returns (uint256); +} + +/// @title ReadPublic +/// @notice An OAppRead contract example to read a public state variable from another chain. +contract ReadPublic is OAppRead, OAppOptionsType3 { + /// @notice Emitted when the data is received. + /// @param data The value of the public state variable. + event DataReceived(uint256 data); + + /// @notice LayerZero read channel ID. + uint32 public READ_CHANNEL; + + /// @notice Message type for the read operation. + uint16 public constant READ_TYPE = 1; + + /** + * @notice Constructor to initialize the OAppRead contract. + * + * @param _endpoint The LayerZero endpoint contract address. + * @param _delegate The address that will have ownership privileges. + * @param _readChannel The LayerZero read channel ID. + */ + constructor( + address _endpoint, + address _delegate, + uint32 _readChannel + ) OAppRead(_endpoint, _delegate) Ownable(_delegate) { + READ_CHANNEL = _readChannel; + _setPeer(_readChannel, AddressCast.toBytes32(address(this))); + } + + /** + * @notice Sets the LayerZero read channel. + * + * @dev Only callable by the owner. + * + * @param _channelId The channel ID to set. + * @param _active Flag to activate or deactivate the channel. + */ + function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { + _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); + READ_CHANNEL = _channelId; + } + + /** + * @notice Sends a read request to fetch the public state variable `data`. + * + * @dev The caller must send enough ETH to cover the messaging fee. + * + * @param _targetContractAddress The address of the contract on the target chain containing the `data` variable. + * @param _targetEid The target chain's Endpoint ID. + * @param _extraOptions Additional messaging options. + * + * @return receipt The LayerZero messaging receipt for the request. + */ + function readData( + address _targetContractAddress, + uint32 _targetEid, + bytes calldata _extraOptions + ) external payable returns (MessagingReceipt memory receipt) { + bytes memory cmd = _getCmd(_targetContractAddress, _targetEid); + return + _lzSend( + READ_CHANNEL, + cmd, + _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), + MessagingFee(msg.value, 0), + payable(msg.sender) + ); + } + + /** + * @notice Estimates the messaging fee required to perform the read operation. + * + * @param _targetContractAddress The address of the contract on the target chain containing the `data` variable. + * @param _targetEid The target chain's Endpoint ID. + * @param _extraOptions Additional messaging options. + * + * @return fee The estimated messaging fee. + */ + function quoteReadFee( + address _targetContractAddress, + uint32 _targetEid, + bytes calldata _extraOptions + ) external view returns (MessagingFee memory fee) { + return + _quote( + READ_CHANNEL, + _getCmd(_targetContractAddress, _targetEid), + _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), + false + ); + } + + /** + * @notice Constructs the read command to fetch the `data` variable. + * + * @param targetContractAddress The address of the contract containing the `data` variable. + * @param targetEid The target chain's Endpoint ID. + * + * @return cmd The encoded command. + */ + function _getCmd(address targetContractAddress, uint32 targetEid) internal view returns (bytes memory cmd) { + // Use the function selector from the interface to ensure correctness + bytes memory callData = abi.encodeWithSelector(IExampleContract.data.selector); + + // Create an array of EVMCallRequestV1 with a single read request + EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); + readRequests[0] = EVMCallRequestV1({ + appRequestLabel: 1, // Application-specific label for tracking + targetEid: targetEid, // Endpoint ID of the target chain + isBlockNum: false, // Use timestamp instead of block number + blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at + confirmations: 15, // Number of confirmations to wait for finality + to: targetContractAddress, // Address of the contract to read from + callData: callData // Encoded function call data + }); + + // No compute logic needed; encode the command with appLabel 0 + cmd = ReadCodecV1.encode(0, readRequests); + } + + /** + * @notice Handles the received data from the target chain. + * + * @dev This function is called internally by the LayerZero protocol. + * + * @param _message The encoded data received. + */ + function _lzReceive( + Origin calldata, /*_origin*/ + bytes32, /*_guid*/ + bytes calldata _message, + address, /*_executor*/ + bytes calldata /*_extraData*/ + ) internal override { + // Decode the data received from bytes to uint256 + uint256 data = abi.decode(_message, (uint256)); + emit DataReceived(data); + } + + /** + * @notice Combines the options for messaging. + * + * @param _channelId The channel ID. + * @param _msgType The message type. + * @param _extraOptions Additional options. + * + * @return options The combined options. + */ + function _combineOptions( + uint32 _channelId, + uint16 _msgType, + bytes calldata _extraOptions + ) internal view returns (bytes memory options) { + options = combineOptions(_channelId, _msgType, _extraOptions); + } +} diff --git a/examples/oapp-read/test/foundry/MyOAppRead.t.sol b/examples/oapp-read/test/foundry/MyOAppRead.t.sol deleted file mode 100644 index d5208f396..000000000 --- a/examples/oapp-read/test/foundry/MyOAppRead.t.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -// MyOApp imports -import { MyOAppRead } from "../../contracts/MyOAppRead.sol"; - -// OApp imports -import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; -import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; -import { MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; - -// OZ imports -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; - -// Forge imports -import "forge-std/console.sol"; - -// DevTools imports -import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; - -contract MyOAppReadTest is TestHelperOz5 { - using OptionsBuilder for bytes; - - uint32 private aEid = 1; - uint32 private bEid = 2; - - MyOAppRead private aOApp; - MyOAppRead private bOApp; - - address private userA = address(0x1); - - function setUp() public virtual override { - vm.deal(userA, 1000 ether); - - super.setUp(); - setUpEndpoints(2, LibraryType.UltraLightNode); - - aOApp = MyOAppRead( - _deployOApp(type(MyOAppRead).creationCode, abi.encode(address(endpoints[aEid]), address(this), "OAppA")) - ); - - bOApp = MyOAppRead( - _deployOApp(type(MyOAppRead).creationCode, abi.encode(address(endpoints[bEid]), address(this), "OAppB")) - ); - - address[] memory oapps = new address[](2); - oapps[0] = address(aOApp); - oapps[1] = address(bOApp); - uint32[] memory channels = new uint32[](1); - channels[0] = DEFAULT_CHANNEL_ID; - this.wireReadOApps(oapps, channels); - } - - function test_constructor() public { - assertEq(aOApp.owner(), address(this)); - assertEq(bOApp.owner(), address(this)); - - assertEq(address(aOApp.endpoint()), address(endpoints[aEid])); - assertEq(address(bOApp.endpoint()), address(endpoints[bEid])); - } - - function test_send_read() public { - bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(1e8, 100, 0); - - MyOAppRead.EvmReadRequest[] memory readRequests = new MyOAppRead.EvmReadRequest[](1); - readRequests[0] = MyOAppRead.EvmReadRequest({ - appRequestLabel: 1, - targetEid: aEid, - isBlockNum: true, - blockNumOrTimestamp: uint64(block.number), - confirmations: 1, - to: address(aOApp) - }); - MyOAppRead.EvmComputeRequest memory computeRequest = MyOAppRead.EvmComputeRequest({ - computeSetting: 3, // MapReduce - targetEid: aEid, - isBlockNum: true, - blockNumOrTimestamp: uint64(block.number), - confirmations: 1, - to: address(aOApp) - }); - - MessagingFee memory fee = aOApp.quote(DEFAULT_CHANNEL_ID, 1, readRequests, computeRequest, options, false); - - assertEq(aOApp.data(), abi.encode("Nothing received yet.")); - assertEq(bOApp.data(), abi.encode("Nothing received yet.")); - - vm.prank(userA); - aOApp.send{ value: fee.nativeFee }(DEFAULT_CHANNEL_ID, 1, readRequests, computeRequest, options); - verifyPackets(aEid, addressToBytes32(address(aOApp)), 0, address(0x0), abi.encode("Test")); - - assertEq(aOApp.data(), abi.encode("Test")); - } -} diff --git a/examples/oapp-read/test/foundry/ReadPublic.t.sol b/examples/oapp-read/test/foundry/ReadPublic.t.sol new file mode 100644 index 000000000..15547c708 --- /dev/null +++ b/examples/oapp-read/test/foundry/ReadPublic.t.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +// MyOApp imports + +import { ReadPublic } from "../../contracts/ReadPublic.sol"; +import { ExampleContract } from "../../contracts/ExampleContract.sol"; + +// OApp imports +import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; + +// OZ imports +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +// Forge imports +import "forge-std/console.sol"; +import "forge-std/Test.sol"; + +// DevTools imports +import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; + +contract ReadPublicTest is TestHelperOz5 { + using OptionsBuilder for bytes; + + uint32 private aEid = 1; // Chain A Endpoint ID + uint32 private bEid = 2; // Chain B Endpoint ID + + ReadPublic private bOApp; // Deployed on chain B + ExampleContract private exampleContract; // Deployed on chain A + + address private userA = address(0x1); + + uint16 private constant READ_TYPE = 1; + + function setUp() public virtual override { + vm.deal(userA, 1000 ether); + + super.setUp(); + setUpEndpoints(2, LibraryType.UltraLightNode); + + // Deploy ExampleContract on chain A (aEid) + // We simulate chain A by associating contracts with aEid + exampleContract = ExampleContract( + _deployOApp( + type(ExampleContract).creationCode, + abi.encode(uint256(42)) // Initialize data to 42 + ) + ); + + // Deploy ReadPublic on chain B (bEid) + bOApp = ReadPublic( + _deployOApp( + type(ReadPublic).creationCode, + abi.encode(address(endpoints[bEid]), address(this), DEFAULT_CHANNEL_ID) + ) + ); + + // Wire the OApps + address[] memory oapps = new address[](1); + oapps[0] = address(bOApp); + uint32[] memory channels = new uint32[](1); + channels[0] = DEFAULT_CHANNEL_ID; + this.wireReadOApps(oapps, channels); + } + + function test_constructor() public { + assertEq(bOApp.owner(), address(this)); + assertEq(address(bOApp.endpoint()), address(endpoints[bEid])); + } + + function test_send_read() public { + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(1e8, 100, 0); + console.logBytes(options); + + // Prepare the read request parameters + uint32 targetEid = aEid; + address targetContractAddress = address(exampleContract); + + // Estimate the fee (you might need to adjust this based on your setup) + MessagingFee memory fee = bOApp.quoteReadFee(targetContractAddress, targetEid, options); + + // Record logs to capture the DataReceived event + vm.recordLogs(); + + // User A initiates the read request on bOApp + vm.prank(userA); + MessagingReceipt memory receipt = bOApp.readData{ value: fee.nativeFee }( + targetContractAddress, + targetEid, + options + ); + + // Process the response packet to bOApp on bEid, injecting the data 42 + this.verifyPackets(bEid, addressToBytes32(address(bOApp)), 0, address(0x0), abi.encode(uint256(42))); + + // Retrieve the logs to verify the DataReceived event + Vm.Log[] memory entries = vm.getRecordedLogs(); + bool found = false; + uint256 dataReceived; + for (uint256 i = 0; i < entries.length; i++) { + Vm.Log memory entry = entries[i]; + if (entry.topics[0] == keccak256("DataReceived(uint256)")) { + dataReceived = abi.decode(entry.data, (uint256)); + found = true; + break; + } + } + require(found, "DataReceived event not found"); + assertEq(dataReceived, 42, "Data received does not match expected value"); + } +} From a937c6a74652c4fd8145220073bf543edc5be220 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 00:51:36 +0700 Subject: [PATCH 02/17] chore: update layerzero.config.ts --- examples/oapp-read/layerzero.config.ts | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/oapp-read/layerzero.config.ts b/examples/oapp-read/layerzero.config.ts index 8ee752984..d44448592 100644 --- a/examples/oapp-read/layerzero.config.ts +++ b/examples/oapp-read/layerzero.config.ts @@ -1,31 +1,31 @@ -import { EndpointId } from '@layerzerolabs/lz-definitions' +import { ChannelId, EndpointId } from '@layerzerolabs/lz-definitions' import type { OAppReadOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat' -const sepoliaContract: OmniPointHardhat = { - eid: EndpointId.SEPOLIA_V2_TESTNET, - contractName: 'MyOAppRead', -} -const fujiContract: OmniPointHardhat = { - eid: EndpointId.AVALANCHE_V2_TESTNET, - contractName: 'MyOAppRead', -} - -const amoyContract: OmniPointHardhat = { - eid: EndpointId.AMOY_V2_TESTNET, - contractName: 'MyOAppRead', +const arbitrumContract: OmniPointHardhat = { + eid: EndpointId.SEPOLIA_V2_TESTNET, + contractName: 'ReadPublic', } const config: OAppReadOmniGraphHardhat = { contracts: [ { - contract: fujiContract, - }, - { - contract: sepoliaContract, - }, - { - contract: amoyContract, + contract: arbitrumContract, + config: { + readLibrary: '0xbcd4CADCac3F767C57c4F402932C4705DF62BEFf', + readChannels: [ + { + channelId: ChannelId.READ_CHANNEL_1, + active: true, + }, + ], + readConfig: { + ulnConfig: { + requiredDVNs: ['0x1308151a7ebac14f435d3ad5ff95c34160d539a5'], + executor: '0x31CAe3B7fB82d847621859fb1585353c5720660D', + }, + }, + }, }, ], connections: [], From ea57c4446a794e324b7af227122586b50e4f7f17 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 00:53:06 +0700 Subject: [PATCH 03/17] chore: run linter --- examples/oapp-read/contracts/ExampleContract.sol | 2 +- examples/oapp-read/contracts/ReadPublic.sol | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/oapp-read/contracts/ExampleContract.sol b/examples/oapp-read/contracts/ExampleContract.sol index b0df9eb29..2b2b8fe06 100644 --- a/examples/oapp-read/contracts/ExampleContract.sol +++ b/examples/oapp-read/contracts/ExampleContract.sol @@ -12,4 +12,4 @@ contract ExampleContract { constructor(uint256 _data) { data = _data; } -} \ No newline at end of file +} diff --git a/examples/oapp-read/contracts/ReadPublic.sol b/examples/oapp-read/contracts/ReadPublic.sol index 397b0e420..75d93f0d4 100644 --- a/examples/oapp-read/contracts/ReadPublic.sol +++ b/examples/oapp-read/contracts/ReadPublic.sol @@ -144,10 +144,10 @@ contract ReadPublic is OAppRead, OAppOptionsType3 { * @param _message The encoded data received. */ function _lzReceive( - Origin calldata, /*_origin*/ - bytes32, /*_guid*/ + Origin calldata /*_origin*/, + bytes32 /*_guid*/, bytes calldata _message, - address, /*_executor*/ + address /*_executor*/, bytes calldata /*_extraData*/ ) internal override { // Decode the data received from bytes to uint256 From 834129902580037540e2c29c9b58184e7e5355b0 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 01:04:54 +0700 Subject: [PATCH 04/17] chore: remove hardhat unit test --- examples/oapp-read/test/foundry/ReadPublic.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/oapp-read/test/foundry/ReadPublic.t.sol b/examples/oapp-read/test/foundry/ReadPublic.t.sol index 15547c708..c10fb7d57 100644 --- a/examples/oapp-read/test/foundry/ReadPublic.t.sol +++ b/examples/oapp-read/test/foundry/ReadPublic.t.sol @@ -86,7 +86,7 @@ contract ReadPublicTest is TestHelperOz5 { // User A initiates the read request on bOApp vm.prank(userA); - MessagingReceipt memory receipt = bOApp.readData{ value: fee.nativeFee }( + bOApp.readData{ value: fee.nativeFee }( targetContractAddress, targetEid, options From ba7f8925bd5b69f892d0620690ba6435a3fbdd6c Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 08:46:30 +0700 Subject: [PATCH 05/17] chore: linter --- examples/oapp-read/test/foundry/ReadPublic.t.sol | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/oapp-read/test/foundry/ReadPublic.t.sol b/examples/oapp-read/test/foundry/ReadPublic.t.sol index c10fb7d57..bd0148e25 100644 --- a/examples/oapp-read/test/foundry/ReadPublic.t.sol +++ b/examples/oapp-read/test/foundry/ReadPublic.t.sol @@ -86,11 +86,7 @@ contract ReadPublicTest is TestHelperOz5 { // User A initiates the read request on bOApp vm.prank(userA); - bOApp.readData{ value: fee.nativeFee }( - targetContractAddress, - targetEid, - options - ); + bOApp.readData{ value: fee.nativeFee }(targetContractAddress, targetEid, options); // Process the response packet to bOApp on bEid, injecting the data 42 this.verifyPackets(bEid, addressToBytes32(address(bOApp)), 0, address(0x0), abi.encode(uint256(42))); From 07f1394efe50a4c6abdc1e9660677cd499d4ec0e Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 08:53:02 +0700 Subject: [PATCH 06/17] chore: update layerzero.config.ts --- examples/oapp-read/layerzero.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/oapp-read/layerzero.config.ts b/examples/oapp-read/layerzero.config.ts index d44448592..575c4261d 100644 --- a/examples/oapp-read/layerzero.config.ts +++ b/examples/oapp-read/layerzero.config.ts @@ -2,7 +2,7 @@ import { ChannelId, EndpointId } from '@layerzerolabs/lz-definitions' import type { OAppReadOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat' -const arbitrumContract: OmniPointHardhat = { +const sepoliaContract: OmniPointHardhat = { eid: EndpointId.SEPOLIA_V2_TESTNET, contractName: 'ReadPublic', } @@ -10,7 +10,7 @@ const arbitrumContract: OmniPointHardhat = { const config: OAppReadOmniGraphHardhat = { contracts: [ { - contract: arbitrumContract, + contract: sepoliaContract, config: { readLibrary: '0xbcd4CADCac3F767C57c4F402932C4705DF62BEFf', readChannels: [ From c2b3afb9de3c71ded1fdb0925d18ed1c05827157 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 09:34:47 +0700 Subject: [PATCH 07/17] chore: add view-pure-example --- examples/view-pure-read/.env.example | 15 + examples/view-pure-read/.eslintignore | 10 + examples/view-pure-read/.eslintrc.js | 10 + examples/view-pure-read/.gitignore | 23 + examples/view-pure-read/.nvmrc | 1 + examples/view-pure-read/.prettierignore | 10 + examples/view-pure-read/.prettierrc.js | 3 + examples/view-pure-read/CHANGELOG.md | 7 + examples/view-pure-read/README.md | 630 ++++++++++++++++++ .../contracts/ExampleContract.sol | 18 + .../contracts/ReadNonViewReturn.sol | 160 +++++ .../view-pure-read/contracts/ReadPublic.sol | 174 +++++ .../contracts/ReadViewOrPure.sol | 180 +++++ .../view-pure-read/deploy/ReadViewOrPure.ts | 56 ++ examples/view-pure-read/foundry.toml | 27 + examples/view-pure-read/hardhat.config.ts | 82 +++ examples/view-pure-read/layerzero.config.ts | 34 + examples/view-pure-read/package.json | 73 ++ examples/view-pure-read/solhint.config.js | 1 + .../test/foundry/ReadViewOrPure.t.sol | 157 +++++ examples/view-pure-read/tsconfig.json | 14 + .../ExecutorOptions.sol/ExecutorOptions.json | 36 +- 22 files changed, 1706 insertions(+), 15 deletions(-) create mode 100644 examples/view-pure-read/.env.example create mode 100644 examples/view-pure-read/.eslintignore create mode 100644 examples/view-pure-read/.eslintrc.js create mode 100644 examples/view-pure-read/.gitignore create mode 100644 examples/view-pure-read/.nvmrc create mode 100644 examples/view-pure-read/.prettierignore create mode 100644 examples/view-pure-read/.prettierrc.js create mode 100644 examples/view-pure-read/CHANGELOG.md create mode 100644 examples/view-pure-read/README.md create mode 100644 examples/view-pure-read/contracts/ExampleContract.sol create mode 100644 examples/view-pure-read/contracts/ReadNonViewReturn.sol create mode 100644 examples/view-pure-read/contracts/ReadPublic.sol create mode 100644 examples/view-pure-read/contracts/ReadViewOrPure.sol create mode 100644 examples/view-pure-read/deploy/ReadViewOrPure.ts create mode 100644 examples/view-pure-read/foundry.toml create mode 100644 examples/view-pure-read/hardhat.config.ts create mode 100644 examples/view-pure-read/layerzero.config.ts create mode 100644 examples/view-pure-read/package.json create mode 100644 examples/view-pure-read/solhint.config.js create mode 100644 examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol create mode 100644 examples/view-pure-read/tsconfig.json diff --git a/examples/view-pure-read/.env.example b/examples/view-pure-read/.env.example new file mode 100644 index 000000000..197ba1d67 --- /dev/null +++ b/examples/view-pure-read/.env.example @@ -0,0 +1,15 @@ +# .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.- +# / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ +# `-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' +# +# Example environment configuration +# +# .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.-. .-.- +# / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ / / \ +# `-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' `-`-' + +# By default, the examples support both mnemonic-based and private key-based authentication +# +# You don't need to set both of these values, just pick the one that you prefer and set that one +MNEMONIC= +PRIVATE_KEY= \ No newline at end of file diff --git a/examples/view-pure-read/.eslintignore b/examples/view-pure-read/.eslintignore new file mode 100644 index 000000000..ee9f768fd --- /dev/null +++ b/examples/view-pure-read/.eslintignore @@ -0,0 +1,10 @@ +artifacts +cache +dist +node_modules +out +*.log +*.sol +*.yaml +*.lock +package-lock.json \ No newline at end of file diff --git a/examples/view-pure-read/.eslintrc.js b/examples/view-pure-read/.eslintrc.js new file mode 100644 index 000000000..f0ea891fd --- /dev/null +++ b/examples/view-pure-read/.eslintrc.js @@ -0,0 +1,10 @@ +require('@rushstack/eslint-patch/modern-module-resolution'); + +module.exports = { + extends: ['@layerzerolabs/eslint-config-next/recommended'], + rules: { + // @layerzerolabs/eslint-config-next defines rules for turborepo-based projects + // that are not relevant for this particular project + 'turbo/no-undeclared-env-vars': 'off', + }, +}; diff --git a/examples/view-pure-read/.gitignore b/examples/view-pure-read/.gitignore new file mode 100644 index 000000000..09d70f7bf --- /dev/null +++ b/examples/view-pure-read/.gitignore @@ -0,0 +1,23 @@ +node_modules +.env +coverage +coverage.json +typechain +typechain-types + +# Hardhat files +cache +artifacts + +# LayerZero specific files +.layerzero + +# foundry test compilation files +out + +# pnpm +pnpm-error.log + +# Editor and OS files +.DS_Store +.idea diff --git a/examples/view-pure-read/.nvmrc b/examples/view-pure-read/.nvmrc new file mode 100644 index 000000000..b714151ef --- /dev/null +++ b/examples/view-pure-read/.nvmrc @@ -0,0 +1 @@ +v18.18.0 \ No newline at end of file diff --git a/examples/view-pure-read/.prettierignore b/examples/view-pure-read/.prettierignore new file mode 100644 index 000000000..6e8232f5a --- /dev/null +++ b/examples/view-pure-read/.prettierignore @@ -0,0 +1,10 @@ +artifacts/ +cache/ +dist/ +node_modules/ +out/ +*.log +*ignore +*.yaml +*.lock +package-lock.json \ No newline at end of file diff --git a/examples/view-pure-read/.prettierrc.js b/examples/view-pure-read/.prettierrc.js new file mode 100644 index 000000000..6f55b4019 --- /dev/null +++ b/examples/view-pure-read/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('@layerzerolabs/prettier-config-next'), +}; diff --git a/examples/view-pure-read/CHANGELOG.md b/examples/view-pure-read/CHANGELOG.md new file mode 100644 index 000000000..0f2d98474 --- /dev/null +++ b/examples/view-pure-read/CHANGELOG.md @@ -0,0 +1,7 @@ +# @layerzerolabs/oapp-read-example + +## 0.2.0 + +### Minor Changes + +- e2395b5: Add OApp Read Example diff --git a/examples/view-pure-read/README.md b/examples/view-pure-read/README.md new file mode 100644 index 000000000..f79f4755b --- /dev/null +++ b/examples/view-pure-read/README.md @@ -0,0 +1,630 @@ +

+ + LayerZero + +

+ +

+ Homepage | Docs | Developers +

+ +

OApp Read Example

+ +

+ Quickstart | Configuration | Message Execution Options | Endpoint, MessageLib, & Executor Addresses | DVN Addresses +

+ +

Template project for getting started with LayerZero's OAppRead contract standard.

+ +## LayerZero Hardhat Helper Tasks + +LayerZero Devtools provides several helper hardhat tasks to easily deploy, verify, configure, connect, and send OFTs cross-chain. + +
+ npx hardhat lz:deploy + +
+ +Deploys your contract to any of the available networks in your [`hardhat.config.ts`](./hardhat.config.ts) when given a deploy tag (by default contract name) and returns a list of available networks to select for the deployment. For specifics around all deployment options, please refer to the [Deploying Contracts](https://docs.layerzero.network/v2/developers/evm/create-lz-oapp/deploying) section of the documentation. LayerZero's `lz:deploy` utilizes `hardhat-deploy`. + +```yml +'arbitrum-sepolia': { + eid: EndpointId.ARBSEP_V2_TESTNET, + url: process.env.RPC_URL_ARBSEP_TESTNET, + accounts, +}, +'base-sepolia': { + eid: EndpointId.BASESEP_V2_TESTNET, + url: process.env.RPC_URL_BASE_TESTNET, + accounts, +}, +``` + +
+ +
+ npx hardhat lz:oapp-read:config:init --oapp-config YOUR_OAPP_CONFIG --contract-name CONTRACT_NAME + +
+ +Initializes a `layerzero.config.ts` file for all available pathways between your hardhat networks with the current LayerZero default placeholder settings. This task can be incredibly useful for correctly formatting your config file. + +It also generates setting for using lzRead in each individual network + +You can run this task by providing the `contract-name` you want to set for the config and `file-name` you want to generate: + +```bash +npx hardhat lz:oapp-read:config:init --contract-name CONTRACT_NAME --oapp-config FILE_NAME +``` + +This will create a `layerzero.config.ts` in your working directory populated with your contract name and connections for every pathway possible between your hardhat networks and lzRead configuration for all networks: + +```yml +import { EndpointId } from '@layerzerolabs/lz-definitions' +const arbsep_testnetContract = { + eid: EndpointId.ARBSEP_V2_TESTNET, + contractName: 'MyOAppRead', +} +const sepolia_testnetContract = { + eid: EndpointId.SEPOLIA_V2_TESTNET, + contractName: 'MyOAppRead', +} +export default { + contracts: [ + { + contract: arbsep_testnetContract, + config: { + readChannelConfigs: [ + { + channelId: 4294967295, + readLibrary: '0x54320b901FDe49Ba98de821Ccf374BA4358a8bf6', + ulnConfig: { + executor: '0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897', + requiredDVNs: ['0xcb998B0CeC8b45B268336b99811533728880F08a'], + optionalDVNs: [], + optionalDVNThreshold: 0, + }, + }, + ], + }, + }, + { + contract: sepolia_testnetContract, + config: { + readChannelConfigs: [ + { + channelId: 4294967295, + readLibrary: '0x908E86e9cb3F16CC94AE7569Bf64Ce2CE04bbcBE', + ulnConfig: { + executor: '0x718B92b5CB0a5552039B593faF724D182A881eDA', + requiredDVNs: ['0xDd0Dd2155e17E5363346cE2Bcb80A3990DD1F97F'], + optionalDVNs: [], + optionalDVNThreshold: 0, + }, + }, + ], + }, + }, + ], + connections: [ + { + from: arbsep_testnetContract, + to: sepolia_testnetContract, + config: { + sendLibrary: '0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E', + receiveLibraryConfig: { receiveLibrary: '0x75Db67CDab2824970131D5aa9CECfC9F69c69636', gracePeriod: 0 }, + sendConfig: { + executorConfig: { maxMessageSize: 10000, executor: '0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897' }, + ulnConfig: { + confirmations: 1, + requiredDVNs: ['0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8'], + optionalDVNs: [], + optionalDVNThreshold: 0, + }, + }, + receiveConfig: { + ulnConfig: { + confirmations: 2, + requiredDVNs: ['0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8'], + optionalDVNs: [], + optionalDVNThreshold: 0, + }, + }, + }, + }, + { + from: sepolia_testnetContract, + to: arbsep_testnetContract, + config: { + sendLibrary: '0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE', + receiveLibraryConfig: { receiveLibrary: '0xdAf00F5eE2158dD58E0d3857851c432E34A3A851', gracePeriod: 0 }, + sendConfig: { + executorConfig: { maxMessageSize: 10000, executor: '0x718B92b5CB0a5552039B593faF724D182A881eDA' }, + ulnConfig: { + confirmations: 2, + requiredDVNs: ['0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193'], + optionalDVNs: [], + optionalDVNThreshold: 0, + }, + }, + receiveConfig: { + ulnConfig: { + confirmations: 1, + requiredDVNs: ['0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193'], + optionalDVNs: [], + optionalDVNThreshold: 0, + }, + }, + }, + }, + ], +} +``` + +
+ +
+ npx hardhat lz:oapp-read:wire --oapp-config YOUR_OAPP_CONFIG + +
+ +Calls the configuration functions between your deployed OApp contracts on every chain based on the provided `layerzero.config.ts`. + +Running `lz:oapp:wire` will make the following function calls per pathway connection for a fully defined config file using your specified settings and your environment variables (Private Keys and RPCs): + +- function setPeer(uint32 \_eid, bytes32 \_peer) public virtual onlyOwner {} + +- function setConfig(address \_oapp, address \_lib, SetConfigParam[] calldata \_params) external onlyRegistered(\_lib) {} + +- function setEnforcedOptions(EnforcedOptionParam[] calldata \_enforcedOptions) public virtual onlyOwner {} + +- function setSendLibrary(address \_oapp, uint32 \_eid, address \_newLib) external onlyRegisteredOrDefault(\_newLib) isSendLib(\_newLib) onlySupportedEid(\_newLib, \_eid) {} + +- function setReceiveLibrary(address \_oapp, uint32 \_eid, address \_newLib, uint256 \_gracePeriod) external onlyRegisteredOrDefault(\_newLib) isReceiveLib(\_newLib) onlySupportedEid(\_newLib, \_eid) {} + +It will also make the following calls per network for a fully defined config file that enables lzRead + +- function setReadChannel(uint32 \_channelId, bool \_active) public virtual onlyOwner {} + +- function setConfig(address \_oapp, address \_lib, SetConfigParam[] calldata \_params) external onlyRegistered(\_lib) {} + +- function setEnforcedOptions(EnforcedOptionParam[] calldata \_enforcedOptions) public virtual onlyOwner {} + +- function setSendLibrary(address \_oapp, uint32 \_eid, address \_newLib) external onlyRegisteredOrDefault(\_newLib) isSendLib(\_newLib) onlySupportedEid(\_newLib, \_eid) {} + +- function setReceiveLibrary(address \_oapp, uint32 \_eid, address \_newLib, uint256 \_gracePeriod) external onlyRegisteredOrDefault(\_newLib) isReceiveLib(\_newLib) onlySupportedEid(\_newLib, \_eid) {} + +To use this task, run: + +```bash +npx hardhat lz:oapp-read:wire --oapp-config YOUR_LAYERZERO_CONFIG_FILE +``` + +Whenever you make changes to the configuration, run `lz:oapp:wire` again. The task will check your current configuration, and only apply NEW changes. + +To use a Gnosis Safe multisig as the signer for these transactions, add the following to each network in your `hardhat.config.ts` and add the `--safe` flag to `lz:oapp:wire --safe`: + +```yml +// hardhat.config.ts + +networks: { + // Include configurations for other networks as needed + fuji: { + /* ... */ + // Network-specific settings + safeConfig: { + safeUrl: 'http://something', // URL of the Safe API, not the Safe itself + safeAddress: 'address' + } + } +} +``` + +
+
+ npx hardhat lz:oapp-read:config:get --oapp-config YOUR_OAPP_CONFIG + +
+ +Returns your current OApp's configuration for each chain and pathway in 3 columns: + +- **Custom Configuration**: the changes that your `layerzero.config.ts` currently has set + +- **Default Configuration**: the default placeholder configuration that LayerZero provides + +- **Active Configuration**: the active configuration that applies to the message pathway (Defaults + Custom Values) + +If you do NOT explicitly set each configuration parameter, your OApp will fallback to the placeholder parameters in the default config. + +```bash +┌────────────────────┬───────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┐ +│ │ Custom OApp Config │ Default OApp Config │ Active OApp Config │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ localNetworkName │ arbsep │ arbsep │ arbsep │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ remoteNetworkName │ sepolia │ sepolia │ sepolia │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ sendLibrary │ 0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E │ 0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E │ 0x4f7cd4DA19ABB31b0eC98b9066B9e857B1bf9C0E │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ receiveLibrary │ 0x75Db67CDab2824970131D5aa9CECfC9F69c69636 │ 0x75Db67CDab2824970131D5aa9CECfC9F69c69636 │ 0x75Db67CDab2824970131D5aa9CECfC9F69c69636 │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ sendUlnConfig │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ +│ │ │ confirmations │ 1 │ │ │ confirmations │ 1 │ │ │ confirmations │ 1 │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ +│ │ │ │ │ 0 │ 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 │ │ │ │ │ │ 0 │ 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 │ │ │ │ │ │ 0 │ 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 │ │ │ +│ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │ +│ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ +│ │ │ │ │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ sendExecutorConfig │ ┌────────────────┬────────────────────────────────────────────┐ │ ┌────────────────┬────────────────────────────────────────────┐ │ ┌────────────────┬────────────────────────────────────────────┐ │ +│ │ │ executor │ 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 │ │ │ executor │ 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 │ │ │ executor │ 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 │ │ +│ │ ├────────────────┼────────────────────────────────────────────┤ │ ├────────────────┼────────────────────────────────────────────┤ │ ├────────────────┼────────────────────────────────────────────┤ │ +│ │ │ maxMessageSize │ 10000 │ │ │ maxMessageSize │ 10000 │ │ │ maxMessageSize │ 10000 │ │ +│ │ └────────────────┴────────────────────────────────────────────┘ │ └────────────────┴────────────────────────────────────────────┘ │ └────────────────┴────────────────────────────────────────────┘ │ +│ │ │ │ │ +├────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ receiveUlnConfig │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ +│ │ │ confirmations │ 2 │ │ │ confirmations │ 2 │ │ │ confirmations │ 2 │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ +│ │ │ │ │ 0 │ 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 │ │ │ │ │ │ 0 │ 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 │ │ │ │ │ │ 0 │ 0x53f488E93b4f1b60E8E83aa374dBe1780A1EE8a8 │ │ │ +│ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │ +│ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ +│ │ │ │ │ +└────────────────────┴───────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┘ + +┌──────────────────┬───────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┐ +│ │ Custom OApp Read Config │ Default OApp Read Config │ Active OApp Read Config │ +├──────────────────┼───────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ localNetworkName │ arbsep-testnet │ arbsep-testnet │ arbsep-testnet │ +├──────────────────┼───────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ channelId │ 4294967295 │ 4294967295 │ 4294967295 │ +├──────────────────┼───────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ readLibrary │ 0x0000000000000000000000000000000000000000 │ 0x54320b901FDe49Ba98de821Ccf374BA4358a8bf6 │ 0x54320b901FDe49Ba98de821Ccf374BA4358a8bf6 │ +├──────────────────┼───────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ +│ readUlnConfig │ ┌──────────────────────┬────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ ┌──────────────────────┬────────────────────────────────────────────────────┐ │ +│ │ │ executor │ 0x0000000000000000000000000000000000000000 │ │ │ executor │ 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 │ │ │ executor │ 0x5Df3a1cEbBD9c8BA7F8dF51Fd632A9aef8308897 │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ requiredDVNs │ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ │ requiredDVNs │ ┌───┬────────────────────────────────────────────┐ │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────┤ │ │ │ │ 0 │ 0xcb998B0CeC8b45B268336b99811533728880F08a │ │ │ │ │ │ 0 │ 0xcb998B0CeC8b45B268336b99811533728880F08a │ │ │ +│ │ │ optionalDVNs │ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ │ │ └───┴────────────────────────────────────────────┘ │ │ +│ │ ├──────────────────────┼────────────────────────────────────────────┤ │ │ │ │ │ │ │ │ │ +│ │ │ optionalDVNThreshold │ 0 │ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ └──────────────────────┴────────────────────────────────────────────┘ │ │ optionalDVNs │ │ │ │ optionalDVNs │ │ │ +│ │ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ ├──────────────────────┼────────────────────────────────────────────────────┤ │ +│ │ │ │ optionalDVNThreshold │ 0 │ │ │ optionalDVNThreshold │ 0 │ │ +│ │ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ └──────────────────────┴────────────────────────────────────────────────────┘ │ +│ │ │ │ │ +└──────────────────┴───────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┘ +``` + +
+
+ npx hardhat lz:oapp:config:get:executor --oapp-config YOUR_OAPP_CONFIG + +
+ +Returns the LayerZero Executor config for each network in your `hardhat.config.ts`. You can use this method to see the max destination gas in wei (`nativeCap`) you can request in your [`execution options`](https://docs.layerzero.network/v2/developers/evm/gas-settings/options). + +```bash +┌───────────────────┬────────────────────────────────────────────┐ +│ localNetworkName │ mantle │ +├───────────────────┼────────────────────────────────────────────┤ +│ remoteNetworkName │ polygon │ +├───────────────────┼────────────────────────────────────────────┤ +│ executorDstConfig │ ┌────────────────┬───────────────────────┐ │ +│ │ │ baseGas │ 85000 │ │ +│ │ ├────────────────┼───────────────────────┤ │ +│ │ │ multiplierBps │ 12000 │ │ +│ │ ├────────────────┼───────────────────────┤ │ +│ │ │ floorMarginUSD │ 5000000000000000000 │ │ +│ │ ├────────────────┼───────────────────────┤ │ +│ │ │ nativeCap │ 681000000000000000000 │ │ +│ │ └────────────────┴───────────────────────┘ │ +│ │ │ +└───────────────────┴────────────────────────────────────────────┘ +``` + +
+ +## Developing Contracts + +#### Installing dependencies + +We recommend using `pnpm` as a package manager (but you can of course use a package manager of your choice): + +```bash +pnpm install +``` + +#### Compiling your contracts + +This project supports both `hardhat` and `forge` compilation. By default, the `compile` command will execute both: + +```bash +pnpm compile +``` + +If you prefer one over the other, you can use the tooling-specific commands: + +```bash +pnpm compile:forge +pnpm compile:hardhat +``` + +Or adjust the `package.json` to for example remove `forge` build: + +```diff +- "compile": "$npm_execpath run compile:forge && $npm_execpath run compile:hardhat", +- "compile:forge": "forge build", +- "compile:hardhat": "hardhat compile", ++ "compile": "hardhat compile" +``` + +#### Running tests + +Similarly to the contract compilation, we support both `hardhat` and `forge` tests. By default, the `test` command will execute both: + +```bash +pnpm test +``` + +If you prefer one over the other, you can use the tooling-specific commands: + +```bash +pnpm test:forge +pnpm test:hardhat +``` + +Or adjust the `package.json` to for example remove `hardhat` tests: + +```diff +- "test": "$npm_execpath test:forge && $npm_execpath test:hardhat", +- "test:forge": "forge test", +- "test:hardhat": "$npm_execpath hardhat test" ++ "test": "forge test" +``` + +## Deploying Contracts + +Set up deployer wallet/account: + +- Rename `.env.example` -> `.env` +- Choose your preferred means of setting up your deployer wallet/account: + +``` +MNEMONIC="test test test test test test test test test test test junk" +or... +PRIVATE_KEY="0xabc...def" +``` + +- Fund this address with the corresponding chain's native tokens you want to deploy to. + +To deploy your contracts to your desired blockchains, run the following command in your project's folder: + +```bash +npx hardhat lz:deploy +``` + +More information about available CLI arguments can be found using the `--help` flag: + +```bash +npx hardhat lz:deploy --help +``` + +By following these steps, you can focus more on creating innovative omnichain solutions and less on the complexities of cross-chain communication. + +

+ +## Connecting Contracts + +### Ethereum Configurations + +Fill out your `layerzero.config.ts` with the contracts you want to connect. You can generate the default config file for your declared hardhat networks by running: + +```bash +npx hardhat lz:oapp-read:config:init --contract-name [YOUR_CONTRACT_NAME] --oapp-config [CONFIG_NAME] +``` + +> [!NOTE] +> You may need to change the contract name if you're deploying multiple OApp contracts on different chains (e.g., OFT and OFT Adapter). + +
+ +```typescript +const ethereumContract: OmniPointHardhat = { + eid: EndpointId.ETHEREUM_V2_MAINNET, + contractName: "MyOAppRead", +}; + +const arbitrumContract: OmniPointHardhat = { + eid: EndpointId.ARBITRUM_V2_MAINNET, + contractName: "MyOAppRead", +}; +``` + +Then define the pathway you want to create from and to each contract: + +```typescript +connections: [ + // ETH <--> ARB PATHWAY: START + { + from: ethereumContract, + to: arbitrumContract, + }, + { + from: arbitrumContract, + to: ethereumContract, + }, + // ETH <--> ARB PATHWAY: END +]; +``` + +Then define the config settings for each direction of the pathway: + +```typescript +connections: [ + // ETH <--> ARB PATHWAY: START + { + from: ethereumContract, + to: arbitrumContract, + config: { + sendLibrary: contractsConfig.ethereum.sendLib302, + receiveLibraryConfig: { + receiveLibrary: contractsConfig.ethereum.receiveLib302, + gracePeriod: BigInt(0), + }, + // Optional Receive Library Timeout for when the Old Receive Library Address will no longer be valid + receiveLibraryTimeoutConfig: { + lib: "0x0000000000000000000000000000000000000000", + expiry: BigInt(0), + }, + // Optional Send Configuration + // @dev Controls how the `from` chain sends messages to the `to` chain. + sendConfig: { + executorConfig: { + maxMessageSize: 10000, + // The configured Executor address + executor: contractsConfig.ethereum.executor, + }, + ulnConfig: { + // The number of block confirmations to wait on BSC before emitting the message from the source chain. + confirmations: BigInt(15), + // The address of the DVNs you will pay to verify a sent message on the source chain ). + // The destination tx will wait until ALL `requiredDVNs` verify the message. + requiredDVNs: [ + contractsConfig.ethereum.horizenDVN, // Horizen + contractsConfig.ethereum.polyhedraDVN, // Polyhedra + contractsConfig.ethereum.animocaBlockdaemonDVN, // Animoca-Blockdaemon (only available on ETH <-> Arbitrum One) + contractsConfig.ethereum.lzDVN, // LayerZero Labs + ], + // The address of the DVNs you will pay to verify a sent message on the source chain ). + // The destination tx will wait until the configured threshold of `optionalDVNs` verify a message. + optionalDVNs: [], + // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified. + optionalDVNThreshold: 0, + }, + }, + // Optional Receive Configuration + // @dev Controls how the `from` chain receives messages from the `to` chain. + receiveConfig: { + ulnConfig: { + // The number of block confirmations to expect from the `to` chain. + confirmations: BigInt(20), + // The address of the DVNs your `receiveConfig` expects to receive verifications from on the `from` chain ). + // The `from` chain's OApp will wait until the configured threshold of `requiredDVNs` verify the message. + requiredDVNs: [ + contractsConfig.ethereum.lzDVN, // LayerZero Labs DVN + contractsConfig.ethereum.animocaBlockdaemonDVN, // Blockdaemon-Animoca + contractsConfig.ethereum.horizenDVN, // Horizen Labs + contractsConfig.ethereum.polyhedraDVN, // Polyhedra + ], + // The address of the `optionalDVNs` you expect to receive verifications from on the `from` chain ). + // The destination tx will wait until the configured threshold of `optionalDVNs` verify the message. + optionalDVNs: [], + // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified. + optionalDVNThreshold: 0, + }, + }, + // Optional Enforced Options Configuration + // @dev Controls how much gas to use on the `to` chain, which the user pays for on the source `from` chain. + enforcedOptions: [ + { + msgType: 1, + optionType: ExecutorOptionType.LZ_RECEIVE, + gas: 65000, + value: 0, + }, + { + msgType: 2, + optionType: ExecutorOptionType.LZ_RECEIVE, + gas: 65000, + value: 0, + }, + { + msgType: 2, + optionType: ExecutorOptionType.COMPOSE, + index: 0, + gas: 50000, + value: 0, + }, + ], + }, + }, + { + from: arbitrumContract, + to: ethereumContract, + }, + // ETH <--> ARB PATHWAY: END +]; +``` + +Finally, define lzRead configurations for each contract + +```typescript +contracts: [ + { + contract: arbitrumContract, + config: { + readChannelConfigs: [ + { + channelId: 4294967295, + readLibrary: contractsConfig.arbitrum.readLib1002 + active: true, + ulnConfig: { + // The address of the Executor that will deliver the message + executor: contractConfig.arbitrum.lzExecutor + // The address of the DVNs your `receiveConfig` expects to receive verifications from on the `from` chain ). + // The `from` chain's OApp will wait until the configured threshold of `requiredDVNs` verify the message. + requiredDVNs: [ + contractsConfig.arbitrum.lzDVN, // LayerZero Labs DVN + contractsConfig.arbitrum.nethermindDVN, // Nethermind DVN + ], + // The address of the `optionalDVNs` you expect to receive verifications from on the `from` chain ). + // The destination tx will wait until the configured threshold of `optionalDVNs` verify the message. + optionalDVNs: [], + // The number of `optionalDVNs` that need to successfully verify the message for it to be considered Verified. + optionalDVNThreshold: 0, + }, + // Optional Enforced Options Configuration + // @dev Controls how much gas to use when delivering the resolved payload, as well as the expected size of the payload + enforcedOptions: [ + { + msgType: 1, + optionType: ExecutorOptionType.LZ_READ, + gas: 65000, + value: 0, + size: 100, + }, + ], + }, + ], + }, + }, + { + contract: ethereumContract, + }, +], +``` + +To set these config settings, run: + +```bash +npx hardhat lz:oapp-read:wire --oapp-config layerzero.config.ts +``` + +

+ Join our community on Discord | Follow us on Twitter +

diff --git a/examples/view-pure-read/contracts/ExampleContract.sol b/examples/view-pure-read/contracts/ExampleContract.sol new file mode 100644 index 000000000..d56f6dbfd --- /dev/null +++ b/examples/view-pure-read/contracts/ExampleContract.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title ExampleContract + * @notice A contract with a pure function for mathematical operations. + */ +contract ExampleContract { + /** + * @notice Adds two numbers. + * @param a First number. + * @param b Second number. + * @return sum The sum of a and b. + */ + function add(uint256 a, uint256 b) external pure returns (uint256 sum) { + return a + b; + } +} diff --git a/examples/view-pure-read/contracts/ReadNonViewReturn.sol b/examples/view-pure-read/contracts/ReadNonViewReturn.sol new file mode 100644 index 000000000..35afdfb93 --- /dev/null +++ b/examples/view-pure-read/contracts/ReadNonViewReturn.sol @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Import necessary interfaces and contracts +import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; +import { ReadCodecV1, EVMCallComputeV1, EVMCallRequestV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; + +import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; + +/** + * @title ReadNonViewFunctionExample + * @notice An OAppRead contract example to call a non-view function returning data. + */ +contract ReadNonViewFunctionExample is OAppRead, OAppOptionsType3 { + /// @notice Emitted when the amountOut is received. + /// @param amountOut The amount of tokenOut that would be received. + event AmountOutReceived(uint256 amountOut); + + /// @notice LayerZero read channel ID. + uint32 public READ_CHANNEL; + + /// @notice Target chain's Endpoint ID and QuoterV2 contract address. + uint32 public targetEid; + address public quoterAddress; + address public tokenIn; + address public tokenOut; + uint24 public fee; + + /** + * @notice Constructor to initialize the OAppRead contract. + * @param _endpoint The LayerZero endpoint contract address. + * @param _readChannel The LayerZero read channel ID. + * @param _targetEid The target chain's Endpoint ID. + * @param _quoterAddress The address of the IQuoterV2 contract on the target chain. + * @param _tokenIn The address of the input token. + * @param _tokenOut The address of the output token. + * @param _fee The pool fee. + */ + constructor( + address _endpoint, + uint32 _readChannel, + uint32 _targetEid, + address _quoterAddress, + address _tokenIn, + address _tokenOut, + uint24 _fee + ) OAppRead(_endpoint, msg.sender) Ownable(msg.sender) { + READ_CHANNEL = _readChannel; + targetEid = _targetEid; + quoterAddress = _quoterAddress; + tokenIn = _tokenIn; + tokenOut = _tokenOut; + fee = _fee; + _setPeer(READ_CHANNEL, AddressCast.toBytes32(address(this))); + } + + /** + * @notice Sets the LayerZero read channel. + * @param _channelId The channel ID to set. + * @param _active Flag to activate or deactivate the channel. + */ + function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { + _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); + READ_CHANNEL = _channelId; + } + + /** + * @notice Sends a read request to get a quote from Uniswap V3. + * @param amountIn The input amount of tokenIn. + * @param _extraOptions Additional messaging options. + * @return receipt The LayerZero messaging receipt for the request. + */ + function readQuote( + uint256 amountIn, + bytes calldata _extraOptions + ) external payable returns (MessagingReceipt memory receipt) { + bytes memory cmd = getCmd(amountIn); + return + _lzSend( + READ_CHANNEL, + cmd, + combineOptions(READ_CHANNEL, 1, _extraOptions), + MessagingFee(msg.value, 0), + payable(msg.sender) + ); + } + + /** + * @notice Constructs the read command to call `quoteExactInputSingle`. + * @param amountIn The input amount of tokenIn. + * @return cmd The encoded command. + */ + function getCmd(uint256 amountIn) public view returns (bytes memory) { + // Define the QuoteExactInputSingleParams + bytes memory params = abi.encode( + tokenIn, + tokenOut, + amountIn, + fee, + uint160(0) // sqrtPriceLimitX96: No price limit + ); + + // Encode the function call + bytes memory callData = abi.encodePacked( + bytes4(keccak256("quoteExactInputSingle((address,address,uint256,uint24,uint160))")), + params + ); + + EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); + readRequests[0] = EVMCallRequestV1({ + appRequestLabel: 1, + targetEid: targetEid, + isBlockNum: false, + blockNumOrTimestamp: uint64(block.timestamp), + confirmations: 15, + to: quoterAddress, + callData: callData + }); + + // Use lzMap to extract amountOut + EVMCallComputeV1 memory computeSettings = EVMCallComputeV1({ + computeSetting: 0, // lzMap only + targetEid: ILayerZeroEndpointV2(endpoint).eid(), + isBlockNum: false, + blockNumOrTimestamp: uint64(block.timestamp), + confirmations: 15, + to: address(this) + }); + + return ReadCodecV1.encode(0, readRequests, computeSettings); + } + + /** + * @notice Processes the response to extract amountOut. + * @param _response The response from the read request. + * @return Encoded amountOut. + */ + function lzMap(bytes calldata, bytes calldata _response) external pure returns (bytes memory) { + require(_response.length >= 32, "Invalid response length"); + (uint256 amountOut, , , ) = abi.decode(_response, (uint256, uint160, uint32, uint256)); + return abi.encode(amountOut); + } + + /** + * @notice Handles the received amountOut from the target chain. + * @param _message The encoded amountOut received. + */ + function _lzReceive(Origin calldata, bytes32, bytes calldata _message, address, bytes calldata) internal override { + // Decode amountOut + require(_message.length == 32, "Invalid message length"); + uint256 amountOut = abi.decode(_message, (uint256)); + emit AmountOutReceived(amountOut); + } +} diff --git a/examples/view-pure-read/contracts/ReadPublic.sol b/examples/view-pure-read/contracts/ReadPublic.sol new file mode 100644 index 000000000..75d93f0d4 --- /dev/null +++ b/examples/view-pure-read/contracts/ReadPublic.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Import necessary interfaces and contracts +import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; +import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { ReadCodecV1, EVMCallRequestV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; +import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/// @title IExampleContract +/// @notice Interface for the ExampleContract's `data()` function. +interface IExampleContract { + function data() external view returns (uint256); +} + +/// @title ReadPublic +/// @notice An OAppRead contract example to read a public state variable from another chain. +contract ReadPublic is OAppRead, OAppOptionsType3 { + /// @notice Emitted when the data is received. + /// @param data The value of the public state variable. + event DataReceived(uint256 data); + + /// @notice LayerZero read channel ID. + uint32 public READ_CHANNEL; + + /// @notice Message type for the read operation. + uint16 public constant READ_TYPE = 1; + + /** + * @notice Constructor to initialize the OAppRead contract. + * + * @param _endpoint The LayerZero endpoint contract address. + * @param _delegate The address that will have ownership privileges. + * @param _readChannel The LayerZero read channel ID. + */ + constructor( + address _endpoint, + address _delegate, + uint32 _readChannel + ) OAppRead(_endpoint, _delegate) Ownable(_delegate) { + READ_CHANNEL = _readChannel; + _setPeer(_readChannel, AddressCast.toBytes32(address(this))); + } + + /** + * @notice Sets the LayerZero read channel. + * + * @dev Only callable by the owner. + * + * @param _channelId The channel ID to set. + * @param _active Flag to activate or deactivate the channel. + */ + function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { + _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); + READ_CHANNEL = _channelId; + } + + /** + * @notice Sends a read request to fetch the public state variable `data`. + * + * @dev The caller must send enough ETH to cover the messaging fee. + * + * @param _targetContractAddress The address of the contract on the target chain containing the `data` variable. + * @param _targetEid The target chain's Endpoint ID. + * @param _extraOptions Additional messaging options. + * + * @return receipt The LayerZero messaging receipt for the request. + */ + function readData( + address _targetContractAddress, + uint32 _targetEid, + bytes calldata _extraOptions + ) external payable returns (MessagingReceipt memory receipt) { + bytes memory cmd = _getCmd(_targetContractAddress, _targetEid); + return + _lzSend( + READ_CHANNEL, + cmd, + _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), + MessagingFee(msg.value, 0), + payable(msg.sender) + ); + } + + /** + * @notice Estimates the messaging fee required to perform the read operation. + * + * @param _targetContractAddress The address of the contract on the target chain containing the `data` variable. + * @param _targetEid The target chain's Endpoint ID. + * @param _extraOptions Additional messaging options. + * + * @return fee The estimated messaging fee. + */ + function quoteReadFee( + address _targetContractAddress, + uint32 _targetEid, + bytes calldata _extraOptions + ) external view returns (MessagingFee memory fee) { + return + _quote( + READ_CHANNEL, + _getCmd(_targetContractAddress, _targetEid), + _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), + false + ); + } + + /** + * @notice Constructs the read command to fetch the `data` variable. + * + * @param targetContractAddress The address of the contract containing the `data` variable. + * @param targetEid The target chain's Endpoint ID. + * + * @return cmd The encoded command. + */ + function _getCmd(address targetContractAddress, uint32 targetEid) internal view returns (bytes memory cmd) { + // Use the function selector from the interface to ensure correctness + bytes memory callData = abi.encodeWithSelector(IExampleContract.data.selector); + + // Create an array of EVMCallRequestV1 with a single read request + EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); + readRequests[0] = EVMCallRequestV1({ + appRequestLabel: 1, // Application-specific label for tracking + targetEid: targetEid, // Endpoint ID of the target chain + isBlockNum: false, // Use timestamp instead of block number + blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at + confirmations: 15, // Number of confirmations to wait for finality + to: targetContractAddress, // Address of the contract to read from + callData: callData // Encoded function call data + }); + + // No compute logic needed; encode the command with appLabel 0 + cmd = ReadCodecV1.encode(0, readRequests); + } + + /** + * @notice Handles the received data from the target chain. + * + * @dev This function is called internally by the LayerZero protocol. + * + * @param _message The encoded data received. + */ + function _lzReceive( + Origin calldata /*_origin*/, + bytes32 /*_guid*/, + bytes calldata _message, + address /*_executor*/, + bytes calldata /*_extraData*/ + ) internal override { + // Decode the data received from bytes to uint256 + uint256 data = abi.decode(_message, (uint256)); + emit DataReceived(data); + } + + /** + * @notice Combines the options for messaging. + * + * @param _channelId The channel ID. + * @param _msgType The message type. + * @param _extraOptions Additional options. + * + * @return options The combined options. + */ + function _combineOptions( + uint32 _channelId, + uint16 _msgType, + bytes calldata _extraOptions + ) internal view returns (bytes memory options) { + options = combineOptions(_channelId, _msgType, _extraOptions); + } +} diff --git a/examples/view-pure-read/contracts/ReadViewOrPure.sol b/examples/view-pure-read/contracts/ReadViewOrPure.sol new file mode 100644 index 000000000..942cccd66 --- /dev/null +++ b/examples/view-pure-read/contracts/ReadViewOrPure.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Import necessary interfaces and contracts +import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; +import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { EVMCallRequestV1, ReadCodecV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; + +/// @title IExampleContract +/// @notice Interface for the ExampleContract's `add` function. +interface IExampleContract { + function add(uint256 a, uint256 b) external pure returns (uint256); +} + +/// @title ReadViewOrPure Example +/// @notice An OAppRead contract example to call a view or pure function from another chain. +contract ReadViewOrPure is OAppRead, OAppOptionsType3 { + /// @notice Emitted when the sum is received. + /// + /// @param sum The result of the addition. + event SumReceived(uint256 sum); + + /// @notice LayerZero read channel ID. + uint32 public READ_CHANNEL; + + /// @notice Message type for the read operation. + uint16 public constant READ_TYPE = 1; + + /// @notice Target chain's Endpoint ID. + uint32 public targetEid; + + /// @notice Target contract address on the target chain. + address public targetContractAddress; + + /** + * @notice Constructor to initialize the OAppRead contract. + * + * @param _endpoint The LayerZero endpoint contract address. + * @param _readChannel The LayerZero read channel ID. + * @param _targetEid The target chain's Endpoint ID. + * @param _targetContractAddress The address of the contract on the target chain. + */ + constructor( + address _endpoint, + uint32 _readChannel, + uint32 _targetEid, + address _targetContractAddress + ) OAppRead(_endpoint, msg.sender) Ownable(msg.sender) { + READ_CHANNEL = _readChannel; + targetEid = _targetEid; + targetContractAddress = _targetContractAddress; + _setPeer(READ_CHANNEL, AddressCast.toBytes32(address(this))); + } + + /** + * @notice Sets the LayerZero read channel. + * + * @dev Only callable by the owner. + * + * @param _channelId The channel ID to set. + * @param _active Flag to activate or deactivate the channel. + */ + function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { + _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); + READ_CHANNEL = _channelId; + } + + /** + * @notice Sends a read request to call the `add` function. + * + * @dev The caller must send enough ETH to cover the messaging fee. + * + * @param a The first number. + * @param b The second number. + * @param _extraOptions Additional messaging options. + * + * @return The LayerZero messaging receipt for the request. + */ + function readSum( + uint256 a, + uint256 b, + bytes calldata _extraOptions + ) external payable returns (MessagingReceipt memory) { + bytes memory cmd = _getCmd(a, b); + return + _lzSend( + READ_CHANNEL, + cmd, + _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), + MessagingFee(msg.value, 0), + payable(msg.sender) + ); + } + + /** + * @notice Estimates the messaging fee required to perform the read operation. + * + * @param a The first number to sum. + * @param b The second number to sum. + * @param _extraOptions Additional messaging options. + * + * @return fee The estimated messaging fee. + */ + function quoteReadFee( + uint256 a, + uint256 b, + bytes calldata _extraOptions + ) external view returns (MessagingFee memory fee) { + return _quote(READ_CHANNEL, _getCmd(a, b), _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), false); + } + + /** + * @notice Constructs the read command to call the `add` function. + * + * @param a The first number. + * @param b The second number. + * + * @return The encoded command. + */ + function _getCmd(uint256 a, uint256 b) internal view returns (bytes memory) { + // Encode callData to call add(a, b) function using the function selector + bytes memory callData = abi.encodeWithSelector(IExampleContract.add.selector, a, b); + + // Create an array of EVMCallRequestV1 with a single read request + EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); + readRequests[0] = EVMCallRequestV1({ + appRequestLabel: 1, // Application-specific label for tracking + targetEid: targetEid, // Endpoint ID of the target chain + isBlockNum: false, // Use timestamp instead of block number + blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at + confirmations: 15, // Number of confirmations to wait for finality + to: targetContractAddress, // Address of the contract to call + callData: callData // Encoded function call data + }); + + // No compute logic needed; encode the command with appLabel 0 + return ReadCodecV1.encode(0, readRequests); + } + + /** + * @notice Handles the received sum from the target chain. + * + * @dev This function is called internally by the LayerZero protocol. + * + * @param _message The encoded sum received. + */ + function _lzReceive( + Origin calldata /*_origin*/, + bytes32 /*_guid*/, + bytes calldata _message, + address /*_executor*/, + bytes calldata /*_extraData*/ + ) internal override { + // Decode the sum received + require(_message.length == 32, "Invalid message length"); + uint256 sum = abi.decode(_message, (uint256)); + emit SumReceived(sum); + } + + /** + * @notice Combines the options for messaging. + * + * @param _channelId The channel ID. + * @param _msgType The message type. + * @param _extraOptions Additional options. + * + * @return options The combined options. + */ + function _combineOptions( + uint32 _channelId, + uint16 _msgType, + bytes calldata _extraOptions + ) internal view returns (bytes memory options) { + options = combineOptions(_channelId, _msgType, _extraOptions); + } +} diff --git a/examples/view-pure-read/deploy/ReadViewOrPure.ts b/examples/view-pure-read/deploy/ReadViewOrPure.ts new file mode 100644 index 000000000..6254e5ee7 --- /dev/null +++ b/examples/view-pure-read/deploy/ReadViewOrPure.ts @@ -0,0 +1,56 @@ +import assert from 'assert' + +import { type DeployFunction } from 'hardhat-deploy/types' + +// TODO declare your contract name here +const contractName = 'ReadViewOrPure' + +const deploy: DeployFunction = async (hre) => { + const { getNamedAccounts, deployments } = hre + + const { deploy } = deployments + const { deployer } = await getNamedAccounts() + + assert(deployer, 'Missing named deployer account') + + console.log(`Network: ${hre.network.name}`) + console.log(`Deployer: ${deployer}`) + + // This is an external deployment pulled in from @layerzerolabs/lz-evm-sdk-v2 + // + // @layerzerolabs/toolbox-hardhat takes care of plugging in the external deployments + // from @layerzerolabs packages based on the configuration in your hardhat config + // + // For this to work correctly, your network config must define an eid property + // set to `EndpointId` as defined in @layerzerolabs/lz-definitions + // + // For example: + // + // networks: { + // fuji: { + // ... + // eid: EndpointId.AVALANCHE_V2_TESTNET + // } + // } + const endpointV2Deployment = await hre.deployments.get('EndpointV2') + const exampleContractDeployment = await hre.deployments.get('ExampleContract') + const targetEid = hre.network.config.eid + + const { address } = await deploy(contractName, { + from: deployer, + args: [ + endpointV2Deployment.address, // LayerZero's EndpointV2 address + deployer, // owner + targetEid, // targetEid + exampleContractDeployment.address, // ExampleContract address + ], + log: true, + skipIfAlreadyDeployed: false, + }) + + console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${address}`) +} + +deploy.tags = [contractName] + +export default deploy diff --git a/examples/view-pure-read/foundry.toml b/examples/view-pure-read/foundry.toml new file mode 100644 index 000000000..37c3d3533 --- /dev/null +++ b/examples/view-pure-read/foundry.toml @@ -0,0 +1,27 @@ +[profile.default] +solc-version = '0.8.22' +src = 'contracts' +out = 'out' +test = 'test/foundry' +cache_path = 'cache/foundry' +libs = [ + # We provide a set of useful contract utilities + # in the lib directory of @layerzerolabs/toolbox-foundry: + # + # - forge-std + # - ds-test + # - solidity-bytes-utils + 'node_modules/@layerzerolabs/toolbox-foundry/lib', + 'node_modules', +] + +remappings = [ + # Due to a misconfiguration of solidity-bytes-utils, an outdated version + # of forge-std is being dragged in + # + # To remedy this, we'll remap the ds-test and forge-std imports to ou own versions + 'ds-test/=node_modules/@layerzerolabs/toolbox-foundry/lib/ds-test', + 'forge-std/=node_modules/@layerzerolabs/toolbox-foundry/lib/forge-std', + '@layerzerolabs/=node_modules/@layerzerolabs/', + '@openzeppelin/=node_modules/@openzeppelin/', +] diff --git a/examples/view-pure-read/hardhat.config.ts b/examples/view-pure-read/hardhat.config.ts new file mode 100644 index 000000000..53c186deb --- /dev/null +++ b/examples/view-pure-read/hardhat.config.ts @@ -0,0 +1,82 @@ +// Get the environment configuration from .env file +// +// To make use of automatic environment setup: +// - Duplicate .env.example file and name it .env +// - Fill in the environment variables +import 'dotenv/config' + +import 'hardhat-deploy' +import 'hardhat-contract-sizer' +import '@nomiclabs/hardhat-ethers' +import '@layerzerolabs/toolbox-hardhat' +import { HardhatUserConfig, HttpNetworkAccountsUserConfig } from 'hardhat/types' + +import { EndpointId } from '@layerzerolabs/lz-definitions' + +// Set your preferred authentication method +// +// If you prefer using a mnemonic, set a MNEMONIC environment variable +// to a valid mnemonic +const MNEMONIC = process.env.MNEMONIC + +// If you prefer to be authenticated using a private key, set a PRIVATE_KEY environment variable +const PRIVATE_KEY = process.env.PRIVATE_KEY + +const accounts: HttpNetworkAccountsUserConfig | undefined = MNEMONIC + ? { mnemonic: MNEMONIC } + : PRIVATE_KEY + ? [PRIVATE_KEY] + : undefined + +if (accounts == null) { + console.warn( + 'Could not find MNEMONIC or PRIVATE_KEY environment variables. It will not be possible to execute transactions in your example.' + ) +} + +const config: HardhatUserConfig = { + paths: { + cache: 'cache/hardhat', + }, + solidity: { + compilers: [ + { + version: '0.8.22', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + ], + }, + networks: { + 'sepolia-testnet': { + eid: EndpointId.SEPOLIA_V2_TESTNET, + url: process.env.RPC_URL_SEPOLIA || 'https://rpc.sepolia.org/', + accounts, + }, + 'avalanche-testnet': { + eid: EndpointId.AVALANCHE_V2_TESTNET, + url: process.env.RPC_URL_FUJI || 'https://rpc.ankr.com/avalanche_fuji', + accounts, + }, + 'amoy-testnet': { + eid: EndpointId.AMOY_V2_TESTNET, + url: process.env.RPC_URL_AMOY || 'https://polygon-amoy-bor-rpc.publicnode.com', + accounts, + }, + hardhat: { + // Need this for testing because TestHelperOz5.sol is exceeding the compiled contract size limit + allowUnlimitedContractSize: true, + }, + }, + namedAccounts: { + deployer: { + default: 0, // wallet address of index[0], of the mnemonic in .env + }, + }, +} + +export default config diff --git a/examples/view-pure-read/layerzero.config.ts b/examples/view-pure-read/layerzero.config.ts new file mode 100644 index 000000000..ef106c864 --- /dev/null +++ b/examples/view-pure-read/layerzero.config.ts @@ -0,0 +1,34 @@ +import { ChannelId, EndpointId } from '@layerzerolabs/lz-definitions' + +import type { OAppReadOmniGraphHardhat, OmniPointHardhat } from '@layerzerolabs/toolbox-hardhat' + +const sepoliaContract: OmniPointHardhat = { + eid: EndpointId.SEPOLIA_V2_TESTNET, + contractName: 'ReadViewOrPure', +} + +const config: OAppReadOmniGraphHardhat = { + contracts: [ + { + contract: sepoliaContract, + config: { + readLibrary: '0xbcd4CADCac3F767C57c4F402932C4705DF62BEFf', + readChannels: [ + { + channelId: ChannelId.READ_CHANNEL_1, + active: true, + }, + ], + readConfig: { + ulnConfig: { + requiredDVNs: ['0x1308151a7ebac14f435d3ad5ff95c34160d539a5'], + executor: '0x31CAe3B7fB82d847621859fb1585353c5720660D', + }, + }, + }, + }, + ], + connections: [], +} + +export default config diff --git a/examples/view-pure-read/package.json b/examples/view-pure-read/package.json new file mode 100644 index 000000000..7d73854ec --- /dev/null +++ b/examples/view-pure-read/package.json @@ -0,0 +1,73 @@ +{ + "name": "@layerzerolabs/view-pure-read-example", + "version": "0.2.0", + "private": true, + "license": "MIT", + "scripts": { + "clean": "rm -rf artifacts cache out", + "compile": "$npm_execpath run compile:forge && $npm_execpath run compile:hardhat", + "compile:forge": "forge build", + "compile:hardhat": "hardhat compile", + "lint": "$npm_execpath run lint:js && $npm_execpath run lint:sol", + "lint:fix": "eslint --fix '**/*.{js,ts,json}' && prettier --write . && solhint 'contracts/**/*.sol' --fix --noPrompt", + "lint:js": "eslint '**/*.{js,ts,json}' && prettier --check .", + "lint:sol": "solhint 'contracts/**/*.sol'", + "test": "$npm_execpath run test:forge && $npm_execpath run test:hardhat", + "test:forge": "forge test", + "test:hardhat": "hardhat test" + }, + "resolutions": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + }, + "devDependencies": { + "@babel/core": "^7.23.9", + "@layerzerolabs/eslint-config-next": "~2.3.39", + "@layerzerolabs/lz-definitions": "^3.0.12", + "@layerzerolabs/lz-evm-messagelib-v2": "^3.0.12", + "@layerzerolabs/lz-evm-protocol-v2": "^3.0.12", + "@layerzerolabs/lz-evm-v1-0.7": "^3.0.12", + "@layerzerolabs/lz-v2-utilities": "^3.0.12", + "@layerzerolabs/oapp-evm": "^0.2.0", + "@layerzerolabs/prettier-config-next": "^2.3.39", + "@layerzerolabs/solhint-config": "^3.0.12", + "@layerzerolabs/test-devtools-evm-foundry": "~4.0.0", + "@layerzerolabs/toolbox-foundry": "~0.1.9", + "@layerzerolabs/toolbox-hardhat": "~0.6.0", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomiclabs/hardhat-ethers": "^2.2.3", + "@openzeppelin/contracts": "^5.0.2", + "@openzeppelin/contracts-upgradeable": "^5.0.2", + "@rushstack/eslint-patch": "^1.7.0", + "@types/chai": "^4.3.11", + "@types/mocha": "^10.0.6", + "@types/node": "~18.18.14", + "chai": "^4.4.1", + "dotenv": "^16.4.1", + "eslint": "^8.55.0", + "eslint-plugin-jest-extended": "~2.0.0", + "ethers": "^5.7.2", + "hardhat": "^2.22.10", + "hardhat-contract-sizer": "^2.10.0", + "hardhat-deploy": "^0.12.1", + "mocha": "^10.2.0", + "prettier": "^3.2.5", + "solhint": "^4.1.1", + "solidity-bytes-utils": "^0.8.2", + "ts-node": "^10.9.2", + "typescript": "^5.4.4" + }, + "engines": { + "node": ">=18.16.0" + }, + "pnpm": { + "overrides": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + } + }, + "overrides": { + "ethers": "^5.7.2", + "hardhat-deploy": "^0.12.1" + } +} diff --git a/examples/view-pure-read/solhint.config.js b/examples/view-pure-read/solhint.config.js new file mode 100644 index 000000000..52efe629c --- /dev/null +++ b/examples/view-pure-read/solhint.config.js @@ -0,0 +1 @@ +module.exports = require('@layerzerolabs/solhint-config'); diff --git a/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol b/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol new file mode 100644 index 000000000..5f666e526 --- /dev/null +++ b/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +// MyOApp imports +import { ReadViewOrPure } from "../../contracts/ReadViewOrPure.sol"; +import { ExampleContract } from "../../contracts/ExampleContract.sol"; + +// OApp imports +import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; + +// OZ imports +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +// Forge imports +import "forge-std/console.sol"; +import "forge-std/Test.sol"; + +// DevTools imports +import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; + +/** + * @title ReadViewOrPureTest + * @notice A test suite for the ReadViewOrPure contract. + */ +contract ReadViewOrPureTest is TestHelperOz5 { + using OptionsBuilder for bytes; + + /// @notice Chain A Endpoint ID. + uint32 private aEid = 1; + + /// @notice Chain B Endpoint ID. + uint32 private bEid = 2; + + /// @notice The ReadViewOrPure contract deployed on chain B. + ReadViewOrPure private bOApp; + + /// @notice The ExampleContract deployed on chain A. + ExampleContract private exampleContract; + + /// @notice Address representing User A. + address private userA = address(0x1); + + /// @notice Message type for the read operation. + uint16 private constant READ_TYPE = 1; + + /** + * @notice Sets up the test environment before each test. + * + * @dev Deploys the ExampleContract on chain A and the ReadViewOrPure contract on chain B. + * Wires the OApps and sets up the endpoints. + */ + function setUp() public virtual override { + vm.deal(userA, 1000 ether); + + super.setUp(); + setUpEndpoints(2, LibraryType.UltraLightNode); + + // Deploy ExampleContract on chain A (aEid) + // We simulate chain A by associating contracts with aEid + exampleContract = ExampleContract( + _deployOApp( + type(ExampleContract).creationCode, + abi.encode() // No constructor arguments needed for ExampleContract + ) + ); + + // Deploy ReadViewOrPure on chain B (bEid) + bOApp = ReadViewOrPure( + _deployOApp( + type(ReadViewOrPure).creationCode, + abi.encode( + address(endpoints[bEid]), // _endpoint (LayerZero endpoint on chain B) + DEFAULT_CHANNEL_ID, // _readChannel + aEid, // _targetEid (Endpoint ID of chain A) + address(exampleContract) // _targetContractAddress (ExampleContract on chain A) + ) + ) + ); + + // Wire the OApps + address[] memory oapps = new address[](1); + oapps[0] = address(bOApp); + uint32[] memory channels = new uint32[](1); + channels[0] = DEFAULT_CHANNEL_ID; + this.wireReadOApps(oapps, channels); + } + + /** + * @notice Tests that the constructor initializes the contract correctly. + * + * @dev Verifies that the owner, endpoint, READ_CHANNEL, targetEid, and targetContractAddress are set as expected. + */ + function test_constructor() public { + // Verify that the owner is correctly set + assertEq(bOApp.owner(), address(this)); + // Verify that the endpoint is correctly set + assertEq(address(bOApp.endpoint()), address(endpoints[bEid])); + // Verify that READ_CHANNEL is correctly set + assertEq(bOApp.READ_CHANNEL(), DEFAULT_CHANNEL_ID); + // Verify that targetEid is correctly set + assertEq(bOApp.targetEid(), aEid); + // Verify that targetContractAddress is correctly set + assertEq(bOApp.targetContractAddress(), address(exampleContract)); + } + + /** + * @notice Tests sending a read request and handling the received sum. + * + * @dev Simulates a user initiating a read request to add two numbers and verifies that the SumReceived event is emitted with the correct sum. + */ + function test_send_read() public { + // Prepare messaging options + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(1e8, 100, 0); + console.logBytes(options); + + // Define the numbers to add + uint256 a = 2; + uint256 b = 3; + uint256 expectedSum = a + b; + + // Estimate the fee for calling readSum with arguments a and b + MessagingFee memory fee = bOApp.quoteReadFee(a, b, options); + + // Record logs to capture the SumReceived event + vm.recordLogs(); + + // User A initiates the read request on bOApp + vm.prank(userA); + bOApp.readSum{ value: fee.nativeFee }(a, b, options); + + // Simulate processing the response packet to bOApp on bEid, injecting the sum + this.verifyPackets( + bEid, + addressToBytes32(address(bOApp)), + 0, + address(0x0), + abi.encode(expectedSum) // The sum of a and b + ); + + // Retrieve the logs to verify the SumReceived event + Vm.Log[] memory entries = vm.getRecordedLogs(); + bool found = false; + uint256 sumReceived; + for (uint256 i = 0; i < entries.length; i++) { + Vm.Log memory entry = entries[i]; + if (entry.topics[0] == keccak256("SumReceived(uint256)")) { + sumReceived = abi.decode(entry.data, (uint256)); + found = true; + break; + } + } + require(found, "SumReceived event not found"); + assertEq(sumReceived, expectedSum, "Sum received does not match expected value"); + } +} diff --git a/examples/view-pure-read/tsconfig.json b/examples/view-pure-read/tsconfig.json new file mode 100644 index 000000000..21d541fa5 --- /dev/null +++ b/examples/view-pure-read/tsconfig.json @@ -0,0 +1,14 @@ +{ + "exclude": ["node_modules"], + "include": ["deploy", "test", "tasks", "hardhat.config.ts"], + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "types": ["node", "mocha"] + } +} diff --git a/packages/oapp-evm-upgradeable/artifacts/ExecutorOptions.sol/ExecutorOptions.json b/packages/oapp-evm-upgradeable/artifacts/ExecutorOptions.sol/ExecutorOptions.json index 2b52fb629..b9a574ee4 100644 --- a/packages/oapp-evm-upgradeable/artifacts/ExecutorOptions.sol/ExecutorOptions.json +++ b/packages/oapp-evm-upgradeable/artifacts/ExecutorOptions.sol/ExecutorOptions.json @@ -5,6 +5,7 @@ "name": "Executor_InvalidLzComposeOption", "inputs": [] }, + { "type": "error", "name": "Executor_InvalidLzReadOption", "inputs": [] }, { "type": "error", "name": "Executor_InvalidLzReceiveOption", @@ -17,17 +18,17 @@ } ], "bytecode": { - "object": "0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212208709f2e348cc6f33a5c25ffcd2bfaac629e8cae37810ea39adfa1038b12cefd764736f6c63430008160033", - "sourceMap": "133:3427:55:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;133:3427:55;;;;;;;;;;;;;;;;;", + "object": "0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220bc11bf556b282e43d54b3c888325ffc71060da4609cd97e9fb6bc1ff9d4ab15764736f6c63430008160033", + "sourceMap": "145:4183:19:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;145:4183:19;;;;;;;;;;;;;;;;;", "linkReferences": {} }, "deployedBytecode": { - "object": "0x73000000000000000000000000000000000000000030146080604052600080fdfea26469706673582212208709f2e348cc6f33a5c25ffcd2bfaac629e8cae37810ea39adfa1038b12cefd764736f6c63430008160033", - "sourceMap": "133:3427:55:-:0;;;;;;;;", + "object": "0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220bc11bf556b282e43d54b3c888325ffc71060da4609cd97e9fb6bc1ff9d4ab15764736f6c63430008160033", + "sourceMap": "145:4183:19:-:0;;;;;;;;", "linkReferences": {} }, "methodIdentifiers": {}, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"Executor_InvalidLzComposeOption\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Executor_InvalidLzReceiveOption\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Executor_InvalidNativeDropOption\",\"type\":\"error\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol\":\"ExecutorOptions\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@layerzerolabs/=node_modules/@layerzerolabs/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":ds-test/=node_modules/@layerzerolabs/toolbox-foundry/src/ds-test/src/\",\":forge-std/=node_modules/@layerzerolabs/toolbox-foundry/src/forge-std/src/\",\":solidity-bytes-utils/contracts/=node_modules/@layerzerolabs/toolbox-foundry/lib/solidity-bytes-utils/\"]},\"sources\":{\"node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\":{\"keccak256\":\"0x5c0db161cef6603c3b256d4220f489419e7478ef775e52a80056654129c61875\",\"license\":\"LZBL-1.2\",\"urls\":[\"bzz-raw://a33245d0fdd3992bb56b31d1840108d36bb46c8d617b659ef1af8dd7ed86302d\",\"dweb:/ipfs/QmWyBqT7Tdrfn5zz9xYM3V1PBtfAZAVwwCrrKwwfi3wMQK\"]},\"node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol\":{\"keccak256\":\"0x621c6090fc432f94a99f677a95d72b75f56db89ddc5fc870669777a313587c28\",\"license\":\"LZBL-1.2\",\"urls\":[\"bzz-raw://69086166f16d29e30c5eb9ca2f97afb27569afe619807d79736833a809a512d2\",\"dweb:/ipfs/QmcZqXjFuVdTrfbteKBF3GuEpjVFZ2YNZTa3J87dyLsqav\"]}},\"version\":1}", + "rawMetadata": "{\"compiler\":{\"version\":\"0.8.22+commit.4fc1097e\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"Executor_InvalidLzComposeOption\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Executor_InvalidLzReadOption\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Executor_InvalidLzReceiveOption\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Executor_InvalidNativeDropOption\",\"type\":\"error\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"node_modules/@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\":\"ExecutorOptions\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@layerzerolabs/=node_modules/@layerzerolabs/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":ds-test/=node_modules/@layerzerolabs/toolbox-foundry/src/ds-test/src/\",\":forge-std/=node_modules/@layerzerolabs/toolbox-foundry/src/forge-std/src/\",\":solidity-bytes-utils/contracts/=node_modules/@layerzerolabs/toolbox-foundry/lib/solidity-bytes-utils/\"]},\"sources\":{\"node_modules/@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol\":{\"keccak256\":\"0x441b723f2f597be2ec2bb361fcf3f11852c23534db1cfa7d2ffff7e61d228e3c\",\"license\":\"LZBL-1.2\",\"urls\":[\"bzz-raw://636817d20f90f75032e35376256cf5f4d2a047d6541b45f644d82a2e4dc8f1eb\",\"dweb:/ipfs/QmcEFRxCmmm9hKbqi7Powj6ATbw4JXXJW4rxfwMcxWsDnT\"]},\"node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol\":{\"keccak256\":\"0x5c0db161cef6603c3b256d4220f489419e7478ef775e52a80056654129c61875\",\"license\":\"LZBL-1.2\",\"urls\":[\"bzz-raw://a33245d0fdd3992bb56b31d1840108d36bb46c8d617b659ef1af8dd7ed86302d\",\"dweb:/ipfs/QmWyBqT7Tdrfn5zz9xYM3V1PBtfAZAVwwCrrKwwfi3wMQK\"]}},\"version\":1}", "metadata": { "compiler": { "version": "0.8.22+commit.4fc1097e" }, "language": "Solidity", @@ -38,6 +39,11 @@ "type": "error", "name": "Executor_InvalidLzComposeOption" }, + { + "inputs": [], + "type": "error", + "name": "Executor_InvalidLzReadOption" + }, { "inputs": [], "type": "error", @@ -63,30 +69,30 @@ "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "ipfs" }, "compilationTarget": { - "node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol": "ExecutorOptions" + "node_modules/@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": "ExecutorOptions" }, "evmVersion": "paris", "libraries": {} }, "sources": { - "node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { - "keccak256": "0x5c0db161cef6603c3b256d4220f489419e7478ef775e52a80056654129c61875", + "node_modules/@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol": { + "keccak256": "0x441b723f2f597be2ec2bb361fcf3f11852c23534db1cfa7d2ffff7e61d228e3c", "urls": [ - "bzz-raw://a33245d0fdd3992bb56b31d1840108d36bb46c8d617b659ef1af8dd7ed86302d", - "dweb:/ipfs/QmWyBqT7Tdrfn5zz9xYM3V1PBtfAZAVwwCrrKwwfi3wMQK" + "bzz-raw://636817d20f90f75032e35376256cf5f4d2a047d6541b45f644d82a2e4dc8f1eb", + "dweb:/ipfs/QmcEFRxCmmm9hKbqi7Powj6ATbw4JXXJW4rxfwMcxWsDnT" ], "license": "LZBL-1.2" }, - "node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol": { - "keccak256": "0x621c6090fc432f94a99f677a95d72b75f56db89ddc5fc870669777a313587c28", + "node_modules/@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol": { + "keccak256": "0x5c0db161cef6603c3b256d4220f489419e7478ef775e52a80056654129c61875", "urls": [ - "bzz-raw://69086166f16d29e30c5eb9ca2f97afb27569afe619807d79736833a809a512d2", - "dweb:/ipfs/QmcZqXjFuVdTrfbteKBF3GuEpjVFZ2YNZTa3J87dyLsqav" + "bzz-raw://a33245d0fdd3992bb56b31d1840108d36bb46c8d617b659ef1af8dd7ed86302d", + "dweb:/ipfs/QmWyBqT7Tdrfn5zz9xYM3V1PBtfAZAVwwCrrKwwfi3wMQK" ], "license": "LZBL-1.2" } }, "version": 1 }, - "id": 55 + "id": 19 } From d53138e2adb81ee83b69d1c697c0faf7296f6b49 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 09:40:08 +0700 Subject: [PATCH 08/17] chore: re-install and build --- .../contracts/ReadNonViewReturn.sol | 160 ---------------- .../view-pure-read/contracts/ReadPublic.sol | 174 ------------------ pnpm-lock.yaml | 166 +++++++++++++++++ 3 files changed, 166 insertions(+), 334 deletions(-) delete mode 100644 examples/view-pure-read/contracts/ReadNonViewReturn.sol delete mode 100644 examples/view-pure-read/contracts/ReadPublic.sol diff --git a/examples/view-pure-read/contracts/ReadNonViewReturn.sol b/examples/view-pure-read/contracts/ReadNonViewReturn.sol deleted file mode 100644 index 35afdfb93..000000000 --- a/examples/view-pure-read/contracts/ReadNonViewReturn.sol +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -// Import necessary interfaces and contracts -import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; -import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; -import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; -import { ReadCodecV1, EVMCallComputeV1, EVMCallRequestV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; -import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; - -import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; - -import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; - -/** - * @title ReadNonViewFunctionExample - * @notice An OAppRead contract example to call a non-view function returning data. - */ -contract ReadNonViewFunctionExample is OAppRead, OAppOptionsType3 { - /// @notice Emitted when the amountOut is received. - /// @param amountOut The amount of tokenOut that would be received. - event AmountOutReceived(uint256 amountOut); - - /// @notice LayerZero read channel ID. - uint32 public READ_CHANNEL; - - /// @notice Target chain's Endpoint ID and QuoterV2 contract address. - uint32 public targetEid; - address public quoterAddress; - address public tokenIn; - address public tokenOut; - uint24 public fee; - - /** - * @notice Constructor to initialize the OAppRead contract. - * @param _endpoint The LayerZero endpoint contract address. - * @param _readChannel The LayerZero read channel ID. - * @param _targetEid The target chain's Endpoint ID. - * @param _quoterAddress The address of the IQuoterV2 contract on the target chain. - * @param _tokenIn The address of the input token. - * @param _tokenOut The address of the output token. - * @param _fee The pool fee. - */ - constructor( - address _endpoint, - uint32 _readChannel, - uint32 _targetEid, - address _quoterAddress, - address _tokenIn, - address _tokenOut, - uint24 _fee - ) OAppRead(_endpoint, msg.sender) Ownable(msg.sender) { - READ_CHANNEL = _readChannel; - targetEid = _targetEid; - quoterAddress = _quoterAddress; - tokenIn = _tokenIn; - tokenOut = _tokenOut; - fee = _fee; - _setPeer(READ_CHANNEL, AddressCast.toBytes32(address(this))); - } - - /** - * @notice Sets the LayerZero read channel. - * @param _channelId The channel ID to set. - * @param _active Flag to activate or deactivate the channel. - */ - function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { - _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); - READ_CHANNEL = _channelId; - } - - /** - * @notice Sends a read request to get a quote from Uniswap V3. - * @param amountIn The input amount of tokenIn. - * @param _extraOptions Additional messaging options. - * @return receipt The LayerZero messaging receipt for the request. - */ - function readQuote( - uint256 amountIn, - bytes calldata _extraOptions - ) external payable returns (MessagingReceipt memory receipt) { - bytes memory cmd = getCmd(amountIn); - return - _lzSend( - READ_CHANNEL, - cmd, - combineOptions(READ_CHANNEL, 1, _extraOptions), - MessagingFee(msg.value, 0), - payable(msg.sender) - ); - } - - /** - * @notice Constructs the read command to call `quoteExactInputSingle`. - * @param amountIn The input amount of tokenIn. - * @return cmd The encoded command. - */ - function getCmd(uint256 amountIn) public view returns (bytes memory) { - // Define the QuoteExactInputSingleParams - bytes memory params = abi.encode( - tokenIn, - tokenOut, - amountIn, - fee, - uint160(0) // sqrtPriceLimitX96: No price limit - ); - - // Encode the function call - bytes memory callData = abi.encodePacked( - bytes4(keccak256("quoteExactInputSingle((address,address,uint256,uint24,uint160))")), - params - ); - - EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); - readRequests[0] = EVMCallRequestV1({ - appRequestLabel: 1, - targetEid: targetEid, - isBlockNum: false, - blockNumOrTimestamp: uint64(block.timestamp), - confirmations: 15, - to: quoterAddress, - callData: callData - }); - - // Use lzMap to extract amountOut - EVMCallComputeV1 memory computeSettings = EVMCallComputeV1({ - computeSetting: 0, // lzMap only - targetEid: ILayerZeroEndpointV2(endpoint).eid(), - isBlockNum: false, - blockNumOrTimestamp: uint64(block.timestamp), - confirmations: 15, - to: address(this) - }); - - return ReadCodecV1.encode(0, readRequests, computeSettings); - } - - /** - * @notice Processes the response to extract amountOut. - * @param _response The response from the read request. - * @return Encoded amountOut. - */ - function lzMap(bytes calldata, bytes calldata _response) external pure returns (bytes memory) { - require(_response.length >= 32, "Invalid response length"); - (uint256 amountOut, , , ) = abi.decode(_response, (uint256, uint160, uint32, uint256)); - return abi.encode(amountOut); - } - - /** - * @notice Handles the received amountOut from the target chain. - * @param _message The encoded amountOut received. - */ - function _lzReceive(Origin calldata, bytes32, bytes calldata _message, address, bytes calldata) internal override { - // Decode amountOut - require(_message.length == 32, "Invalid message length"); - uint256 amountOut = abi.decode(_message, (uint256)); - emit AmountOutReceived(amountOut); - } -} diff --git a/examples/view-pure-read/contracts/ReadPublic.sol b/examples/view-pure-read/contracts/ReadPublic.sol deleted file mode 100644 index 75d93f0d4..000000000 --- a/examples/view-pure-read/contracts/ReadPublic.sol +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -// Import necessary interfaces and contracts -import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; -import { MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; -import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; -import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; -import { ReadCodecV1, EVMCallRequestV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; -import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; - -/// @title IExampleContract -/// @notice Interface for the ExampleContract's `data()` function. -interface IExampleContract { - function data() external view returns (uint256); -} - -/// @title ReadPublic -/// @notice An OAppRead contract example to read a public state variable from another chain. -contract ReadPublic is OAppRead, OAppOptionsType3 { - /// @notice Emitted when the data is received. - /// @param data The value of the public state variable. - event DataReceived(uint256 data); - - /// @notice LayerZero read channel ID. - uint32 public READ_CHANNEL; - - /// @notice Message type for the read operation. - uint16 public constant READ_TYPE = 1; - - /** - * @notice Constructor to initialize the OAppRead contract. - * - * @param _endpoint The LayerZero endpoint contract address. - * @param _delegate The address that will have ownership privileges. - * @param _readChannel The LayerZero read channel ID. - */ - constructor( - address _endpoint, - address _delegate, - uint32 _readChannel - ) OAppRead(_endpoint, _delegate) Ownable(_delegate) { - READ_CHANNEL = _readChannel; - _setPeer(_readChannel, AddressCast.toBytes32(address(this))); - } - - /** - * @notice Sets the LayerZero read channel. - * - * @dev Only callable by the owner. - * - * @param _channelId The channel ID to set. - * @param _active Flag to activate or deactivate the channel. - */ - function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { - _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); - READ_CHANNEL = _channelId; - } - - /** - * @notice Sends a read request to fetch the public state variable `data`. - * - * @dev The caller must send enough ETH to cover the messaging fee. - * - * @param _targetContractAddress The address of the contract on the target chain containing the `data` variable. - * @param _targetEid The target chain's Endpoint ID. - * @param _extraOptions Additional messaging options. - * - * @return receipt The LayerZero messaging receipt for the request. - */ - function readData( - address _targetContractAddress, - uint32 _targetEid, - bytes calldata _extraOptions - ) external payable returns (MessagingReceipt memory receipt) { - bytes memory cmd = _getCmd(_targetContractAddress, _targetEid); - return - _lzSend( - READ_CHANNEL, - cmd, - _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), - MessagingFee(msg.value, 0), - payable(msg.sender) - ); - } - - /** - * @notice Estimates the messaging fee required to perform the read operation. - * - * @param _targetContractAddress The address of the contract on the target chain containing the `data` variable. - * @param _targetEid The target chain's Endpoint ID. - * @param _extraOptions Additional messaging options. - * - * @return fee The estimated messaging fee. - */ - function quoteReadFee( - address _targetContractAddress, - uint32 _targetEid, - bytes calldata _extraOptions - ) external view returns (MessagingFee memory fee) { - return - _quote( - READ_CHANNEL, - _getCmd(_targetContractAddress, _targetEid), - _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), - false - ); - } - - /** - * @notice Constructs the read command to fetch the `data` variable. - * - * @param targetContractAddress The address of the contract containing the `data` variable. - * @param targetEid The target chain's Endpoint ID. - * - * @return cmd The encoded command. - */ - function _getCmd(address targetContractAddress, uint32 targetEid) internal view returns (bytes memory cmd) { - // Use the function selector from the interface to ensure correctness - bytes memory callData = abi.encodeWithSelector(IExampleContract.data.selector); - - // Create an array of EVMCallRequestV1 with a single read request - EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); - readRequests[0] = EVMCallRequestV1({ - appRequestLabel: 1, // Application-specific label for tracking - targetEid: targetEid, // Endpoint ID of the target chain - isBlockNum: false, // Use timestamp instead of block number - blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at - confirmations: 15, // Number of confirmations to wait for finality - to: targetContractAddress, // Address of the contract to read from - callData: callData // Encoded function call data - }); - - // No compute logic needed; encode the command with appLabel 0 - cmd = ReadCodecV1.encode(0, readRequests); - } - - /** - * @notice Handles the received data from the target chain. - * - * @dev This function is called internally by the LayerZero protocol. - * - * @param _message The encoded data received. - */ - function _lzReceive( - Origin calldata /*_origin*/, - bytes32 /*_guid*/, - bytes calldata _message, - address /*_executor*/, - bytes calldata /*_extraData*/ - ) internal override { - // Decode the data received from bytes to uint256 - uint256 data = abi.decode(_message, (uint256)); - emit DataReceived(data); - } - - /** - * @notice Combines the options for messaging. - * - * @param _channelId The channel ID. - * @param _msgType The message type. - * @param _extraOptions Additional options. - * - * @return options The combined options. - */ - function _combineOptions( - uint32 _channelId, - uint16 _msgType, - bytes calldata _extraOptions - ) internal view returns (bytes memory options) { - options = combineOptions(_channelId, _msgType, _extraOptions); - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e199ca0a4..a7fae9f79 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1347,6 +1347,114 @@ importers: specifier: ^5.4.4 version: 5.5.3 + examples/view-pure-read: + devDependencies: + '@babel/core': + specifier: ^7.23.9 + version: 7.23.9 + '@layerzerolabs/eslint-config-next': + specifier: ~2.3.39 + version: 2.3.44(typescript@5.5.3) + '@layerzerolabs/lz-definitions': + specifier: ^3.0.12 + version: 3.0.12 + '@layerzerolabs/lz-evm-messagelib-v2': + specifier: ^3.0.12 + version: 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': + specifier: ^3.0.12 + version: 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': + specifier: ^3.0.12 + version: 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) + '@layerzerolabs/lz-v2-utilities': + specifier: ^3.0.12 + version: 3.0.12 + '@layerzerolabs/oapp-evm': + specifier: ^0.2.0 + version: 0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@layerzerolabs/prettier-config-next': + specifier: ^2.3.39 + version: 2.3.44 + '@layerzerolabs/solhint-config': + specifier: ^3.0.12 + version: 3.0.12(typescript@5.5.3) + '@layerzerolabs/test-devtools-evm-foundry': + specifier: ~4.0.0 + version: 4.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@layerzerolabs/oft-evm@2.0.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@layerzerolabs/toolbox-foundry': + specifier: ~0.1.9 + version: link:../../packages/toolbox-foundry + '@layerzerolabs/toolbox-hardhat': + specifier: ~0.6.0 + version: link:../../packages/toolbox-hardhat + '@nomicfoundation/hardhat-ethers': + specifier: ^3.0.5 + version: 3.0.5(ethers@5.7.2)(hardhat@2.22.12) + '@nomiclabs/hardhat-ethers': + specifier: ^2.2.3 + version: 2.2.3(ethers@5.7.2)(hardhat@2.22.12) + '@openzeppelin/contracts': + specifier: ^5.0.2 + version: 5.1.0 + '@openzeppelin/contracts-upgradeable': + specifier: ^5.0.2 + version: 5.1.0(@openzeppelin/contracts@5.1.0) + '@rushstack/eslint-patch': + specifier: ^1.7.0 + version: 1.7.0 + '@types/chai': + specifier: ^4.3.11 + version: 4.3.20 + '@types/mocha': + specifier: ^10.0.6 + version: 10.0.6 + '@types/node': + specifier: ~18.18.14 + version: 18.18.14 + chai: + specifier: ^4.4.1 + version: 4.5.0 + dotenv: + specifier: ^16.4.1 + version: 16.4.5 + eslint: + specifier: ^8.55.0 + version: 8.57.1 + eslint-plugin-jest-extended: + specifier: ~2.0.0 + version: 2.0.0(eslint@8.57.1)(typescript@5.5.3) + ethers: + specifier: ^5.7.2 + version: 5.7.2 + hardhat: + specifier: ^2.22.10 + version: 2.22.12(ts-node@10.9.2)(typescript@5.5.3) + hardhat-contract-sizer: + specifier: ^2.10.0 + version: 2.10.0(hardhat@2.22.12) + hardhat-deploy: + specifier: ^0.12.1 + version: 0.12.4 + mocha: + specifier: ^10.2.0 + version: 10.2.0 + prettier: + specifier: ^3.2.5 + version: 3.2.5 + solhint: + specifier: ^4.1.1 + version: 4.1.1(typescript@5.5.3) + solidity-bytes-utils: + specifier: ^0.8.2 + version: 0.8.2 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + typescript: + specifier: ^5.4.4 + version: 5.5.3 + packages/build-devtools: devDependencies: '@swc/core': @@ -6877,6 +6985,44 @@ packages: bs58: 5.0.0 tiny-invariant: 1.3.3 + /@layerzerolabs/oapp-evm@0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): + resolution: {integrity: sha512-EDphs2MHJKU+iU8QbXRFvBuOD9N9cPnLk9Z0HEzXoQ5bHX5fwQadJHoIHUJCmJQYrzGfSSjfHO93DmRxtMLCsg==} + peerDependencies: + '@layerzerolabs/lz-evm-messagelib-v2': ^3.0.12 + '@layerzerolabs/lz-evm-protocol-v2': ^3.0.12 + '@layerzerolabs/lz-evm-v1-0.7': ^3.0.12 + '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 + '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 + dependencies: + '@layerzerolabs/lz-evm-messagelib-v2': 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) + '@openzeppelin/contracts': 5.1.0 + '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) + ethers: 5.7.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@layerzerolabs/oft-evm@2.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): + resolution: {integrity: sha512-S92jH/7q7DiZv6N5EGzDBh/rzzydNUa0hmGRyQ9nvNKOc+hiIRJoS4q9hhv1X69ufmPlqd+TLw14HIGDnhDbJw==} + peerDependencies: + '@layerzerolabs/lz-evm-messagelib-v2': ^3.0.12 + '@layerzerolabs/lz-evm-protocol-v2': ^3.0.12 + '@layerzerolabs/lz-evm-v1-0.7': ^3.0.12 + '@layerzerolabs/oapp-evm': ^0.2.0 + '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 + '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 + dependencies: + '@layerzerolabs/lz-evm-messagelib-v2': 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) + '@layerzerolabs/oapp-evm': 0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@openzeppelin/contracts': 5.1.0 + '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) + dev: true + /@layerzerolabs/oft-v2-solana-sdk@3.0.0(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-fiamhnEafuf83B45mDwj17rvp1BLFUmYGJEtzjp+nkKV6ZVwQwdtCXocq/5Kh8BbflEXmvpkHP+KxCHremFw6g==} dependencies: @@ -6917,6 +7063,26 @@ packages: - typescript dev: true + /@layerzerolabs/test-devtools-evm-foundry@4.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@layerzerolabs/oft-evm@2.0.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): + resolution: {integrity: sha512-NveSzu0FsjtKA4O/eRccI6mKKSEA/ErjZFUB72Y7Wj9qDqBPP1FXc683yWb0rIpM2gcNa6fYhBXI/YuS65foSw==} + peerDependencies: + '@layerzerolabs/lz-evm-messagelib-v2': ^3.0.12 + '@layerzerolabs/lz-evm-protocol-v2': ^3.0.12 + '@layerzerolabs/lz-evm-v1-0.7': ^3.0.12 + '@layerzerolabs/oapp-evm': ^0.2.0 + '@layerzerolabs/oft-evm': ^2.0.0 + '@openzeppelin/contracts': ^4.9.5 || ^5.0.0 + '@openzeppelin/contracts-upgradeable': ^4.9.5 || ^5.0.0 + dependencies: + '@layerzerolabs/lz-evm-messagelib-v2': 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) + '@layerzerolabs/oapp-evm': 0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@layerzerolabs/oft-evm': 2.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@openzeppelin/contracts': 5.1.0 + '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: From 15ea5f731b7e9ec6f2e9423912776305c8aef266 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Tue, 19 Nov 2024 19:40:14 +0700 Subject: [PATCH 09/17] chore: fix requested changes --- examples/view-pure-read/CHANGELOG.md | 7 - .../contracts/ReadViewOrPureAndCompute.sol | 238 ++++++++++++++++++ examples/view-pure-read/package.json | 2 +- .../test/foundry/ReadViewOrPure.t.sol | 7 +- .../foundry/ReadViewOrPureAndCompute.t.sol | 175 +++++++++++++ 5 files changed, 417 insertions(+), 12 deletions(-) delete mode 100644 examples/view-pure-read/CHANGELOG.md create mode 100644 examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol create mode 100644 examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol diff --git a/examples/view-pure-read/CHANGELOG.md b/examples/view-pure-read/CHANGELOG.md deleted file mode 100644 index 0f2d98474..000000000 --- a/examples/view-pure-read/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @layerzerolabs/oapp-read-example - -## 0.2.0 - -### Minor Changes - -- e2395b5: Add OApp Read Example diff --git a/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol b/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol new file mode 100644 index 000000000..a8c24c403 --- /dev/null +++ b/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// Import necessary interfaces and contracts +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + + +import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; +import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; +import { IOAppMapper } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppMapper.sol"; +import { IOAppReducer } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppReducer.sol"; +import { EVMCallRequestV1, EVMCallComputeV1, ReadCodecV1 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/ReadCodecV1.sol"; + +import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol"; +import { MessagingFee, MessagingReceipt, ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; + +/// @title IExampleContract +/// @notice Interface for the ExampleContract's `add` function. +interface IExampleContract { + function add(uint256 a, uint256 b) external pure returns (uint256); +} + +/// @title ReadViewOrPureAndCompute +/// @notice An OAppRead contract example that calls a view or pure function from another chain and performs compute operations. +contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOptionsType3 { + + /// @notice Emitted when the sum is received after compute operations. + /// + /// @param sum The final result after computation. + event SumReceived(uint256 sum); + + /// @notice LayerZero read channel ID. + uint32 public READ_CHANNEL; + + /// @notice Message type for the read operation. + uint16 public constant READ_TYPE = 1; + + /// @notice Target chain's Endpoint ID. + uint32 public targetEid; + + /// @notice Target contract address on the target chain. + address public targetContractAddress; + + /** + * @notice Constructor to initialize the OAppRead contract. + * + * @param _endpoint The LayerZero endpoint contract address. + * @param _readChannel The LayerZero read channel ID. + * @param _targetEid The target chain's Endpoint ID. + * @param _targetContractAddress The address of the contract on the target chain. + */ + constructor( + address _endpoint, + uint32 _readChannel, + uint32 _targetEid, + address _targetContractAddress + ) OAppRead(_endpoint, msg.sender) Ownable(msg.sender) { + READ_CHANNEL = _readChannel; + targetEid = _targetEid; + targetContractAddress = _targetContractAddress; + _setPeer(READ_CHANNEL, AddressCast.toBytes32(address(this))); + } + + /** + * @notice Sets the LayerZero read channel. + * + * @dev Only callable by the owner. + * + * @param _channelId The channel ID to set. + * @param _active Flag to activate or deactivate the channel. + */ + function setReadChannel(uint32 _channelId, bool _active) public override onlyOwner { + _setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0)); + READ_CHANNEL = _channelId; + } + + /** + * @notice Sends a read request to call the `add` function and performs compute operations. + * + * @dev The caller must send enough ETH to cover the messaging fee. + * + * @param a The first number. + * @param b The second number. + * @param _extraOptions Additional messaging options. + * + * @return The LayerZero messaging receipt for the request. + */ + function readSum( + uint256 a, + uint256 b, + bytes calldata _extraOptions + ) external payable returns (MessagingReceipt memory) { + bytes memory cmd = _getCmd(a, b); + return + _lzSend( + READ_CHANNEL, + cmd, + _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), + MessagingFee(msg.value, 0), + payable(msg.sender) + ); + } + + /** + * @notice Estimates the messaging fee required to perform the read and compute operation. + * + * @param a The first number to sum. + * @param b The second number to sum. + * @param _extraOptions Additional messaging options. + * + * @return fee The estimated messaging fee. + */ + function quoteReadFee( + uint256 a, + uint256 b, + bytes calldata _extraOptions + ) external view returns (MessagingFee memory fee) { + return _quote(READ_CHANNEL, _getCmd(a, b), _combineOptions(READ_CHANNEL, READ_TYPE, _extraOptions), false); + } + + /** + * @notice Constructs the read command to call the `add` function and sets up compute requests. + * + * @param a The first number. + * @param b The second number. + * + * @return The encoded command. + */ + function _getCmd(uint256 a, uint256 b) internal view returns (bytes memory) { + // Encode callData to call add(a, b) function using the function selector + bytes memory callData = abi.encodeWithSelector(IExampleContract.add.selector, a, b); + + // Create an array of EVMCallRequestV1 with a single read request + EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](2); + readRequests[0] = EVMCallRequestV1({ + appRequestLabel: 1, // Application-specific label for tracking + targetEid: targetEid, // Endpoint ID of the target chain + isBlockNum: false, // Use timestamp instead of block number + blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at + confirmations: 15, // Number of confirmations to wait for finality + to: targetContractAddress, // Address of the contract to call + callData: callData // Encoded function call data + }); + readRequests[1] = EVMCallRequestV1({ + appRequestLabel: 1, // Application-specific label for tracking + targetEid: targetEid, // Endpoint ID of the target chain + isBlockNum: false, // Use timestamp instead of block number + blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at + confirmations: 15, // Number of confirmations to wait for finality + to: targetContractAddress, // Address of the contract to call + callData: callData // Encoded function call data + }); + + // Create an EVMCallComputeV1 struct with a compute request + EVMCallComputeV1 memory computeRequest; + computeRequest = EVMCallComputeV1({ + computeSetting: 2, // Use both lzMap() and lzReduce() + targetEid: ILayerZeroEndpointV2(endpoint).eid(), // Endpoint ID of the current chain + isBlockNum: false, // Use timestamp instead of block number + blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to execute the compute at + confirmations: 15, // Number of confirmations to wait for finality + to: address(this) // Address of this contract to call lzMap and lzReduce + }); + + // Encode the command with an arbitrary appLabel, in this case 0 + return ReadCodecV1.encode(0, readRequests, computeRequest); + } + + /** + * @notice The map function called during the compute process. + * + * @dev _request The original request data. + * @param _response The response data from the read request. + * + * @return The transformed data after the map operation. + */ + function lzMap(bytes calldata /*_request*/, bytes calldata _response) external pure override returns (bytes memory) { + uint256 sum = abi.decode(_response, (uint256)); + sum += 1; // Example computation: increment the sum by 1 + return abi.encode(sum); + } + + /** + * @notice The reduce function called during the compute process. + * + * @dev _cmd The original command data. + * @param _responses The array of responses from the raw response or map functions. + * + * @return The final result after the reduce operation. + */ + function lzReduce(bytes calldata /*_cmd*/, bytes[] calldata _responses) external pure override returns (bytes memory) { + uint256 totalSum = 0; + for (uint256 i = 0; i < _responses.length; i++) { + require(_responses[i].length == 32, "Invalid response length"); + uint256 sum = abi.decode(_responses[i], (uint256)); + totalSum += sum; // Example computation: get the totalSum by adding the sum of all responses + } + return abi.encode(totalSum); + } + + /** + * @notice Handles the received sum from the target chain after compute operations. + * + * @dev This function is called internally by the LayerZero protocol. + * + * @param _message The encoded sum received. + */ + function _lzReceive( + Origin calldata, /*_origin*/ + bytes32, /*_guid*/ + bytes calldata _message, + address, /*_executor*/ + bytes calldata /*_extraData*/ + ) internal override { + // Decode the sum received + require(_message.length == 32, "Invalid message length"); + uint256 sum = abi.decode(_message, (uint256)); + emit SumReceived(sum); + } + + /** + * @notice Combines the options for messaging. + * + * @param _channelId The channel ID. + * @param _msgType The message type. + * @param _extraOptions Additional options. + * + * @return options The combined options. + */ + function _combineOptions( + uint32 _channelId, + uint16 _msgType, + bytes calldata _extraOptions + ) internal view returns (bytes memory options) { + options = combineOptions(_channelId, _msgType, _extraOptions); + } +} diff --git a/examples/view-pure-read/package.json b/examples/view-pure-read/package.json index 7d73854ec..52c95564a 100644 --- a/examples/view-pure-read/package.json +++ b/examples/view-pure-read/package.json @@ -1,6 +1,6 @@ { "name": "@layerzerolabs/view-pure-read-example", - "version": "0.2.0", + "version": "0.0.1", "private": true, "license": "MIT", "scripts": { diff --git a/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol b/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol index 5f666e526..94f31532c 100644 --- a/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol +++ b/examples/view-pure-read/test/foundry/ReadViewOrPure.t.sol @@ -112,13 +112,12 @@ contract ReadViewOrPureTest is TestHelperOz5 { */ function test_send_read() public { // Prepare messaging options - bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(1e8, 100, 0); - console.logBytes(options); + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(1e8, 32, 0); // Define the numbers to add uint256 a = 2; uint256 b = 3; - uint256 expectedSum = a + b; + uint256 expectedSum = 5; // Estimate the fee for calling readSum with arguments a and b MessagingFee memory fee = bOApp.quoteReadFee(a, b, options); @@ -136,7 +135,7 @@ contract ReadViewOrPureTest is TestHelperOz5 { addressToBytes32(address(bOApp)), 0, address(0x0), - abi.encode(expectedSum) // The sum of a and b + abi.encode(a + b) // The sum of a and b ); // Retrieve the logs to verify the SumReceived event diff --git a/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol b/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol new file mode 100644 index 000000000..f646aa83d --- /dev/null +++ b/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +// MyOApp imports +import { ReadViewOrPureAndCompute } from "../../contracts/ReadViewOrPureAndCompute.sol"; +import { ExampleContract } from "../../contracts/ExampleContract.sol"; + +// OApp imports +import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; + +// OZ imports +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +// Forge imports +import "forge-std/console.sol"; +import "forge-std/Test.sol"; + +// DevTools imports +import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; + +/** + * @title ReadViewOrPureAndComputeTest + * @notice A test suite for the ReadViewOrPureAndCompute contract. + */ +contract ReadViewOrPureAndComputeTest is TestHelperOz5 { + using OptionsBuilder for bytes; + + /// @notice Chain A Endpoint ID. + uint32 private aEid = 1; + + /// @notice Chain B Endpoint ID. + uint32 private bEid = 2; + + /// @notice The ReadViewOrPureAndCompute contract deployed on chain B. + ReadViewOrPureAndCompute private bOApp; + + /// @notice The ExampleContract deployed on chain A. + ExampleContract private exampleContract; + + /// @notice Address representing User A. + address private userA = address(0x1); + + /// @notice Message type for the read operation. + uint16 private constant READ_TYPE = 1; + + /** + * @notice Sets up the test environment before each test. + * + * @dev Deploys the ExampleContract on chain A and the ReadViewOrPureAndCompute contract on chain B. + * Wires the OApps and sets up the endpoints. + */ + function setUp() public virtual override { + vm.deal(userA, 1000 ether); + + super.setUp(); + setUpEndpoints(2, LibraryType.UltraLightNode); + + // Deploy ExampleContract on chain A (aEid) + // We simulate chain A by associating contracts with aEid + exampleContract = ExampleContract( + _deployOApp( + type(ExampleContract).creationCode, + abi.encode() // No constructor arguments needed for ExampleContract + ) + ); + + // Deploy ReadViewOrPureAndCompute on chain B (bEid) + bOApp = ReadViewOrPureAndCompute( + _deployOApp( + type(ReadViewOrPureAndCompute).creationCode, + abi.encode( + address(endpoints[bEid]), // _endpoint (LayerZero endpoint on chain B) + DEFAULT_CHANNEL_ID, // _readChannel + aEid, // _targetEid (Endpoint ID of chain A) + address(exampleContract) // _targetContractAddress (ExampleContract on chain A) + ) + ) + ); + + // Wire the OApps + address[] memory oapps = new address[](1); + oapps[0] = address(bOApp); + uint32[] memory channels = new uint32[](1); + channels[0] = DEFAULT_CHANNEL_ID; + this.wireReadOApps(oapps, channels); + } + + /** + * @notice Tests that the constructor initializes the contract correctly. + * + * @dev Verifies that the owner, endpoint, READ_CHANNEL, targetEid, and targetContractAddress are set as expected. + */ + function test_constructor() public { + // Verify that the owner is correctly set + assertEq(bOApp.owner(), address(this)); + // Verify that the endpoint is correctly set + assertEq(address(bOApp.endpoint()), address(endpoints[bEid])); + // Verify that READ_CHANNEL is correctly set + assertEq(bOApp.READ_CHANNEL(), DEFAULT_CHANNEL_ID); + // Verify that targetEid is correctly set + assertEq(bOApp.targetEid(), aEid); + // Verify that targetContractAddress is correctly set + assertEq(bOApp.targetContractAddress(), address(exampleContract)); + } + + /** + * @notice Tests sending a read request with compute and handling the received sum. + * + * @dev Simulates a user initiating a read request to add two numbers, applies compute operations, and verifies that the SumReceived event is emitted with the correct final sum. + */ + function test_send_read_with_compute() public { + // Prepare messaging options + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReadOption(1e8, 32, 0); + + // Define the numbers to add + uint256 a = 2; + uint256 b = 3; + uint256 initialSum = a + b; // Expected sum from the read request + + // Estimate the fee for calling readSum with arguments a and b + MessagingFee memory fee = bOApp.quoteReadFee(a, b, options); + + // Record logs to capture the SumReceived event + vm.recordLogs(); + + // User A initiates the read request on bOApp + vm.prank(userA); + bOApp.readSum{ value: fee.nativeFee }(a, b, options); + + // Simulate the read response from the target chain + bytes memory readResponse = abi.encode(initialSum); // The sum of a and b + + // Simulate lzMap and lzReduce as they would be called during message processing + // Note: In an actual environment, lzMap and lzReduce would be called by the LayerZero protocol + // Here, we mock this behavior in the unit test + + // Simulate lzMap operation + bytes memory mappedResponse = bOApp.lzMap("", readResponse); + + // Simulate lzReduce operation + bytes[] memory mapResponses = new bytes[](1); + mapResponses[0] = mappedResponse; + bytes memory reducedResponse = bOApp.lzReduce("", mapResponses); + + // Simulate processing the response packet to bOApp on bEid, injecting the final sum after compute operations + this.verifyPackets( + bEid, + addressToBytes32(address(bOApp)), + 0, + address(0x0), + reducedResponse // The final sum after compute operations + ); + + // Calculate the expected final sum after compute operations + uint256 afterMapSum = abi.decode(mappedResponse, (uint256)); // initialSum + 1 + uint256 finalSum = abi.decode(reducedResponse, (uint256)); // afterMapSum + 3 + + // Retrieve the logs to verify the SumReceived event + Vm.Log[] memory entries = vm.getRecordedLogs(); + bool found = false; + uint256 sumReceived; + for (uint256 i = 0; i < entries.length; i++) { + Vm.Log memory entry = entries[i]; + if (entry.topics[0] == keccak256("SumReceived(uint256)")) { + sumReceived = abi.decode(entry.data, (uint256)); + found = true; + break; + } + } + require(found, "SumReceived event not found"); + assertEq(sumReceived, initialSum + 1, "Sum received does not match expected value"); + } +} From 5af71eb20eb01d86582518f1105d181f3954ea35 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Tue, 19 Nov 2024 19:48:37 +0700 Subject: [PATCH 10/17] chore: update linter --- .../contracts/ReadViewOrPureAndCompute.sol | 27 ++++++++----------- .../foundry/ReadViewOrPureAndCompute.t.sol | 12 +++------ 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol b/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol index a8c24c403..fbaae17dc 100644 --- a/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol +++ b/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.20; // Import necessary interfaces and contracts import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; - import { Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; import { OAppRead } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppRead.sol"; @@ -24,7 +23,6 @@ interface IExampleContract { /// @title ReadViewOrPureAndCompute /// @notice An OAppRead contract example that calls a view or pure function from another chain and performs compute operations. contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOptionsType3 { - /// @notice Emitted when the sum is received after compute operations. /// /// @param sum The final result after computation. @@ -142,15 +140,6 @@ contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOp to: targetContractAddress, // Address of the contract to call callData: callData // Encoded function call data }); - readRequests[1] = EVMCallRequestV1({ - appRequestLabel: 1, // Application-specific label for tracking - targetEid: targetEid, // Endpoint ID of the target chain - isBlockNum: false, // Use timestamp instead of block number - blockNumOrTimestamp: uint64(block.timestamp), // Timestamp to read the state at - confirmations: 15, // Number of confirmations to wait for finality - to: targetContractAddress, // Address of the contract to call - callData: callData // Encoded function call data - }); // Create an EVMCallComputeV1 struct with a compute request EVMCallComputeV1 memory computeRequest; @@ -175,7 +164,10 @@ contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOp * * @return The transformed data after the map operation. */ - function lzMap(bytes calldata /*_request*/, bytes calldata _response) external pure override returns (bytes memory) { + function lzMap( + bytes calldata /*_request*/, + bytes calldata _response + ) external pure override returns (bytes memory) { uint256 sum = abi.decode(_response, (uint256)); sum += 1; // Example computation: increment the sum by 1 return abi.encode(sum); @@ -189,7 +181,10 @@ contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOp * * @return The final result after the reduce operation. */ - function lzReduce(bytes calldata /*_cmd*/, bytes[] calldata _responses) external pure override returns (bytes memory) { + function lzReduce( + bytes calldata /*_cmd*/, + bytes[] calldata _responses + ) external pure override returns (bytes memory) { uint256 totalSum = 0; for (uint256 i = 0; i < _responses.length; i++) { require(_responses[i].length == 32, "Invalid response length"); @@ -207,10 +202,10 @@ contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOp * @param _message The encoded sum received. */ function _lzReceive( - Origin calldata, /*_origin*/ - bytes32, /*_guid*/ + Origin calldata /*_origin*/, + bytes32 /*_guid*/, bytes calldata _message, - address, /*_executor*/ + address /*_executor*/, bytes calldata /*_extraData*/ ) internal override { // Decode the sum received diff --git a/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol b/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol index f646aa83d..777cdbe8a 100644 --- a/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol +++ b/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol @@ -71,10 +71,10 @@ contract ReadViewOrPureAndComputeTest is TestHelperOz5 { _deployOApp( type(ReadViewOrPureAndCompute).creationCode, abi.encode( - address(endpoints[bEid]), // _endpoint (LayerZero endpoint on chain B) - DEFAULT_CHANNEL_ID, // _readChannel - aEid, // _targetEid (Endpoint ID of chain A) - address(exampleContract) // _targetContractAddress (ExampleContract on chain A) + address(endpoints[bEid]), // _endpoint (LayerZero endpoint on chain B) + DEFAULT_CHANNEL_ID, // _readChannel + aEid, // _targetEid (Endpoint ID of chain A) + address(exampleContract) // _targetContractAddress (ExampleContract on chain A) ) ) ); @@ -153,10 +153,6 @@ contract ReadViewOrPureAndComputeTest is TestHelperOz5 { reducedResponse // The final sum after compute operations ); - // Calculate the expected final sum after compute operations - uint256 afterMapSum = abi.decode(mappedResponse, (uint256)); // initialSum + 1 - uint256 finalSum = abi.decode(reducedResponse, (uint256)); // afterMapSum + 3 - // Retrieve the logs to verify the SumReceived event Vm.Log[] memory entries = vm.getRecordedLogs(); bool found = false; From 65b4d759cb9485c125547ef58da773bb19ed6441 Mon Sep 17 00:00:00 2001 From: ravinagill15 Date: Mon, 18 Nov 2024 15:34:45 -0800 Subject: [PATCH 11/17] =?UTF-8?q?=F0=9F=A7=B9=20=20Updating=20OmniTransact?= =?UTF-8?q?ion=20properties=20for=20logs=20(#991)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/spicy-bats-shout.md | 5 +++++ pnpm-lock.yaml | 40 ---------------------------------- 2 files changed, 5 insertions(+), 40 deletions(-) create mode 100644 .changeset/spicy-bats-shout.md diff --git a/.changeset/spicy-bats-shout.md b/.changeset/spicy-bats-shout.md new file mode 100644 index 000000000..551641222 --- /dev/null +++ b/.changeset/spicy-bats-shout.md @@ -0,0 +1,5 @@ +--- +"@layerzerolabs/devtools": patch +--- + +Adding optional metadata property to OmniTransaction to improve logging diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7fae9f79..d6b01634d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6601,34 +6601,6 @@ packages: solidity-bytes-utils: 0.8.2 dev: true - /@layerzerolabs/lz-evm-messagelib-v2@3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2): - resolution: {integrity: sha512-nDSaBOwDXjNhmCROnHb7xsrU9AfAgZGhlXIMwYudo+ML4Qz8B5NcAsxxByaK7cxtsDdsUiZRDJBNUociMNkPrA==} - peerDependencies: - '@arbitrum/nitro-contracts': ^1.1.0 - '@axelar-network/axelar-gmp-sdk-solidity': ^5.6.4 - '@chainlink/contracts-ccip': ^0.7.6 - '@eth-optimism/contracts': ^0.6.0 - '@layerzerolabs/lz-evm-protocol-v2': ^3.0.12 - '@layerzerolabs/lz-evm-v1-0.7': ^3.0.12 - '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 - '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 - hardhat-deploy: ^0.12.1 - solidity-bytes-utils: ^0.8.0 - peerDependenciesMeta: - '@arbitrum/nitro-contracts': - optional: true - dependencies: - '@axelar-network/axelar-gmp-sdk-solidity': 5.10.0 - '@chainlink/contracts-ccip': 0.7.6(ethers@5.7.2) - '@eth-optimism/contracts': 0.6.0(ethers@5.7.2) - '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) - '@openzeppelin/contracts': 5.1.0 - '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) - hardhat-deploy: 0.12.4 - solidity-bytes-utils: 0.8.2 - dev: true - /@layerzerolabs/lz-evm-oapp-v1@3.0.12(@layerzerolabs/lz-evm-v1-0.7@3.0.12)(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.1)(solidity-bytes-utils@0.8.2): resolution: {integrity: sha512-+c6IfSZAlQliCgkaqISYFEca4j55MWkO3wPGgLPnaNLm7zR6qPobU7nE/q8pSYQtyLPlhBpV+tNyVfM8dqUCrw==} peerDependencies: @@ -6890,18 +6862,6 @@ packages: hardhat-deploy: 0.12.1 dev: true - /@layerzerolabs/lz-evm-v1-0.7@3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4): - resolution: {integrity: sha512-UBKv054WdANywkVeXKb+SEEXyD/bo1ztfIIvGCtvOgNUetqM1QQFFwzlC8Usf+/MFLpq9cSLj1lF4IuzuZes5Q==} - peerDependencies: - '@openzeppelin/contracts': 3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0 - '@openzeppelin/contracts-upgradeable': 3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0 - hardhat-deploy: ^0.12.1 - dependencies: - '@openzeppelin/contracts': 5.1.0 - '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) - hardhat-deploy: 0.12.4 - dev: true - /@layerzerolabs/lz-foundation@3.0.0: resolution: {integrity: sha512-vYuuZAfIaRQUEGaKnV4dCOiFTENhfc2w+Fcnza/T7+O/JIKL2p/GqdxcC21HqrMNdLGMmSWnUqLlou+eCk3ROQ==} dependencies: From 4c7a39cefff0039ee46c731db280b8ad1e365463 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 17 Nov 2024 09:40:08 +0700 Subject: [PATCH 12/17] chore: re-install and build --- pnpm-lock.yaml | 81 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6b01634d..568ec7d2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3010,7 +3010,7 @@ importers: version: 2.16.2 ink: specifier: ^3.2.0 - version: 3.2.0(@types/react@17.0.75)(react@17.0.2) + version: 3.2.0(react@17.0.2) ink-gradient: specifier: ^2.0.0 version: 2.0.0(ink@3.2.0)(react@17.0.2) @@ -6601,6 +6601,34 @@ packages: solidity-bytes-utils: 0.8.2 dev: true + /@layerzerolabs/lz-evm-messagelib-v2@3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2): + resolution: {integrity: sha512-nDSaBOwDXjNhmCROnHb7xsrU9AfAgZGhlXIMwYudo+ML4Qz8B5NcAsxxByaK7cxtsDdsUiZRDJBNUociMNkPrA==} + peerDependencies: + '@arbitrum/nitro-contracts': ^1.1.0 + '@axelar-network/axelar-gmp-sdk-solidity': ^5.6.4 + '@chainlink/contracts-ccip': ^0.7.6 + '@eth-optimism/contracts': ^0.6.0 + '@layerzerolabs/lz-evm-protocol-v2': ^3.0.12 + '@layerzerolabs/lz-evm-v1-0.7': ^3.0.12 + '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 + '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 + hardhat-deploy: ^0.12.1 + solidity-bytes-utils: ^0.8.0 + peerDependenciesMeta: + '@arbitrum/nitro-contracts': + optional: true + dependencies: + '@axelar-network/axelar-gmp-sdk-solidity': 5.10.0 + '@chainlink/contracts-ccip': 0.7.6(ethers@5.7.2) + '@eth-optimism/contracts': 0.6.0(ethers@5.7.2) + '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) + '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) + '@openzeppelin/contracts': 5.1.0 + '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) + hardhat-deploy: 0.12.4 + solidity-bytes-utils: 0.8.2 + dev: true + /@layerzerolabs/lz-evm-oapp-v1@3.0.12(@layerzerolabs/lz-evm-v1-0.7@3.0.12)(@openzeppelin/contracts@5.0.2)(hardhat-deploy@0.12.1)(solidity-bytes-utils@0.8.2): resolution: {integrity: sha512-+c6IfSZAlQliCgkaqISYFEca4j55MWkO3wPGgLPnaNLm7zR6qPobU7nE/q8pSYQtyLPlhBpV+tNyVfM8dqUCrw==} peerDependencies: @@ -6862,6 +6890,18 @@ packages: hardhat-deploy: 0.12.1 dev: true + /@layerzerolabs/lz-evm-v1-0.7@3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4): + resolution: {integrity: sha512-UBKv054WdANywkVeXKb+SEEXyD/bo1ztfIIvGCtvOgNUetqM1QQFFwzlC8Usf+/MFLpq9cSLj1lF4IuzuZes5Q==} + peerDependencies: + '@openzeppelin/contracts': 3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0 + '@openzeppelin/contracts-upgradeable': 3.4.2-solc-0.7 || ^3.4.2 || ^4.0.0 || ^5.0.0 + hardhat-deploy: ^0.12.1 + dependencies: + '@openzeppelin/contracts': 5.1.0 + '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) + hardhat-deploy: 0.12.4 + dev: true + /@layerzerolabs/lz-foundation@3.0.0: resolution: {integrity: sha512-vYuuZAfIaRQUEGaKnV4dCOiFTENhfc2w+Fcnza/T7+O/JIKL2p/GqdxcC21HqrMNdLGMmSWnUqLlou+eCk3ROQ==} dependencies: @@ -13933,6 +13973,45 @@ packages: - bufferutil - utf-8-validate + /ink@3.2.0(react@17.0.2): + resolution: {integrity: sha512-firNp1q3xxTzoItj/eOOSZQnYSlyrWks5llCTVX37nJ59K3eXbQ8PtzCguqo8YI19EELo5QxaKnJd4VxzhU8tg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '>=16.8.0' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + ansi-escapes: 4.3.2 + auto-bind: 4.0.0 + chalk: 4.1.2 + cli-boxes: 2.2.1 + cli-cursor: 3.1.0 + cli-truncate: 2.1.0 + code-excerpt: 3.0.0 + indent-string: 4.0.0 + is-ci: 2.0.0 + lodash: 4.17.21 + patch-console: 1.0.0 + react: 17.0.2 + react-devtools-core: 4.28.5 + react-reconciler: 0.26.2(react@17.0.2) + scheduler: 0.20.2 + signal-exit: 3.0.7 + slice-ansi: 3.0.0 + stack-utils: 2.0.6 + string-width: 4.2.3 + type-fest: 0.12.0 + widest-line: 3.1.0 + wrap-ansi: 6.2.0 + ws: 7.5.10 + yoga-layout-prebuilt: 1.10.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /internal-slot@1.0.6: resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} engines: {node: '>= 0.4'} From a8097d61421cbd3d489c2ad4ded09795a8ab98bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yargo=20V=C3=B3=20Tessaro?= Date: Tue, 19 Nov 2024 20:15:49 +0000 Subject: [PATCH 13/17] Fix test --- examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol b/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol index fbaae17dc..d1b8a1fe6 100644 --- a/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol +++ b/examples/view-pure-read/contracts/ReadViewOrPureAndCompute.sol @@ -130,7 +130,7 @@ contract ReadViewOrPureAndCompute is OAppRead, IOAppMapper, IOAppReducer, OAppOp bytes memory callData = abi.encodeWithSelector(IExampleContract.add.selector, a, b); // Create an array of EVMCallRequestV1 with a single read request - EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](2); + EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](1); readRequests[0] = EVMCallRequestV1({ appRequestLabel: 1, // Application-specific label for tracking targetEid: targetEid, // Endpoint ID of the target chain From 34ed249ee4b422f340331d074b0fa29c6ed5432c Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Wed, 20 Nov 2024 12:48:55 +0700 Subject: [PATCH 14/17] chore: update code comment --- .../view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol b/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol index 777cdbe8a..a824a8872 100644 --- a/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol +++ b/examples/view-pure-read/test/foundry/ReadViewOrPureAndCompute.t.sol @@ -133,7 +133,7 @@ contract ReadViewOrPureAndComputeTest is TestHelperOz5 { bytes memory readResponse = abi.encode(initialSum); // The sum of a and b // Simulate lzMap and lzReduce as they would be called during message processing - // Note: In an actual environment, lzMap and lzReduce would be called by the LayerZero protocol + // Note: In an actual environment, lzMap and lzReduce would be called by the configured DVNs // Here, we mock this behavior in the unit test // Simulate lzMap operation From 972871a674d55e0313cf9d4f734007e7c4f4ba19 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 24 Nov 2024 12:11:29 +0700 Subject: [PATCH 15/17] chore: update package.json to latest oapp version --- examples/view-pure-read/package.json | 2 +- pnpm-lock.yaml | 77 ++++------------------------ 2 files changed, 10 insertions(+), 69 deletions(-) diff --git a/examples/view-pure-read/package.json b/examples/view-pure-read/package.json index 52c95564a..5cd66754c 100644 --- a/examples/view-pure-read/package.json +++ b/examples/view-pure-read/package.json @@ -28,7 +28,7 @@ "@layerzerolabs/lz-evm-protocol-v2": "^3.0.12", "@layerzerolabs/lz-evm-v1-0.7": "^3.0.12", "@layerzerolabs/lz-v2-utilities": "^3.0.12", - "@layerzerolabs/oapp-evm": "^0.2.0", + "@layerzerolabs/oapp-evm": "^0.3.0", "@layerzerolabs/prettier-config-next": "^2.3.39", "@layerzerolabs/solhint-config": "^3.0.12", "@layerzerolabs/test-devtools-evm-foundry": "~4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 568ec7d2d..9559f9361 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1371,8 +1371,8 @@ importers: specifier: ^3.0.12 version: 3.0.12 '@layerzerolabs/oapp-evm': - specifier: ^0.2.0 - version: 0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + specifier: ^0.3.0 + version: link:../../packages/oapp-evm '@layerzerolabs/prettier-config-next': specifier: ^2.3.39 version: 2.3.44 @@ -1381,7 +1381,7 @@ importers: version: 3.0.12(typescript@5.5.3) '@layerzerolabs/test-devtools-evm-foundry': specifier: ~4.0.0 - version: 4.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@layerzerolabs/oft-evm@2.0.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + version: 4.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@packages+oapp-evm)(@layerzerolabs/oft-evm@2.0.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) '@layerzerolabs/toolbox-foundry': specifier: ~0.1.9 version: link:../../packages/toolbox-foundry @@ -3010,7 +3010,7 @@ importers: version: 2.16.2 ink: specifier: ^3.2.0 - version: 3.2.0(react@17.0.2) + version: 3.2.0(@types/react@17.0.75)(react@17.0.2) ink-gradient: specifier: ^2.0.0 version: 2.0.0(ink@3.2.0)(react@17.0.2) @@ -6985,27 +6985,7 @@ packages: bs58: 5.0.0 tiny-invariant: 1.3.3 - /@layerzerolabs/oapp-evm@0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): - resolution: {integrity: sha512-EDphs2MHJKU+iU8QbXRFvBuOD9N9cPnLk9Z0HEzXoQ5bHX5fwQadJHoIHUJCmJQYrzGfSSjfHO93DmRxtMLCsg==} - peerDependencies: - '@layerzerolabs/lz-evm-messagelib-v2': ^3.0.12 - '@layerzerolabs/lz-evm-protocol-v2': ^3.0.12 - '@layerzerolabs/lz-evm-v1-0.7': ^3.0.12 - '@openzeppelin/contracts': ^4.8.1 || ^5.0.0 - '@openzeppelin/contracts-upgradeable': ^4.8.1 || ^5.0.0 - dependencies: - '@layerzerolabs/lz-evm-messagelib-v2': 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) - '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) - '@openzeppelin/contracts': 5.1.0 - '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) - ethers: 5.7.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: true - - /@layerzerolabs/oft-evm@2.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): + /@layerzerolabs/oft-evm@2.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@packages+oapp-evm)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): resolution: {integrity: sha512-S92jH/7q7DiZv6N5EGzDBh/rzzydNUa0hmGRyQ9nvNKOc+hiIRJoS4q9hhv1X69ufmPlqd+TLw14HIGDnhDbJw==} peerDependencies: '@layerzerolabs/lz-evm-messagelib-v2': ^3.0.12 @@ -7018,7 +6998,7 @@ packages: '@layerzerolabs/lz-evm-messagelib-v2': 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) - '@layerzerolabs/oapp-evm': 0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@layerzerolabs/oapp-evm': link:packages/oapp-evm '@openzeppelin/contracts': 5.1.0 '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) dev: true @@ -7063,7 +7043,7 @@ packages: - typescript dev: true - /@layerzerolabs/test-devtools-evm-foundry@4.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@layerzerolabs/oft-evm@2.0.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): + /@layerzerolabs/test-devtools-evm-foundry@4.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@packages+oapp-evm)(@layerzerolabs/oft-evm@2.0.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0): resolution: {integrity: sha512-NveSzu0FsjtKA4O/eRccI6mKKSEA/ErjZFUB72Y7Wj9qDqBPP1FXc683yWb0rIpM2gcNa6fYhBXI/YuS65foSw==} peerDependencies: '@layerzerolabs/lz-evm-messagelib-v2': ^3.0.12 @@ -7077,8 +7057,8 @@ packages: '@layerzerolabs/lz-evm-messagelib-v2': 3.0.12(@axelar-network/axelar-gmp-sdk-solidity@5.10.0)(@chainlink/contracts-ccip@0.7.6)(@eth-optimism/contracts@0.6.0)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) '@layerzerolabs/lz-evm-protocol-v2': 3.0.12(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4)(solidity-bytes-utils@0.8.2) '@layerzerolabs/lz-evm-v1-0.7': 3.0.15(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0)(hardhat-deploy@0.12.4) - '@layerzerolabs/oapp-evm': 0.2.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) - '@layerzerolabs/oft-evm': 2.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@0.2.0)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) + '@layerzerolabs/oapp-evm': link:packages/oapp-evm + '@layerzerolabs/oft-evm': 2.0.0(@layerzerolabs/lz-evm-messagelib-v2@3.0.12)(@layerzerolabs/lz-evm-protocol-v2@3.0.12)(@layerzerolabs/lz-evm-v1-0.7@3.0.15)(@layerzerolabs/oapp-evm@packages+oapp-evm)(@openzeppelin/contracts-upgradeable@5.1.0)(@openzeppelin/contracts@5.1.0) '@openzeppelin/contracts': 5.1.0 '@openzeppelin/contracts-upgradeable': 5.1.0(@openzeppelin/contracts@5.1.0) dev: true @@ -13973,45 +13953,6 @@ packages: - bufferutil - utf-8-validate - /ink@3.2.0(react@17.0.2): - resolution: {integrity: sha512-firNp1q3xxTzoItj/eOOSZQnYSlyrWks5llCTVX37nJ59K3eXbQ8PtzCguqo8YI19EELo5QxaKnJd4VxzhU8tg==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '>=16.8.0' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - ansi-escapes: 4.3.2 - auto-bind: 4.0.0 - chalk: 4.1.2 - cli-boxes: 2.2.1 - cli-cursor: 3.1.0 - cli-truncate: 2.1.0 - code-excerpt: 3.0.0 - indent-string: 4.0.0 - is-ci: 2.0.0 - lodash: 4.17.21 - patch-console: 1.0.0 - react: 17.0.2 - react-devtools-core: 4.28.5 - react-reconciler: 0.26.2(react@17.0.2) - scheduler: 0.20.2 - signal-exit: 3.0.7 - slice-ansi: 3.0.0 - stack-utils: 2.0.6 - string-width: 4.2.3 - type-fest: 0.12.0 - widest-line: 3.1.0 - wrap-ansi: 6.2.0 - ws: 7.5.10 - yoga-layout-prebuilt: 1.10.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - /internal-slot@1.0.6: resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} engines: {node: '>= 0.4'} From 1562aa69ab179ac25d5ccc6a3bfe67d681bd38c5 Mon Sep 17 00:00:00 2001 From: Matthew Krak Date: Sun, 24 Nov 2024 12:23:08 +0700 Subject: [PATCH 16/17] chore: remove hardhat test --- .../oapp-read/test/hardhat/MyOAppRead.test.ts | 107 ------------------ 1 file changed, 107 deletions(-) delete mode 100644 examples/oapp-read/test/hardhat/MyOAppRead.test.ts diff --git a/examples/oapp-read/test/hardhat/MyOAppRead.test.ts b/examples/oapp-read/test/hardhat/MyOAppRead.test.ts deleted file mode 100644 index 362c3d635..000000000 --- a/examples/oapp-read/test/hardhat/MyOAppRead.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { Contract, ContractFactory } from 'ethers' -import { defaultAbiCoder, toUtf8Bytes } from 'ethers/lib/utils' -import { deployments, ethers } from 'hardhat' - -import { ComputeSetting, Options } from '@layerzerolabs/lz-v2-utilities' - -describe('MyOApp Test', function () { - // Constant representing a mock Endpoint ID for testing purposes - const eidA = 1 - const eidB = 2 - const channelId = 1001 - // Declaration of variables to be used in the test suite - let MyOAppRead: ContractFactory - let EndpointV2Mock: ContractFactory - let ownerA: SignerWithAddress - let ownerB: SignerWithAddress - let endpointOwner: SignerWithAddress - let myOAppReadA: Contract - let myOAppReadB: Contract - let mockEndpointV2A: Contract - let mockEndpointV2B: Contract - - // Before hook for setup that runs once before all tests in the block - before(async function () { - // Contract factory for our tested contract - MyOAppRead = await ethers.getContractFactory('MyOAppRead') - - // Fetching the first three signers (accounts) from Hardhat's local Ethereum network - const signers = await ethers.getSigners() - - ;[ownerA, ownerB, endpointOwner] = signers - - // The EndpointV2Mock contract comes from @layerzerolabs/test-devtools-evm-hardhat package - // and its artifacts are connected as external artifacts to this project - // - // Unfortunately, hardhat itself does not yet provide a way of connecting external artifacts, - // so we rely on hardhat-deploy to create a ContractFactory for EndpointV2Mock - // - // See https://github.com/NomicFoundation/hardhat/issues/1040 - const EndpointV2MockArtifact = await deployments.getArtifact('EndpointV2Mock') - EndpointV2Mock = new ContractFactory(EndpointV2MockArtifact.abi, EndpointV2MockArtifact.bytecode, endpointOwner) - }) - - // beforeEach hook for setup that runs before each test in the block - beforeEach(async function () { - // Deploying a mock LZ EndpointV2 with the given Endpoint ID - mockEndpointV2A = await EndpointV2Mock.deploy(eidA) - mockEndpointV2B = await EndpointV2Mock.deploy(eidB) - - // Deploying two instances of MyOAppRead contract and linking them to the mock LZEndpoint - myOAppReadA = await MyOAppRead.deploy(mockEndpointV2A.address, ownerA.address, 'oAppA') - myOAppReadB = await MyOAppRead.deploy(mockEndpointV2B.address, ownerB.address, 'oAppB') - - // Setting destination endpoints in the LZEndpoint mock for each MyOApp instance - await mockEndpointV2A.setDestLzEndpoint(myOAppReadB.address, mockEndpointV2B.address) - await mockEndpointV2B.setDestLzEndpoint(myOAppReadA.address, mockEndpointV2A.address) - - // Setting each MyOApp instance as a peer of the other - await myOAppReadA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(myOAppReadB.address, 32)) - await myOAppReadB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(myOAppReadA.address, 32)) - - // Setting read channel and configuration only for MyOAppReadA - await mockEndpointV2A.setDestLzEndpoint(myOAppReadA.address, mockEndpointV2A.address) - await mockEndpointV2A.setReadChannelId(channelId) - - await myOAppReadA.connect(ownerA).setReadChannel(channelId, true) - }) - - // A test case to verify message sending functionality - it('should send a message to each destination OApp', async function () { - // Assert initial state of data in both MyOApp instances - expect(await myOAppReadA.data()).to.equal( - defaultAbiCoder.encode(['bytes'], [toUtf8Bytes('Nothing received yet.')]) - ) - expect(await myOAppReadB.data()).to.equal( - defaultAbiCoder.encode(['bytes'], [toUtf8Bytes('Nothing received yet.')]) - ) - const options = Options.newOptions().addExecutorLzReadOption(500000, 100, 0).toHex().toString() - - // Initialize command options - const currentBlockNum = await ethers.provider.getBlockNumber() - const evmReadRequest = [1, eidA, true, currentBlockNum, 1, myOAppReadA.address] - const evmComputeRequest = [ComputeSetting.MapReduce, eidA, true, currentBlockNum, 1, myOAppReadA.address] - - // Define native fee and quote for the message send operation - const [nativeFee] = await myOAppReadA.quote(channelId, 1, [evmReadRequest], evmComputeRequest, options, false) - - // Execute send operation from myOAppReadA with expected response - await mockEndpointV2A.setReadResponse( - myOAppReadA.address, - defaultAbiCoder.encode(['bytes'], [toUtf8Bytes('Test read message.')]) - ) - await myOAppReadA.send(channelId, 1, [evmReadRequest], evmComputeRequest, options, { - value: nativeFee.toString(), - }) - - // Assert the resulting state of data in both MyOApp instances - expect(await myOAppReadA.data()).to.equal( - defaultAbiCoder.encode(['bytes'], [toUtf8Bytes('Test read message.')]) - ) - expect(await myOAppReadB.data()).to.equal( - defaultAbiCoder.encode(['bytes'], [toUtf8Bytes('Nothing received yet.')]) - ) - }) -}) From ac7d8db2cad2206cd4b95b95d85d8fb4bea2cfb7 Mon Sep 17 00:00:00 2001 From: Ryan Goulding Date: Tue, 26 Nov 2024 11:53:25 -0500 Subject: [PATCH 17/17] fix pnpm-lock.yaml (#1083) Signed-off-by: Ryan Goulding --- pnpm-lock.yaml | 75 ++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 57 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9559f9361..a14f590ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,7 +40,7 @@ importers: version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0) eslint-plugin-jest: specifier: ^27.6.3 version: 27.6.3(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.0)(typescript@5.5.3) @@ -1538,7 +1538,7 @@ importers: version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -1672,7 +1672,7 @@ importers: version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -1785,7 +1785,7 @@ importers: version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -2775,7 +2775,7 @@ importers: version: 2.6.2 tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -2857,7 +2857,7 @@ importers: version: 2.6.2 tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -2881,7 +2881,7 @@ importers: version: 2.6.3 tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -3902,7 +3902,7 @@ importers: version: 2.6.2 tsup: specifier: ~8.0.1 - version: 8.0.1(ts-node@10.9.2)(typescript@5.5.3) + version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3) typescript: specifier: ^5.4.4 version: 5.5.3 @@ -6290,7 +6290,7 @@ packages: eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) eslint-plugin-autofix: 2.2.0(eslint@8.57.0) eslint-plugin-compat: 4.2.0(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0) eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5) eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.0) prettier: 3.2.5 @@ -11785,7 +11785,7 @@ packages: eslint-plugin-promise: ^6.0.0 dependencies: eslint: 8.57.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0) eslint-plugin-n: 16.6.2(eslint@8.57.0) eslint-plugin-promise: 6.1.1(eslint@8.57.0) dev: true @@ -11810,8 +11810,8 @@ packages: debug: 4.3.5 enhanced-resolve: 5.16.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.3 is-core-module: 2.13.1 @@ -11823,7 +11823,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -11848,12 +11848,11 @@ packages: debug: 3.2.7 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: @@ -11877,6 +11876,7 @@ packages: '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) debug: 3.2.7 eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -11934,7 +11934,7 @@ packages: regexpp: 3.2.0 dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -11953,7 +11953,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -18335,7 +18335,7 @@ packages: bundle-require: 4.0.2(esbuild@0.19.11) cac: 6.7.14 chokidar: 3.6.0 - debug: 4.3.7 + debug: 4.3.5 esbuild: 0.19.11 execa: 5.1.1 globby: 11.1.0 @@ -18392,45 +18392,6 @@ packages: - ts-node dev: true - /tsup@8.0.1(ts-node@10.9.2)(typescript@5.5.3): - resolution: {integrity: sha512-hvW7gUSG96j53ZTSlT4j/KL0q1Q2l6TqGBFc6/mu/L46IoNWqLLUzLRLP1R8Q7xrJTmkDxxDoojV5uCVs1sVOg==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - '@microsoft/api-extractor': ^7.36.0 - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: '>=4.5.0' - peerDependenciesMeta: - '@microsoft/api-extractor': - optional: true - '@swc/core': - optional: true - postcss: - optional: true - typescript: - optional: true - dependencies: - bundle-require: 4.0.2(esbuild@0.19.11) - cac: 6.7.14 - chokidar: 3.6.0 - debug: 4.3.5 - esbuild: 0.19.11 - execa: 5.1.1 - globby: 11.1.0 - joycon: 3.1.1 - postcss-load-config: 4.0.2(ts-node@10.9.2) - resolve-from: 5.0.0 - rollup: 4.9.6 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tree-kill: 1.2.2 - typescript: 5.5.3 - transitivePeerDependencies: - - supports-color - - ts-node - dev: true - /tsutils@3.21.0(typescript@5.5.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'}