Skip to content

Commit

Permalink
🗞️ OApp Read String Example (#1019)
Browse files Browse the repository at this point in the history
  • Loading branch information
yargo13 authored Nov 14, 2024
1 parent 2fd45ca commit e2395b5
Show file tree
Hide file tree
Showing 35 changed files with 2,187 additions and 87 deletions.
17 changes: 17 additions & 0 deletions .changeset/silly-schools-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"@layerzerolabs/test-devtools-evm-foundry": minor
"@layerzerolabs/test-devtools-evm-hardhat": minor
"@layerzerolabs/ua-devtools-evm-hardhat-test": minor
"@layerzerolabs/native-oft-adapter-example": minor
"@layerzerolabs/oft-upgradeable-example": minor
"create-lz-oapp": minor
"@layerzerolabs/oft-adapter-example": minor
"@layerzerolabs/oft-solana-example": minor
"@layerzerolabs/oapp-read-example": minor
"@layerzerolabs/oapp-evm": minor
"@layerzerolabs/onft721-example": minor
"@layerzerolabs/oapp-example": minor
"@layerzerolabs/oft-example": minor
---

Add OApp Read Example
4 changes: 4 additions & 0 deletions examples/native-oft-adapter/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const config: HardhatUserConfig = {
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: {
Expand Down
15 changes: 15 additions & 0 deletions examples/oapp-read/.env.example
Original file line number Diff line number Diff line change
@@ -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=
10 changes: 10 additions & 0 deletions examples/oapp-read/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
artifacts
cache
dist
node_modules
out
*.log
*.sol
*.yaml
*.lock
package-lock.json
10 changes: 10 additions & 0 deletions examples/oapp-read/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -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',
},
};
23 changes: 23 additions & 0 deletions examples/oapp-read/.gitignore
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions examples/oapp-read/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v18.18.0
10 changes: 10 additions & 0 deletions examples/oapp-read/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
artifacts/
cache/
dist/
node_modules/
out/
*.log
*ignore
*.yaml
*.lock
package-lock.json
3 changes: 3 additions & 0 deletions examples/oapp-read/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
...require('@layerzerolabs/prettier-config-next'),
};
630 changes: 630 additions & 0 deletions examples/oapp-read/README.md

Large diffs are not rendered by default.

173 changes: 173 additions & 0 deletions examples/oapp-read/contracts/MyOAppRead.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// 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);
}
}
53 changes: 53 additions & 0 deletions examples/oapp-read/deploy/MyOAppRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import assert from 'assert'

import { type DeployFunction } from 'hardhat-deploy/types'

// TODO declare your contract name here
const contractName = 'MyOAppRead'

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 { address } = await deploy(contractName, {
from: deployer,
args: [
endpointV2Deployment.address, // LayerZero's EndpointV2 address
deployer, // owner
`oAppRead-${hre.network.name}`,
],
log: true,
skipIfAlreadyDeployed: false,
})

console.log(`Deployed contract: ${contractName}, network: ${hre.network.name}, address: ${address}`)
}

deploy.tags = [contractName]

export default deploy
27 changes: 27 additions & 0 deletions examples/oapp-read/foundry.toml
Original file line number Diff line number Diff line change
@@ -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/',
]
Loading

0 comments on commit e2395b5

Please sign in to comment.