@layerzerolabs/protocol-v2-oapp-evm-contracts 1.2.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/contracts/oapp/OApp.sol +39 -0
- package/contracts/oapp/OAppCore.sol +83 -0
- package/contracts/oapp/OAppRead.sol +18 -0
- package/contracts/oapp/OAppReceiver.sol +122 -0
- package/contracts/oapp/OAppSender.sol +124 -0
- package/contracts/oapp/examples/CmdCodecV1Mock.sol +31 -0
- package/contracts/oapp/examples/LzReadCounter.sol +160 -0
- package/contracts/oapp/examples/OmniCounter.sol +15 -0
- package/contracts/oapp/examples/OmniCounterAbstract.sol +285 -0
- package/contracts/oapp/examples/OmniCounterAlt.sol +90 -0
- package/contracts/oapp/examples/OmniCounterPreCrime.sol +102 -0
- package/contracts/oapp/interfaces/IOAppComposer.sol +12 -0
- package/contracts/oapp/interfaces/IOAppComputer.sol +8 -0
- package/contracts/oapp/interfaces/IOAppComputerMap.sol +7 -0
- package/contracts/oapp/interfaces/IOAppComputerReduce.sol +7 -0
- package/contracts/oapp/interfaces/IOAppCore.sol +52 -0
- package/contracts/oapp/interfaces/IOAppMsgInspector.sol +22 -0
- package/contracts/oapp/interfaces/IOAppOptionsType3.sol +43 -0
- package/contracts/oapp/interfaces/IOAppReceiver.sol +25 -0
- package/contracts/oapp/libs/OAppOptionsType3.sol +98 -0
- package/contracts/oapp/libs/OptionsBuilder.sol +218 -0
- package/contracts/oapp/libs/ReadCmdCodecV1.sol +231 -0
- package/contracts/oapp/utils/RateLimiter.sol +229 -0
- package/contracts/oft/OFT.sol +89 -0
- package/contracts/oft/OFTAdapter.sol +106 -0
- package/contracts/oft/OFTCore.sol +399 -0
- package/contracts/oft/OFTPrecrime.sol +99 -0
- package/contracts/oft/interfaces/IOFT.sol +149 -0
- package/contracts/oft/libs/OFTComposeMsgCodec.sol +91 -0
- package/contracts/oft/libs/OFTMsgCodec.sol +83 -0
- package/contracts/precrime/OAppPreCrimeSimulator.sol +125 -0
- package/contracts/precrime/PreCrime.sol +211 -0
- package/contracts/precrime/extensions/PreCrimeE1.sol +30 -0
- package/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol +55 -0
- package/contracts/precrime/interfaces/IPreCrime.sol +40 -0
- package/contracts/precrime/libs/Packet.sol +61 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# `@layerzerolabs/protocol-v2-oapp-evm-contracts`
|
|
2
|
+
|
|
3
|
+
LayerZero V2 EVM OApp, OFT and PreCrime base contracts, and the `OmniCounter` example. Its sources import the old package names, which resolve to the workspace copies through package aliases.
|
|
4
|
+
|
|
5
|
+
Copied byte for byte from `@layerzerolabs/lz-evm-oapp-v2` (`packages/layerzero-v2/evm/oapp`) in the old LayerZero-Labs/monorepo at `origin/cb/executor-upgrade` (`d976cafeaf`). Only `contracts/` and the Foundry tests are copied; the old deploy scripts, deployment records, typechain bindings and Hardhat tests are not.
|
|
6
|
+
|
|
7
|
+
## Build
|
|
8
|
+
|
|
9
|
+
`pnpm build` compiles with the old compiler settings (`hardhat.config.cjs`) into `artifacts/` (EVM), `artifacts-zk/` (zkSync) and `artifacts-tron/` (Tron), which the package publishes for consumers to read by path, e.g. `@layerzerolabs/protocol-v2-oapp-evm-contracts/artifacts/contracts/<Source>.sol/<Contract>.json`.
|
|
10
|
+
|
|
11
|
+
`../scripts/compare-artifacts.mjs` compares the compiled bytecode with the artifacts the old monorepo published.
|
|
12
|
+
|
|
13
|
+
## Test
|
|
14
|
+
|
|
15
|
+
`pnpm test` runs the Foundry tests.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers
|
|
6
|
+
// solhint-disable-next-line no-unused-import
|
|
7
|
+
import { OAppSender, MessagingFee, MessagingReceipt } from "./OAppSender.sol";
|
|
8
|
+
// @dev Import the 'Origin' so it's exposed to OApp implementers
|
|
9
|
+
// solhint-disable-next-line no-unused-import
|
|
10
|
+
import { OAppReceiver, Origin } from "./OAppReceiver.sol";
|
|
11
|
+
import { OAppCore } from "./OAppCore.sol";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @title OApp
|
|
15
|
+
* @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.
|
|
16
|
+
*/
|
|
17
|
+
abstract contract OApp is OAppSender, OAppReceiver {
|
|
18
|
+
/**
|
|
19
|
+
* @dev Constructor to initialize the OApp with the provided endpoint and owner.
|
|
20
|
+
* @param _endpoint The address of the LOCAL LayerZero endpoint.
|
|
21
|
+
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
|
|
22
|
+
*/
|
|
23
|
+
constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice Retrieves the OApp version information.
|
|
27
|
+
* @return senderVersion The version of the OAppSender.sol implementation.
|
|
28
|
+
* @return receiverVersion The version of the OAppReceiver.sol implementation.
|
|
29
|
+
*/
|
|
30
|
+
function oAppVersion()
|
|
31
|
+
public
|
|
32
|
+
pure
|
|
33
|
+
virtual
|
|
34
|
+
override(OAppSender, OAppReceiver)
|
|
35
|
+
returns (uint64 senderVersion, uint64 receiverVersion)
|
|
36
|
+
{
|
|
37
|
+
return (SENDER_VERSION, RECEIVER_VERSION);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
|
+
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title OAppCore
|
|
10
|
+
* @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
|
|
11
|
+
*/
|
|
12
|
+
abstract contract OAppCore is IOAppCore, Ownable {
|
|
13
|
+
// The LayerZero endpoint associated with the given OApp
|
|
14
|
+
ILayerZeroEndpointV2 public immutable endpoint;
|
|
15
|
+
|
|
16
|
+
// Mapping to store peers associated with corresponding endpoints
|
|
17
|
+
mapping(uint32 eid => bytes32 peer) public peers;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
|
|
21
|
+
* @param _endpoint The address of the LOCAL Layer Zero endpoint.
|
|
22
|
+
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
|
|
23
|
+
*
|
|
24
|
+
* @dev The delegate typically should be set as the owner of the contract.
|
|
25
|
+
*/
|
|
26
|
+
constructor(address _endpoint, address _delegate) {
|
|
27
|
+
endpoint = ILayerZeroEndpointV2(_endpoint);
|
|
28
|
+
|
|
29
|
+
if (_delegate == address(0)) revert InvalidDelegate();
|
|
30
|
+
endpoint.setDelegate(_delegate);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
|
|
35
|
+
* @param _eid The endpoint ID.
|
|
36
|
+
* @param _peer The address of the peer to be associated with the corresponding endpoint.
|
|
37
|
+
*
|
|
38
|
+
* @dev Only the owner/admin of the OApp can call this function.
|
|
39
|
+
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
|
|
40
|
+
* @dev Set this to bytes32(0) to remove the peer address.
|
|
41
|
+
* @dev Peer is a bytes32 to accommodate non-evm chains.
|
|
42
|
+
*/
|
|
43
|
+
function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
|
|
44
|
+
_setPeer(_eid, _peer);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
|
|
49
|
+
* @param _eid The endpoint ID.
|
|
50
|
+
* @param _peer The address of the peer to be associated with the corresponding endpoint.
|
|
51
|
+
*
|
|
52
|
+
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
|
|
53
|
+
* @dev Set this to bytes32(0) to remove the peer address.
|
|
54
|
+
* @dev Peer is a bytes32 to accommodate non-evm chains.
|
|
55
|
+
*/
|
|
56
|
+
function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
|
|
57
|
+
peers[_eid] = _peer;
|
|
58
|
+
emit PeerSet(_eid, _peer);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
|
|
63
|
+
* ie. the peer is set to bytes32(0).
|
|
64
|
+
* @param _eid The endpoint ID.
|
|
65
|
+
* @return peer The address of the peer associated with the specified endpoint.
|
|
66
|
+
*/
|
|
67
|
+
function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
|
|
68
|
+
bytes32 peer = peers[_eid];
|
|
69
|
+
if (peer == bytes32(0)) revert NoPeer(_eid);
|
|
70
|
+
return peer;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @notice Sets the delegate address for the OApp.
|
|
75
|
+
* @param _delegate The address of the delegate to be set.
|
|
76
|
+
*
|
|
77
|
+
* @dev Only the owner/admin of the OApp can call this function.
|
|
78
|
+
* @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
|
|
79
|
+
*/
|
|
80
|
+
function setDelegate(address _delegate) public onlyOwner {
|
|
81
|
+
endpoint.setDelegate(_delegate);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol";
|
|
6
|
+
|
|
7
|
+
import { OApp } from "./OApp.sol";
|
|
8
|
+
|
|
9
|
+
abstract contract OAppRead is OApp {
|
|
10
|
+
|
|
11
|
+
constructor(address _endpoint, address _delegate) OApp(_endpoint, _delegate) {}
|
|
12
|
+
|
|
13
|
+
// -------------------------------
|
|
14
|
+
// Only Owner
|
|
15
|
+
function setReadChannel(uint32 _channelId, bool _active) public virtual onlyOwner {
|
|
16
|
+
_setPeer(_channelId, _active ? AddressCast.toBytes32(address(this)) : bytes32(0));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { IOAppReceiver, Origin } from "./interfaces/IOAppReceiver.sol";
|
|
6
|
+
import { OAppCore } from "./OAppCore.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title OAppReceiver
|
|
10
|
+
* @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.
|
|
11
|
+
*/
|
|
12
|
+
abstract contract OAppReceiver is IOAppReceiver, OAppCore {
|
|
13
|
+
// Custom error message for when the caller is not the registered endpoint/
|
|
14
|
+
error OnlyEndpoint(address addr);
|
|
15
|
+
|
|
16
|
+
// @dev The version of the OAppReceiver implementation.
|
|
17
|
+
// @dev Version is bumped when changes are made to this contract.
|
|
18
|
+
uint64 internal constant RECEIVER_VERSION = 2;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @notice Retrieves the OApp version information.
|
|
22
|
+
* @return senderVersion The version of the OAppSender.sol contract.
|
|
23
|
+
* @return receiverVersion The version of the OAppReceiver.sol contract.
|
|
24
|
+
*
|
|
25
|
+
* @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.
|
|
26
|
+
* ie. this is a RECEIVE only OApp.
|
|
27
|
+
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.
|
|
28
|
+
*/
|
|
29
|
+
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
|
|
30
|
+
return (0, RECEIVER_VERSION);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
|
|
35
|
+
* @dev _origin The origin information containing the source endpoint and sender address.
|
|
36
|
+
* - srcEid: The source chain endpoint ID.
|
|
37
|
+
* - sender: The sender address on the src chain.
|
|
38
|
+
* - nonce: The nonce of the message.
|
|
39
|
+
* @dev _message The lzReceive payload.
|
|
40
|
+
* @param _sender The sender address.
|
|
41
|
+
* @return isSender Is a valid sender.
|
|
42
|
+
*
|
|
43
|
+
* @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.
|
|
44
|
+
* @dev The default sender IS the OAppReceiver implementer.
|
|
45
|
+
*/
|
|
46
|
+
function isComposeMsgSender(
|
|
47
|
+
Origin calldata /*_origin*/,
|
|
48
|
+
bytes calldata /*_message*/,
|
|
49
|
+
address _sender
|
|
50
|
+
) public view virtual returns (bool) {
|
|
51
|
+
return _sender == address(this);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @notice Checks if the path initialization is allowed based on the provided origin.
|
|
56
|
+
* @param origin The origin information containing the source endpoint and sender address.
|
|
57
|
+
* @return Whether the path has been initialized.
|
|
58
|
+
*
|
|
59
|
+
* @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.
|
|
60
|
+
* @dev This defaults to assuming if a peer has been set, its initialized.
|
|
61
|
+
* Can be overridden by the OApp if there is other logic to determine this.
|
|
62
|
+
*/
|
|
63
|
+
function allowInitializePath(Origin calldata origin) public view virtual returns (bool) {
|
|
64
|
+
return peers[origin.srcEid] == origin.sender;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @notice Retrieves the next nonce for a given source endpoint and sender address.
|
|
69
|
+
* @dev _srcEid The source endpoint ID.
|
|
70
|
+
* @dev _sender The sender address.
|
|
71
|
+
* @return nonce The next nonce.
|
|
72
|
+
*
|
|
73
|
+
* @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.
|
|
74
|
+
* @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.
|
|
75
|
+
* @dev This is also enforced by the OApp.
|
|
76
|
+
* @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.
|
|
77
|
+
*/
|
|
78
|
+
function nextNonce(uint32 /*_srcEid*/, bytes32 /*_sender*/) public view virtual returns (uint64 nonce) {
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @dev Entry point for receiving messages or packets from the endpoint.
|
|
84
|
+
* @param _origin The origin information containing the source endpoint and sender address.
|
|
85
|
+
* - srcEid: The source chain endpoint ID.
|
|
86
|
+
* - sender: The sender address on the src chain.
|
|
87
|
+
* - nonce: The nonce of the message.
|
|
88
|
+
* @param _guid The unique identifier for the received LayerZero message.
|
|
89
|
+
* @param _message The payload of the received message.
|
|
90
|
+
* @param _executor The address of the executor for the received message.
|
|
91
|
+
* @param _extraData Additional arbitrary data provided by the corresponding executor.
|
|
92
|
+
*
|
|
93
|
+
* @dev Entry point for receiving msg/packet from the LayerZero endpoint.
|
|
94
|
+
*/
|
|
95
|
+
function lzReceive(
|
|
96
|
+
Origin calldata _origin,
|
|
97
|
+
bytes32 _guid,
|
|
98
|
+
bytes calldata _message,
|
|
99
|
+
address _executor,
|
|
100
|
+
bytes calldata _extraData
|
|
101
|
+
) public payable virtual {
|
|
102
|
+
// Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.
|
|
103
|
+
if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);
|
|
104
|
+
|
|
105
|
+
// Ensure that the sender matches the expected peer for the source endpoint.
|
|
106
|
+
if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);
|
|
107
|
+
|
|
108
|
+
// Call the internal OApp implementation of lzReceive.
|
|
109
|
+
_lzReceive(_origin, _guid, _message, _executor, _extraData);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.
|
|
114
|
+
*/
|
|
115
|
+
function _lzReceive(
|
|
116
|
+
Origin calldata _origin,
|
|
117
|
+
bytes32 _guid,
|
|
118
|
+
bytes calldata _message,
|
|
119
|
+
address _executor,
|
|
120
|
+
bytes calldata _extraData
|
|
121
|
+
) internal virtual;
|
|
122
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
|
|
7
|
+
import { OAppCore } from "./OAppCore.sol";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @title OAppSender
|
|
11
|
+
* @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
|
|
12
|
+
*/
|
|
13
|
+
abstract contract OAppSender is OAppCore {
|
|
14
|
+
using SafeERC20 for IERC20;
|
|
15
|
+
|
|
16
|
+
// Custom error messages
|
|
17
|
+
error NotEnoughNative(uint256 msgValue);
|
|
18
|
+
error LzTokenUnavailable();
|
|
19
|
+
|
|
20
|
+
// @dev The version of the OAppSender implementation.
|
|
21
|
+
// @dev Version is bumped when changes are made to this contract.
|
|
22
|
+
uint64 internal constant SENDER_VERSION = 1;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @notice Retrieves the OApp version information.
|
|
26
|
+
* @return senderVersion The version of the OAppSender.sol contract.
|
|
27
|
+
* @return receiverVersion The version of the OAppReceiver.sol contract.
|
|
28
|
+
*
|
|
29
|
+
* @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
|
|
30
|
+
* ie. this is a SEND only OApp.
|
|
31
|
+
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
|
|
32
|
+
*/
|
|
33
|
+
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
|
|
34
|
+
return (SENDER_VERSION, 0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
|
|
39
|
+
* @param _dstEid The destination endpoint ID.
|
|
40
|
+
* @param _message The message payload.
|
|
41
|
+
* @param _options Additional options for the message.
|
|
42
|
+
* @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
|
|
43
|
+
* @return fee The calculated MessagingFee for the message.
|
|
44
|
+
* - nativeFee: The native fee for the message.
|
|
45
|
+
* - lzTokenFee: The LZ token fee for the message.
|
|
46
|
+
*/
|
|
47
|
+
function _quote(
|
|
48
|
+
uint32 _dstEid,
|
|
49
|
+
bytes memory _message,
|
|
50
|
+
bytes memory _options,
|
|
51
|
+
bool _payInLzToken
|
|
52
|
+
) internal view virtual returns (MessagingFee memory fee) {
|
|
53
|
+
return
|
|
54
|
+
endpoint.quote(
|
|
55
|
+
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
|
|
56
|
+
address(this)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
|
|
62
|
+
* @param _dstEid The destination endpoint ID.
|
|
63
|
+
* @param _message The message payload.
|
|
64
|
+
* @param _options Additional options for the message.
|
|
65
|
+
* @param _fee The calculated LayerZero fee for the message.
|
|
66
|
+
* - nativeFee: The native fee.
|
|
67
|
+
* - lzTokenFee: The lzToken fee.
|
|
68
|
+
* @param _refundAddress The address to receive any excess fee values sent to the endpoint.
|
|
69
|
+
* @return receipt The receipt for the sent message.
|
|
70
|
+
* - guid: The unique identifier for the sent message.
|
|
71
|
+
* - nonce: The nonce of the sent message.
|
|
72
|
+
* - fee: The LayerZero fee incurred for the message.
|
|
73
|
+
*/
|
|
74
|
+
function _lzSend(
|
|
75
|
+
uint32 _dstEid,
|
|
76
|
+
bytes memory _message,
|
|
77
|
+
bytes memory _options,
|
|
78
|
+
MessagingFee memory _fee,
|
|
79
|
+
address _refundAddress
|
|
80
|
+
) internal virtual returns (MessagingReceipt memory receipt) {
|
|
81
|
+
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
|
|
82
|
+
uint256 messageValue = _payNative(_fee.nativeFee);
|
|
83
|
+
if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);
|
|
84
|
+
|
|
85
|
+
return
|
|
86
|
+
// solhint-disable-next-line check-send-result
|
|
87
|
+
endpoint.send{ value: messageValue }(
|
|
88
|
+
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
|
|
89
|
+
_refundAddress
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @dev Internal function to pay the native fee associated with the message.
|
|
95
|
+
* @param _nativeFee The native fee to be paid.
|
|
96
|
+
* @return nativeFee The amount of native currency paid.
|
|
97
|
+
*
|
|
98
|
+
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
|
|
99
|
+
* this will need to be overridden because msg.value would contain multiple lzFees.
|
|
100
|
+
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
|
|
101
|
+
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
|
|
102
|
+
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
|
|
103
|
+
*/
|
|
104
|
+
function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
|
|
105
|
+
if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
|
|
106
|
+
return _nativeFee;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @dev Internal function to pay the LZ token fee associated with the message.
|
|
111
|
+
* @param _lzTokenFee The LZ token fee to be paid.
|
|
112
|
+
*
|
|
113
|
+
* @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
|
|
114
|
+
* @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
|
|
115
|
+
*/
|
|
116
|
+
function _payLzToken(uint256 _lzTokenFee) internal virtual {
|
|
117
|
+
// @dev Cannot cache the token because it is not immutable in the endpoint.
|
|
118
|
+
address lzToken = endpoint.lzToken();
|
|
119
|
+
if (lzToken == address(0)) revert LzTokenUnavailable();
|
|
120
|
+
|
|
121
|
+
// Pay LZ token fee by sending tokens to the endpoint.
|
|
122
|
+
IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import { EVMCallRequestV1, EVMCallComputeV1, ReadCmdCodecV1 } from "../libs/ReadCmdCodecV1.sol";
|
|
5
|
+
|
|
6
|
+
contract CmdCodecV1Mock {
|
|
7
|
+
function decode(
|
|
8
|
+
bytes calldata _cmd
|
|
9
|
+
)
|
|
10
|
+
external
|
|
11
|
+
pure
|
|
12
|
+
returns (uint16 appCmdLabel, EVMCallRequestV1[] memory evmRequests, EVMCallComputeV1 memory compute)
|
|
13
|
+
{
|
|
14
|
+
return ReadCmdCodecV1.decode(_cmd);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function encode(
|
|
18
|
+
uint16 _appCmdLabel,
|
|
19
|
+
EVMCallRequestV1[] calldata _evmRequests
|
|
20
|
+
) external pure returns (bytes memory) {
|
|
21
|
+
return ReadCmdCodecV1.encode(_appCmdLabel, _evmRequests);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function encode(
|
|
25
|
+
uint16 _appCmdLabel,
|
|
26
|
+
EVMCallRequestV1[] calldata _evmRequests,
|
|
27
|
+
EVMCallComputeV1 calldata _evmCompute
|
|
28
|
+
) external pure returns (bytes memory) {
|
|
29
|
+
return ReadCmdCodecV1.encode(_appCmdLabel, _evmRequests, _evmCompute);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { ILayerZeroEndpointV2, MessagingFee, MessagingReceipt, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
|
|
6
|
+
import { ReadCmdCodecV1, EVMCallComputeV1, EVMCallRequestV1 } from "../libs/ReadCmdCodecV1.sol";
|
|
7
|
+
import { IOAppComputer } from "../interfaces/IOAppComputer.sol";
|
|
8
|
+
|
|
9
|
+
import { OAppRead } from "../OAppRead.sol";
|
|
10
|
+
|
|
11
|
+
contract LzReadCounter is OAppRead, IOAppComputer {
|
|
12
|
+
struct EvmReadRequest {
|
|
13
|
+
uint16 appRequestLabel;
|
|
14
|
+
uint32 targetEid;
|
|
15
|
+
bool isBlockNum;
|
|
16
|
+
uint64 blockNumOrTimestamp;
|
|
17
|
+
uint16 confirmations;
|
|
18
|
+
address to;
|
|
19
|
+
uint256 countAddition; // addition to add to the count when reading
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
struct ComputeSetting {
|
|
23
|
+
uint8 computeSetting;
|
|
24
|
+
uint16 computeConfirmations;
|
|
25
|
+
uint64 blockNumOrTimestamp;
|
|
26
|
+
bool isBlockNum;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
uint8 internal constant COMPUTE_SETTING_MAP_ONLY = 0;
|
|
30
|
+
uint8 internal constant COMPUTE_SETTING_REDUCE_ONLY = 1;
|
|
31
|
+
uint8 internal constant COMPUTE_SETTING_MAP_REDUCE = 2;
|
|
32
|
+
uint8 internal constant COMPUTE_SETTING_NONE = 3;
|
|
33
|
+
|
|
34
|
+
uint32 public immutable eid;
|
|
35
|
+
uint256 public count;
|
|
36
|
+
|
|
37
|
+
constructor(address _endpoint) OAppRead(_endpoint, msg.sender) {
|
|
38
|
+
eid = ILayerZeroEndpointV2(_endpoint).eid();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// -------------------------------
|
|
42
|
+
// Trigger Read
|
|
43
|
+
function triggerRead(
|
|
44
|
+
uint32 _channelId, // The read channel id
|
|
45
|
+
uint16 _appLabel, // The cmd app label
|
|
46
|
+
EvmReadRequest[] memory _requests,
|
|
47
|
+
ComputeSetting memory _computeSetting,
|
|
48
|
+
bytes calldata _options
|
|
49
|
+
) external payable returns (MessagingReceipt memory receipt) {
|
|
50
|
+
bytes memory cmd = buildCmd(_appLabel, _requests, _computeSetting);
|
|
51
|
+
count += 1; // increase the count, for pin block testing
|
|
52
|
+
return _lzSend(_channelId, cmd, _options, MessagingFee(msg.value, 0), payable(msg.sender));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function clearCount() external {
|
|
56
|
+
count = 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// -------------------------------
|
|
60
|
+
// View
|
|
61
|
+
function quote(
|
|
62
|
+
uint32 _channelId,
|
|
63
|
+
uint16 _appLabel,
|
|
64
|
+
EvmReadRequest[] memory _requests,
|
|
65
|
+
ComputeSetting memory _computeSetting,
|
|
66
|
+
bytes calldata _options
|
|
67
|
+
) public view returns (uint256 nativeFee, uint256 lzTokenFee) {
|
|
68
|
+
bytes memory cmd = buildCmd(_appLabel, _requests, _computeSetting);
|
|
69
|
+
MessagingFee memory fee = _quote(_channelId, cmd, _options, false);
|
|
70
|
+
return (fee.nativeFee, fee.lzTokenFee);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildCmd(
|
|
74
|
+
uint16 appLabel,
|
|
75
|
+
EvmReadRequest[] memory _readRequests,
|
|
76
|
+
ComputeSetting memory _computeSetting
|
|
77
|
+
) public view returns (bytes memory) {
|
|
78
|
+
require(_readRequests.length > 0, "LzReadCounter: empty requests");
|
|
79
|
+
// build read requests
|
|
80
|
+
EVMCallRequestV1[] memory readRequests = new EVMCallRequestV1[](_readRequests.length);
|
|
81
|
+
for (uint256 i = 0; i < _readRequests.length; i++) {
|
|
82
|
+
EvmReadRequest memory req = _readRequests[i];
|
|
83
|
+
readRequests[i] = EVMCallRequestV1({
|
|
84
|
+
appRequestLabel: req.appRequestLabel,
|
|
85
|
+
targetEid: req.targetEid,
|
|
86
|
+
isBlockNum: req.isBlockNum,
|
|
87
|
+
blockNumOrTimestamp: req.blockNumOrTimestamp,
|
|
88
|
+
confirmations: req.confirmations,
|
|
89
|
+
to: req.to,
|
|
90
|
+
callData: abi.encodeWithSelector(this.readCount.selector, req.countAddition)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// build compute, on current contract
|
|
94
|
+
require(_computeSetting.computeSetting <= COMPUTE_SETTING_NONE, "LzReadCounter: invalid compute type");
|
|
95
|
+
EVMCallComputeV1 memory evmCompute = EVMCallComputeV1({
|
|
96
|
+
computeSetting: _computeSetting.computeSetting,
|
|
97
|
+
targetEid: _computeSetting.computeSetting == COMPUTE_SETTING_NONE ? 0 : eid, // 0(means no compute) for none, else use local eid
|
|
98
|
+
isBlockNum: _computeSetting.isBlockNum,
|
|
99
|
+
blockNumOrTimestamp: _computeSetting.blockNumOrTimestamp,
|
|
100
|
+
confirmations: _computeSetting.computeConfirmations,
|
|
101
|
+
to: address(this)
|
|
102
|
+
});
|
|
103
|
+
bytes memory cmd = ReadCmdCodecV1.encode(appLabel, readRequests, evmCompute);
|
|
104
|
+
|
|
105
|
+
return cmd;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function readCount(uint256 countAddition) external view returns (uint256) {
|
|
109
|
+
require(countAddition != 9, "LzReadCounter: invalid count addition"); // This is only for testing
|
|
110
|
+
return count + countAddition;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function lzMap(bytes calldata _request, bytes calldata _response) external pure returns (bytes memory) {
|
|
114
|
+
require(_response.length == 32, "LzReadCounter: invalid response length");
|
|
115
|
+
uint16 requestId = ReadCmdCodecV1.decodeRequestV1AppRequestLabel(_request);
|
|
116
|
+
uint256 countNum = abi.decode(_response, (uint256));
|
|
117
|
+
return abi.encode(countNum + 100 + requestId * 1000); // map behavior
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function lzReduce(bytes calldata _cmd, bytes[] calldata _responses) external pure returns (bytes memory) {
|
|
121
|
+
uint256 total = 0;
|
|
122
|
+
for (uint256 i = 0; i < _responses.length; i++) {
|
|
123
|
+
require(_responses[i].length == 32, "LzReadCounter: invalid response length");
|
|
124
|
+
uint256 countNum = abi.decode(_responses[i], (uint256));
|
|
125
|
+
total += countNum;
|
|
126
|
+
}
|
|
127
|
+
total += 10000; // reduce behavior
|
|
128
|
+
|
|
129
|
+
uint16 cmdAppLabel = ReadCmdCodecV1.decodeCmdAppLabel(_cmd);
|
|
130
|
+
total += uint256(cmdAppLabel) * 100000; // cmdAppLabel behavior
|
|
131
|
+
|
|
132
|
+
return abi.encode(total);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// -------------------------------
|
|
136
|
+
function _lzReceive(
|
|
137
|
+
Origin calldata /* _origin */,
|
|
138
|
+
bytes32 /* _guid */,
|
|
139
|
+
bytes calldata _message,
|
|
140
|
+
address /*_executor*/,
|
|
141
|
+
bytes calldata /*_extraData*/
|
|
142
|
+
) internal override {
|
|
143
|
+
require(_message.length % 32 == 0, "LzReadCounter: invalid message length");
|
|
144
|
+
uint256 total = 0;
|
|
145
|
+
// loop read bytes32 of the message and decode it to uint256 then add it to the total
|
|
146
|
+
for (uint256 i = 0; i < _message.length; i += 32) {
|
|
147
|
+
total += abi.decode(_message[i:i + 32], (uint256));
|
|
148
|
+
}
|
|
149
|
+
// reset count if it's too large
|
|
150
|
+
if (count >= 2 ** 128) {
|
|
151
|
+
count = 0;
|
|
152
|
+
}
|
|
153
|
+
count += total;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// be able to receive ether
|
|
157
|
+
receive() external payable virtual {}
|
|
158
|
+
|
|
159
|
+
fallback() external payable {}
|
|
160
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
// @dev Oz5 implementation
|
|
5
|
+
// import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
|
+
|
|
7
|
+
import { OmniCounterAbstract, MsgCodec } from "./OmniCounterAbstract.sol";
|
|
8
|
+
|
|
9
|
+
contract OmniCounter is OmniCounterAbstract {
|
|
10
|
+
// @dev Oz4 implementation
|
|
11
|
+
constructor(address _endpoint, address _delegate) OmniCounterAbstract(_endpoint, _delegate) {}
|
|
12
|
+
|
|
13
|
+
// @dev Oz5 implementation
|
|
14
|
+
// constructor(address _endpoint, address _delegate) OmniCounterAbstract(_endpoint, _delegate) Ownable(_delegate) {}
|
|
15
|
+
}
|