@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.
Files changed (42) hide show
  1. package/README.md +15 -0
  2. package/contracts/oapp/OApp.sol +39 -0
  3. package/contracts/oapp/OAppCore.sol +83 -0
  4. package/contracts/oapp/OAppRead.sol +18 -0
  5. package/contracts/oapp/OAppReceiver.sol +122 -0
  6. package/contracts/oapp/OAppSender.sol +124 -0
  7. package/contracts/oapp/examples/CmdCodecV1Mock.sol +31 -0
  8. package/contracts/oapp/examples/LzReadCounter.sol +160 -0
  9. package/contracts/oapp/examples/OmniCounter.sol +15 -0
  10. package/contracts/oapp/examples/OmniCounterAbstract.sol +285 -0
  11. package/contracts/oapp/examples/OmniCounterAlt.sol +90 -0
  12. package/contracts/oapp/examples/OmniCounterPreCrime.sol +102 -0
  13. package/contracts/oapp/interfaces/IOAppComposer.sol +12 -0
  14. package/contracts/oapp/interfaces/IOAppComputer.sol +8 -0
  15. package/contracts/oapp/interfaces/IOAppComputerMap.sol +7 -0
  16. package/contracts/oapp/interfaces/IOAppComputerReduce.sol +7 -0
  17. package/contracts/oapp/interfaces/IOAppCore.sol +52 -0
  18. package/contracts/oapp/interfaces/IOAppMsgInspector.sol +22 -0
  19. package/contracts/oapp/interfaces/IOAppOptionsType3.sol +43 -0
  20. package/contracts/oapp/interfaces/IOAppReceiver.sol +25 -0
  21. package/contracts/oapp/libs/OAppOptionsType3.sol +98 -0
  22. package/contracts/oapp/libs/OptionsBuilder.sol +218 -0
  23. package/contracts/oapp/libs/ReadCmdCodecV1.sol +231 -0
  24. package/contracts/oapp/utils/RateLimiter.sol +229 -0
  25. package/contracts/oft/OFT.sol +89 -0
  26. package/contracts/oft/OFTAdapter.sol +106 -0
  27. package/contracts/oft/OFTCore.sol +399 -0
  28. package/contracts/oft/OFTPrecrime.sol +99 -0
  29. package/contracts/oft/interfaces/IOFT.sol +149 -0
  30. package/contracts/oft/libs/OFTComposeMsgCodec.sol +91 -0
  31. package/contracts/oft/libs/OFTMsgCodec.sol +83 -0
  32. package/contracts/precrime/OAppPreCrimeSimulator.sol +125 -0
  33. package/contracts/precrime/PreCrime.sol +211 -0
  34. package/contracts/precrime/extensions/PreCrimeE1.sol +30 -0
  35. package/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol +55 -0
  36. package/contracts/precrime/interfaces/IPreCrime.sol +40 -0
  37. package/contracts/precrime/libs/Packet.sol +61 -0
  38. package/dist/index.d.ts +7 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +6 -0
  41. package/dist/index.js.map +1 -0
  42. package/package.json +67 -0
@@ -0,0 +1,285 @@
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 { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol";
7
+
8
+ import { OApp } from "../OApp.sol";
9
+ import { OptionsBuilder } from "../libs/OptionsBuilder.sol";
10
+ import { OAppPreCrimeSimulator } from "../../precrime/OAppPreCrimeSimulator.sol";
11
+
12
+ library MsgCodec {
13
+ uint8 internal constant VANILLA_TYPE = 1;
14
+ uint8 internal constant COMPOSED_TYPE = 2;
15
+ uint8 internal constant ABA_TYPE = 3;
16
+ uint8 internal constant COMPOSED_ABA_TYPE = 4;
17
+
18
+ uint8 internal constant MSG_TYPE_OFFSET = 0;
19
+ uint8 internal constant SRC_EID_OFFSET = 1;
20
+ uint8 internal constant VALUE_OFFSET = 5;
21
+
22
+ function encode(uint8 _type, uint32 _srcEid) internal pure returns (bytes memory) {
23
+ return abi.encodePacked(_type, _srcEid);
24
+ }
25
+
26
+ function encode(uint8 _type, uint32 _srcEid, uint256 _value) internal pure returns (bytes memory) {
27
+ return abi.encodePacked(_type, _srcEid, _value);
28
+ }
29
+
30
+ function msgType(bytes calldata _message) internal pure returns (uint8) {
31
+ return uint8(bytes1(_message[MSG_TYPE_OFFSET:SRC_EID_OFFSET]));
32
+ }
33
+
34
+ function srcEid(bytes calldata _message) internal pure returns (uint32) {
35
+ return uint32(bytes4(_message[SRC_EID_OFFSET:VALUE_OFFSET]));
36
+ }
37
+
38
+ function value(bytes calldata _message) internal pure returns (uint256) {
39
+ return uint256(bytes32(_message[VALUE_OFFSET:]));
40
+ }
41
+ }
42
+
43
+ // @dev declared as abstract to provide backwards compatibility with Oz5/Oz4
44
+ abstract contract OmniCounterAbstract is ILayerZeroComposer, OApp, OAppPreCrimeSimulator {
45
+ using MsgCodec for bytes;
46
+ using OptionsBuilder for bytes;
47
+
48
+ uint256 public count;
49
+ uint256 public composedCount;
50
+
51
+ address public admin;
52
+ uint32 public eid;
53
+
54
+ mapping(uint32 srcEid => mapping(bytes32 sender => uint64 nonce)) private maxReceivedNonce;
55
+ bool private orderedNonce;
56
+
57
+ // for global assertions
58
+ mapping(uint32 srcEid => uint256 count) public inboundCount;
59
+ mapping(uint32 dstEid => uint256 count) public outboundCount;
60
+
61
+ constructor(address _endpoint, address _delegate) OApp(_endpoint, _delegate) {
62
+ admin = msg.sender;
63
+ eid = ILayerZeroEndpointV2(_endpoint).eid();
64
+ }
65
+
66
+ modifier onlyAdmin() {
67
+ require(msg.sender == admin, "only admin");
68
+ _;
69
+ }
70
+
71
+ // -------------------------------
72
+ // Only Admin
73
+ function setAdmin(address _admin) external onlyAdmin {
74
+ admin = _admin;
75
+ }
76
+
77
+ function withdraw(address payable _to, uint256 _amount) external virtual onlyAdmin {
78
+ (bool success, ) = _to.call{ value: _amount }("");
79
+ require(success, "OmniCounter: withdraw failed");
80
+ }
81
+
82
+ // -------------------------------
83
+ // Send
84
+ function increment(uint32 _eid, uint8 _type, bytes calldata _options) external payable {
85
+ // bytes memory options = combineOptions(_eid, _type, _options);
86
+ _lzSend(_eid, MsgCodec.encode(_type, eid), _options, MessagingFee(msg.value, 0), payable(msg.sender));
87
+ _incrementOutbound(_eid);
88
+ }
89
+
90
+ // this is a broken function to skip incrementing outbound count
91
+ // so that preCrime will fail
92
+ function brokenIncrement(uint32 _eid, uint8 _type, bytes calldata _options) external payable onlyAdmin {
93
+ // bytes memory options = combineOptions(_eid, _type, _options);
94
+ _lzSend(_eid, MsgCodec.encode(_type, eid), _options, MessagingFee(msg.value, 0), payable(msg.sender));
95
+ // _incrementOutbound(_eid); // mock method which intentionally does not increment outboundCount to cause a PreCrime Crime
96
+ }
97
+
98
+ function batchIncrement(
99
+ uint32[] calldata _eids,
100
+ uint8[] calldata _types,
101
+ bytes[] calldata _options
102
+ ) external payable virtual {
103
+ require(_eids.length == _options.length && _eids.length == _types.length, "OmniCounter: length mismatch");
104
+
105
+ MessagingReceipt memory receipt;
106
+ uint256 providedFee = msg.value;
107
+ for (uint256 i = 0; i < _eids.length; i++) {
108
+ address refundAddress = i == _eids.length - 1 ? msg.sender : address(this);
109
+ uint32 dstEid = _eids[i];
110
+ uint8 msgType = _types[i];
111
+ // bytes memory options = combineOptions(dstEid, msgType, _options[i]);
112
+ receipt = _lzSend(
113
+ dstEid,
114
+ MsgCodec.encode(msgType, eid),
115
+ _options[i],
116
+ MessagingFee(providedFee, 0),
117
+ payable(refundAddress)
118
+ );
119
+ _incrementOutbound(dstEid);
120
+ providedFee -= receipt.fee.nativeFee;
121
+ }
122
+ }
123
+
124
+ // -------------------------------
125
+ // View
126
+ function quote(
127
+ uint32 _eid,
128
+ uint8 _type,
129
+ bytes calldata _options
130
+ ) public view returns (uint256 nativeFee, uint256 lzTokenFee) {
131
+ // bytes memory options = combineOptions(_eid, _type, _options);
132
+ MessagingFee memory fee = _quote(_eid, MsgCodec.encode(_type, eid), _options, false);
133
+ return (fee.nativeFee, fee.lzTokenFee);
134
+ }
135
+
136
+ // @dev enables preCrime simulator
137
+ // @dev routes the call down from the OAppPreCrimeSimulator, and up to the OApp
138
+ function _lzReceiveSimulate(
139
+ Origin calldata _origin,
140
+ bytes32 _guid,
141
+ bytes calldata _message,
142
+ address _executor,
143
+ bytes calldata _extraData
144
+ ) internal virtual override {
145
+ _lzReceive(_origin, _guid, _message, _executor, _extraData);
146
+ }
147
+
148
+ // -------------------------------
149
+ function _lzReceive(
150
+ Origin calldata _origin,
151
+ bytes32 _guid,
152
+ bytes calldata _message,
153
+ address /*_executor*/,
154
+ bytes calldata /*_extraData*/
155
+ ) internal override {
156
+ _acceptNonce(_origin.srcEid, _origin.sender, _origin.nonce);
157
+ uint8 messageType = _message.msgType();
158
+
159
+ if (messageType == MsgCodec.VANILLA_TYPE) {
160
+ count++;
161
+
162
+ //////////////////////////////// IMPORTANT //////////////////////////////////
163
+ /// if you request for msg.value in the options, you should also encode it
164
+ /// into your message and check the value received at destination (example below).
165
+ /// if not, the executor could potentially provide less msg.value than you requested
166
+ /// leading to unintended behavior. Another option is to assert the executor to be
167
+ /// one that you trust.
168
+ /////////////////////////////////////////////////////////////////////////////
169
+ require(msg.value >= _message.value(), "OmniCounter: insufficient value");
170
+
171
+ _incrementInbound(_origin.srcEid);
172
+ } else if (messageType == MsgCodec.COMPOSED_TYPE || messageType == MsgCodec.COMPOSED_ABA_TYPE) {
173
+ count++;
174
+ _incrementInbound(_origin.srcEid);
175
+ endpoint.sendCompose(address(this), _guid, 0, _message);
176
+ } else if (messageType == MsgCodec.ABA_TYPE) {
177
+ count++;
178
+ _incrementInbound(_origin.srcEid);
179
+
180
+ // send back to the sender
181
+ _incrementOutbound(_origin.srcEid);
182
+ bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 10);
183
+ _lzSend(
184
+ _origin.srcEid,
185
+ MsgCodec.encode(MsgCodec.VANILLA_TYPE, eid, 10),
186
+ options,
187
+ MessagingFee(msg.value, 0),
188
+ payable(address(this))
189
+ );
190
+ } else {
191
+ revert("invalid message type");
192
+ }
193
+ }
194
+
195
+ function _incrementInbound(uint32 _srcEid) internal {
196
+ inboundCount[_srcEid]++;
197
+ }
198
+
199
+ function _incrementOutbound(uint32 _dstEid) internal {
200
+ outboundCount[_dstEid]++;
201
+ }
202
+
203
+ function lzCompose(
204
+ address _oApp,
205
+ bytes32 /*_guid*/,
206
+ bytes calldata _message,
207
+ address,
208
+ bytes calldata
209
+ ) external payable override {
210
+ require(_oApp == address(this), "!oApp");
211
+ require(msg.sender == address(endpoint), "!endpoint");
212
+
213
+ uint8 msgType = _message.msgType();
214
+ if (msgType == MsgCodec.COMPOSED_TYPE) {
215
+ composedCount += 1;
216
+ } else if (msgType == MsgCodec.COMPOSED_ABA_TYPE) {
217
+ composedCount += 1;
218
+
219
+ uint32 srcEid = _message.srcEid();
220
+ _incrementOutbound(srcEid);
221
+ bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0);
222
+ _lzSend(
223
+ srcEid,
224
+ MsgCodec.encode(MsgCodec.VANILLA_TYPE, eid),
225
+ options,
226
+ MessagingFee(msg.value, 0),
227
+ payable(address(this))
228
+ );
229
+ } else {
230
+ revert("invalid message type");
231
+ }
232
+ }
233
+
234
+ // -------------------------------
235
+ // Ordered OApp
236
+ // this demonstrates how to build an app that requires execution nonce ordering
237
+ // normally an app should decide ordered or not on contract construction
238
+ // this is just a demo
239
+ function setOrderedNonce(bool _orderedNonce) external onlyOwner {
240
+ orderedNonce = _orderedNonce;
241
+ }
242
+
243
+ function _acceptNonce(uint32 _srcEid, bytes32 _sender, uint64 _nonce) internal virtual {
244
+ uint64 currentNonce = maxReceivedNonce[_srcEid][_sender];
245
+ if (orderedNonce) {
246
+ require(_nonce == currentNonce + 1, "OApp: invalid nonce");
247
+ }
248
+ // update the max nonce anyway. once the ordered mode is turned on, missing early nonces will be rejected
249
+ if (_nonce > currentNonce) {
250
+ maxReceivedNonce[_srcEid][_sender] = _nonce;
251
+ }
252
+ }
253
+
254
+ function nextNonce(uint32 _srcEid, bytes32 _sender) public view virtual override returns (uint64) {
255
+ if (orderedNonce) {
256
+ return maxReceivedNonce[_srcEid][_sender] + 1;
257
+ } else {
258
+ return 0; // path nonce starts from 1. if 0 it means that there is no specific nonce enforcement
259
+ }
260
+ }
261
+
262
+ // TODO should override oApp version with added ordered nonce increment
263
+ // a governance function to skip nonce
264
+ function skipInboundNonce(uint32 _srcEid, bytes32 _sender, uint64 _nonce) public virtual onlyOwner {
265
+ endpoint.skip(address(this), _srcEid, _sender, _nonce);
266
+ if (orderedNonce) {
267
+ maxReceivedNonce[_srcEid][_sender]++;
268
+ }
269
+ }
270
+
271
+ function isPeer(uint32 _eid, bytes32 _peer) public view override returns (bool) {
272
+ return peers[_eid] == _peer;
273
+ }
274
+
275
+ // @dev Batch send requires overriding this function from OAppSender because the msg.value contains multiple fees
276
+ function _payNative(uint256 _nativeFee) internal virtual override returns (uint256 nativeFee) {
277
+ if (msg.value < _nativeFee) revert NotEnoughNative(msg.value);
278
+ return _nativeFee;
279
+ }
280
+
281
+ // be able to receive ether
282
+ receive() external payable virtual {}
283
+
284
+ fallback() external payable virtual {}
285
+ }
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
5
+ import { ILayerZeroEndpointV2, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
6
+ import { OAppSender } from "../OAppSender.sol";
7
+
8
+ import { OmniCounterAbstract, MsgCodec } from "./OmniCounterAbstract.sol";
9
+
10
+ /**
11
+ * @title OmniCounterAlt
12
+ * @dev OmniCounter variant for EndpointV2Alt where the native fee is paid in ERC20 token.
13
+ * @dev Unaudited contract. Use at your own risk.
14
+ */
15
+ contract OmniCounterAlt is OmniCounterAbstract {
16
+ using MsgCodec for bytes;
17
+ using SafeERC20 for IERC20;
18
+
19
+ /// @notice ERC20 token used by EndpointV2Alt as the native fee token.
20
+ address public immutable feeToken;
21
+
22
+ error OmniCounterAlt_NotAnAltEndpoint();
23
+ error OmniCounterAlt_OnlyAltToken();
24
+
25
+ constructor(address _endpoint, address _delegate) OmniCounterAbstract(_endpoint, _delegate) {
26
+ feeToken = ILayerZeroEndpointV2(_endpoint).nativeToken();
27
+ if (feeToken == address(0)) revert OmniCounterAlt_NotAnAltEndpoint();
28
+ }
29
+
30
+ /// @dev Override to withdraw ERC20 fee instead of native ETH.
31
+ function withdraw(address payable _to, uint256 _amount) external virtual override onlyAdmin {
32
+ IERC20(feeToken).safeTransfer(_to, _amount);
33
+ }
34
+
35
+ /// @dev Override to quote actual fee instead of relying on msg.value.
36
+ function batchIncrement(
37
+ uint32[] calldata _eids,
38
+ uint8[] calldata _types,
39
+ bytes[] calldata _options
40
+ ) external payable virtual override {
41
+ require(_eids.length == _options.length && _eids.length == _types.length, "OmniCounter: length mismatch");
42
+
43
+ for (uint256 i = 0; i < _eids.length; i++) {
44
+ uint32 dstEid = _eids[i];
45
+
46
+ bytes memory message = MsgCodec.encode(_types[i], eid);
47
+ MessagingFee memory fee = _quote(dstEid, message, _options[i], false);
48
+
49
+ OAppSender._lzSend(dstEid, message, _options[i], fee, msg.sender);
50
+ _incrementOutbound(dstEid);
51
+ }
52
+ }
53
+
54
+ /// @dev Override to quote actual fee since parent passes msg.value (which is 0 for Alt).
55
+ function _lzSend(
56
+ uint32 _dstEid,
57
+ bytes memory _message,
58
+ bytes memory _options,
59
+ MessagingFee memory _fee,
60
+ address _refundAddress
61
+ ) internal virtual override returns (MessagingReceipt memory receipt) {
62
+ MessagingFee memory actualFee = _quote(_dstEid, _message, _options, _fee.lzTokenFee > 0);
63
+ return super._lzSend(_dstEid, _message, _options, actualFee, _refundAddress);
64
+ }
65
+
66
+ /// @dev Transfer ERC20 fee to endpoint instead of native ETH.
67
+ function _payNative(uint256 _nativeFee) internal override returns (uint256) {
68
+ if (msg.value != 0) revert OmniCounterAlt_OnlyAltToken();
69
+ if (_nativeFee > 0) {
70
+ if (msg.sender == address(endpoint)) {
71
+ // ABA reply or compose callback - contract pays from its balance
72
+ IERC20(feeToken).safeTransfer(address(endpoint), _nativeFee);
73
+ } else {
74
+ // User-initiated - user pays (must have approved this contract)
75
+ IERC20(feeToken).safeTransferFrom(msg.sender, address(endpoint), _nativeFee);
76
+ }
77
+ }
78
+ return 0;
79
+ }
80
+
81
+ /// @dev Override to avoid receiving native ETH.
82
+ receive() external payable virtual override {
83
+ revert OmniCounterAlt_OnlyAltToken();
84
+ }
85
+
86
+ /// @dev Override to avoid receiving native ETH.
87
+ fallback() external payable virtual override {
88
+ revert OmniCounterAlt_OnlyAltToken();
89
+ }
90
+ }
@@ -0,0 +1,102 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { PreCrime, PreCrimePeer } from "../../precrime/PreCrime.sol";
6
+ import { InboundPacket } from "../../precrime/libs/Packet.sol";
7
+ import { OmniCounter } from "./OmniCounter.sol";
8
+
9
+ contract OmniCounterPreCrime is PreCrime {
10
+ struct ChainCount {
11
+ uint32 remoteEid;
12
+ uint256 inboundCount;
13
+ uint256 outboundCount;
14
+ }
15
+
16
+ constructor(address _endpoint, address _counter) PreCrime(_endpoint, _counter) {}
17
+
18
+ function buildSimulationResult() external view override returns (bytes memory) {
19
+ address payable payableSimulator = payable(simulator);
20
+ OmniCounter counter = OmniCounter(payableSimulator);
21
+ ChainCount[] memory chainCounts = new ChainCount[](preCrimePeers.length);
22
+ for (uint256 i = 0; i < preCrimePeers.length; i++) {
23
+ uint32 remoteEid = preCrimePeers[i].eid;
24
+ chainCounts[i] = ChainCount(remoteEid, counter.inboundCount(remoteEid), counter.outboundCount(remoteEid));
25
+ }
26
+ return abi.encode(chainCounts);
27
+ }
28
+
29
+ function _preCrime(
30
+ InboundPacket[] memory /** _packets */,
31
+ uint32[] memory _eids,
32
+ bytes[] memory _simulations
33
+ ) internal view override {
34
+ uint32 localEid = _getLocalEid();
35
+ ChainCount[] memory localChainCounts;
36
+
37
+ // find local chain counts
38
+ for (uint256 i = 0; i < _eids.length; i++) {
39
+ if (_eids[i] == localEid) {
40
+ localChainCounts = abi.decode(_simulations[i], (ChainCount[]));
41
+ break;
42
+ }
43
+ }
44
+
45
+ // local against remote
46
+ for (uint256 i = 0; i < _eids.length; i++) {
47
+ uint32 remoteEid = _eids[i];
48
+ ChainCount[] memory remoteChainCounts = abi.decode(_simulations[i], (ChainCount[]));
49
+ (uint256 _inboundCount, ) = _findChainCounts(localChainCounts, remoteEid);
50
+ (, uint256 _outboundCount) = _findChainCounts(remoteChainCounts, localEid);
51
+ if (_inboundCount > _outboundCount) {
52
+ revert CrimeFound("inboundCount > outboundCount");
53
+ }
54
+ }
55
+ }
56
+
57
+ function _findChainCounts(
58
+ ChainCount[] memory _chainCounts,
59
+ uint32 _remoteEid
60
+ ) internal pure returns (uint256, uint256) {
61
+ for (uint256 i = 0; i < _chainCounts.length; i++) {
62
+ if (_chainCounts[i].remoteEid == _remoteEid) {
63
+ return (_chainCounts[i].inboundCount, _chainCounts[i].outboundCount);
64
+ }
65
+ }
66
+ return (0, 0);
67
+ }
68
+
69
+ function _getPreCrimePeers(
70
+ InboundPacket[] memory _packets
71
+ ) internal view override returns (PreCrimePeer[] memory peers) {
72
+ PreCrimePeer[] memory allPeers = preCrimePeers;
73
+ PreCrimePeer[] memory peersTmp = new PreCrimePeer[](_packets.length);
74
+
75
+ int256 cursor = -1;
76
+ for (uint256 i = 0; i < _packets.length; i++) {
77
+ uint32 srcEid = _packets[i].origin.srcEid;
78
+
79
+ // push src eid & peer
80
+ int256 index = _indexOf(allPeers, srcEid);
81
+ if (index >= 0 && _indexOf(peersTmp, srcEid) < 0) {
82
+ cursor++;
83
+ peersTmp[uint256(cursor)] = allPeers[uint256(index)];
84
+ }
85
+ }
86
+ // copy to return
87
+ if (cursor >= 0) {
88
+ uint256 len = uint256(cursor) + 1;
89
+ peers = new PreCrimePeer[](len);
90
+ for (uint256 i = 0; i < len; i++) {
91
+ peers[i] = peersTmp[i];
92
+ }
93
+ }
94
+ }
95
+
96
+ function _indexOf(PreCrimePeer[] memory _peers, uint32 _eid) internal pure returns (int256) {
97
+ for (uint256 i = 0; i < _peers.length; i++) {
98
+ if (_peers[i].eid == _eid) return int256(i);
99
+ }
100
+ return -1;
101
+ }
102
+ }
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol";
6
+
7
+ /**
8
+ * @title IOAppComposer
9
+ * @dev This interface defines the OApp Composer, allowing developers to inherit only the OApp package without the protocol.
10
+ */
11
+ // solhint-disable-next-line no-empty-blocks
12
+ interface IOAppComposer is ILayerZeroComposer {}
@@ -0,0 +1,8 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { IOAppComputerReduce } from "./IOAppComputerReduce.sol";
6
+ import { IOAppComputerMap } from "./IOAppComputerMap.sol";
7
+
8
+ interface IOAppComputer is IOAppComputerMap, IOAppComputerReduce {}
@@ -0,0 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ interface IOAppComputerMap {
6
+ function lzMap(bytes calldata _request, bytes calldata _response) external view returns (bytes memory);
7
+ }
@@ -0,0 +1,7 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ interface IOAppComputerReduce {
6
+ function lzReduce(bytes calldata _cmd, bytes[] calldata _responses) external view returns (bytes memory);
7
+ }
@@ -0,0 +1,52 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
6
+
7
+ /**
8
+ * @title IOAppCore
9
+ */
10
+ interface IOAppCore {
11
+ // Custom error messages
12
+ error OnlyPeer(uint32 eid, bytes32 sender);
13
+ error NoPeer(uint32 eid);
14
+ error InvalidEndpointCall();
15
+ error InvalidDelegate();
16
+
17
+ // Event emitted when a peer (OApp) is set for a corresponding endpoint
18
+ event PeerSet(uint32 eid, bytes32 peer);
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
+ function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);
26
+
27
+ /**
28
+ * @notice Retrieves the LayerZero endpoint associated with the OApp.
29
+ * @return iEndpoint The LayerZero endpoint as an interface.
30
+ */
31
+ function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);
32
+
33
+ /**
34
+ * @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
35
+ * @param _eid The endpoint ID.
36
+ * @return peer The peer address (OApp instance) associated with the corresponding endpoint.
37
+ */
38
+ function peers(uint32 _eid) external view returns (bytes32 peer);
39
+
40
+ /**
41
+ * @notice Sets the peer address (OApp instance) for a corresponding endpoint.
42
+ * @param _eid The endpoint ID.
43
+ * @param _peer The address of the peer to be associated with the corresponding endpoint.
44
+ */
45
+ function setPeer(uint32 _eid, bytes32 _peer) external;
46
+
47
+ /**
48
+ * @notice Sets the delegate address for the OApp Core.
49
+ * @param _delegate The address of the delegate to be set.
50
+ */
51
+ function setDelegate(address _delegate) external;
52
+ }
@@ -0,0 +1,22 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ /**
6
+ * @title IOAppMsgInspector
7
+ * @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.
8
+ */
9
+ interface IOAppMsgInspector {
10
+ // Custom error message for inspection failure
11
+ error InspectionFailed(bytes message, bytes options);
12
+
13
+ /**
14
+ * @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.
15
+ * @param _message The message payload to be inspected.
16
+ * @param _options Additional options or parameters for inspection.
17
+ * @return valid A boolean indicating whether the inspection passed (true) or failed (false).
18
+ *
19
+ * @dev Optionally done as a revert, OR use the boolean provided to handle the failure.
20
+ */
21
+ function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);
22
+ }
@@ -0,0 +1,43 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ /**
6
+ * @dev Struct representing enforced option parameters.
7
+ */
8
+ struct EnforcedOptionParam {
9
+ uint32 eid; // Endpoint ID
10
+ uint16 msgType; // Message Type
11
+ bytes options; // Additional options
12
+ }
13
+
14
+ /**
15
+ * @title IOAppOptionsType3
16
+ * @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.
17
+ */
18
+ interface IOAppOptionsType3 {
19
+ // Custom error message for invalid options
20
+ error InvalidOptions(bytes options);
21
+
22
+ // Event emitted when enforced options are set
23
+ event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);
24
+
25
+ /**
26
+ * @notice Sets enforced options for specific endpoint and message type combinations.
27
+ * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
28
+ */
29
+ function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;
30
+
31
+ /**
32
+ * @notice Combines options for a given endpoint and message type.
33
+ * @param _eid The endpoint ID.
34
+ * @param _msgType The OApp message type.
35
+ * @param _extraOptions Additional options passed by the caller.
36
+ * @return options The combination of caller specified options AND enforced options.
37
+ */
38
+ function combineOptions(
39
+ uint32 _eid,
40
+ uint16 _msgType,
41
+ bytes calldata _extraOptions
42
+ ) external view returns (bytes memory options);
43
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import { ILayerZeroReceiver, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol";
5
+
6
+ interface IOAppReceiver is ILayerZeroReceiver {
7
+ /**
8
+ * @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
9
+ * @param _origin The origin information containing the source endpoint and sender address.
10
+ * - srcEid: The source chain endpoint ID.
11
+ * - sender: The sender address on the src chain.
12
+ * - nonce: The nonce of the message.
13
+ * @param _message The lzReceive payload.
14
+ * @param _sender The sender address.
15
+ * @return isSender Is a valid sender.
16
+ *
17
+ * @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.
18
+ * @dev The default sender IS the OAppReceiver implementer.
19
+ */
20
+ function isComposeMsgSender(
21
+ Origin calldata _origin,
22
+ bytes calldata _message,
23
+ address _sender
24
+ ) external view returns (bool isSender);
25
+ }