@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,98 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
6
+ import { IOAppOptionsType3, EnforcedOptionParam } from "../interfaces/IOAppOptionsType3.sol";
7
+
8
+ /**
9
+ * @title OAppOptionsType3
10
+ * @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.
11
+ */
12
+ abstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {
13
+ uint16 internal constant OPTION_TYPE_3 = 3;
14
+
15
+ // @dev The "msgType" should be defined in the child contract.
16
+ mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;
17
+
18
+ /**
19
+ * @dev Sets the enforced options for specific endpoint and message type combinations.
20
+ * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
21
+ *
22
+ * @dev Only the owner/admin of the OApp can call this function.
23
+ * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
24
+ * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
25
+ * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
26
+ * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
27
+ */
28
+ function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {
29
+ _setEnforcedOptions(_enforcedOptions);
30
+ }
31
+
32
+ /**
33
+ * @dev Sets the enforced options for specific endpoint and message type combinations.
34
+ * @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
35
+ *
36
+ * @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
37
+ * @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
38
+ * eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
39
+ * if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
40
+ */
41
+ function _setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internal virtual {
42
+ for (uint256 i = 0; i < _enforcedOptions.length; i++) {
43
+ // @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.
44
+ _assertOptionsType3(_enforcedOptions[i].options);
45
+ enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;
46
+ }
47
+
48
+ emit EnforcedOptionSet(_enforcedOptions);
49
+ }
50
+
51
+ /**
52
+ * @notice Combines options for a given endpoint and message type.
53
+ * @param _eid The endpoint ID.
54
+ * @param _msgType The OAPP message type.
55
+ * @param _extraOptions Additional options passed by the caller.
56
+ * @return options The combination of caller specified options AND enforced options.
57
+ *
58
+ * @dev If there is an enforced lzReceive option:
59
+ * - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}
60
+ * - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.
61
+ * @dev This presence of duplicated options is handled off-chain in the verifier/executor.
62
+ */
63
+ function combineOptions(
64
+ uint32 _eid,
65
+ uint16 _msgType,
66
+ bytes calldata _extraOptions
67
+ ) public view virtual returns (bytes memory) {
68
+ bytes memory enforced = enforcedOptions[_eid][_msgType];
69
+
70
+ // No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.
71
+ if (enforced.length == 0) return _extraOptions;
72
+
73
+ // No caller options, return enforced
74
+ if (_extraOptions.length == 0) return enforced;
75
+
76
+ // @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.
77
+ if (_extraOptions.length >= 2) {
78
+ _assertOptionsType3(_extraOptions);
79
+ // @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.
80
+ return bytes.concat(enforced, _extraOptions[2:]);
81
+ }
82
+
83
+ // No valid set of options was found.
84
+ revert InvalidOptions(_extraOptions);
85
+ }
86
+
87
+ /**
88
+ * @dev Internal function to assert that options are of type 3.
89
+ * @param _options The options to be checked.
90
+ */
91
+ function _assertOptionsType3(bytes memory _options) internal pure virtual {
92
+ uint16 optionsType;
93
+ assembly {
94
+ optionsType := mload(add(_options, 2))
95
+ }
96
+ if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);
97
+ }
98
+ }
@@ -0,0 +1,218 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol";
6
+ import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
7
+
8
+ import { ExecutorOptions } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol";
9
+ import { DVNOptions } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol";
10
+
11
+ /**
12
+ * @title OptionsBuilder
13
+ * @dev Library for building and encoding various message options.
14
+ */
15
+ library OptionsBuilder {
16
+ using SafeCast for uint256;
17
+ using BytesLib for bytes;
18
+
19
+ // Constants for options types
20
+ uint16 internal constant TYPE_1 = 1; // legacy options type 1
21
+ uint16 internal constant TYPE_2 = 2; // legacy options type 2
22
+ uint16 internal constant TYPE_3 = 3;
23
+
24
+ // Custom error message
25
+ error InvalidSize(uint256 max, uint256 actual);
26
+ error InvalidOptionType(uint16 optionType);
27
+
28
+ // Modifier to ensure only options of type 3 are used
29
+ modifier onlyType3(bytes memory _options) {
30
+ if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));
31
+ _;
32
+ }
33
+
34
+ /**
35
+ * @dev Creates a new options container with type 3.
36
+ * @return options The newly created options container.
37
+ */
38
+ function newOptions() internal pure returns (bytes memory) {
39
+ return abi.encodePacked(TYPE_3);
40
+ }
41
+
42
+ /**
43
+ * @dev Adds an executor LZ receive option to the existing options.
44
+ * @param _options The existing options container.
45
+ * @param _gas The gasLimit used on the lzReceive() function in the OApp.
46
+ * @param _value The msg.value passed to the lzReceive() function in the OApp.
47
+ * @return options The updated options container.
48
+ *
49
+ * @dev When multiples of this option are added, they are summed by the executor
50
+ * eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,
51
+ * that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.
52
+ */
53
+ function addExecutorLzReceiveOption(
54
+ bytes memory _options,
55
+ uint128 _gas,
56
+ uint128 _value
57
+ ) internal pure onlyType3(_options) returns (bytes memory) {
58
+ bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);
59
+ return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);
60
+ }
61
+
62
+ /**
63
+ * @dev Adds an executor LZ read option to the existing options.
64
+ * @param _options The existing options container.
65
+ * @param _gas The gas limit used for the lzReceive() function in the ReadOApp.
66
+ * @param _calldataSize The size of the payload for lzReceive() function in the ReadOApp.
67
+ * @param _value The msg.value passed to the lzReceive() function in the ReadOApp.
68
+ * @return options The updated options container.
69
+ */
70
+ function addExecutorLzReadOption(
71
+ bytes memory _options,
72
+ uint128 _gas,
73
+ uint32 _calldataSize,
74
+ uint128 _value
75
+ ) internal pure onlyType3(_options) returns (bytes memory) {
76
+ bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _calldataSize, _value);
77
+ return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);
78
+ }
79
+
80
+ /**
81
+ * @dev Adds an executor native drop option to the existing options.
82
+ * @param _options The existing options container.
83
+ * @param _amount The amount for the native value that is airdropped to the 'receiver'.
84
+ * @param _receiver The receiver address for the native drop option.
85
+ * @return options The updated options container.
86
+ *
87
+ * @dev When multiples of this option are added, they are summed by the executor on the remote chain.
88
+ */
89
+ function addExecutorNativeDropOption(
90
+ bytes memory _options,
91
+ uint128 _amount,
92
+ bytes32 _receiver
93
+ ) internal pure onlyType3(_options) returns (bytes memory) {
94
+ bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);
95
+ return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);
96
+ }
97
+
98
+ /**
99
+ * @dev Adds an executor LZ compose option to the existing options.
100
+ * @param _options The existing options container.
101
+ * @param _index The index for the lzCompose() function call.
102
+ * @param _gas The gasLimit for the lzCompose() function call.
103
+ * @param _value The msg.value for the lzCompose() function call.
104
+ * @return options The updated options container.
105
+ *
106
+ * @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.
107
+ * @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.
108
+ * ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2
109
+ */
110
+ function addExecutorLzComposeOption(
111
+ bytes memory _options,
112
+ uint16 _index,
113
+ uint128 _gas,
114
+ uint128 _value
115
+ ) internal pure onlyType3(_options) returns (bytes memory) {
116
+ bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);
117
+ return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);
118
+ }
119
+
120
+ /**
121
+ * @dev Adds an executor ordered execution option to the existing options.
122
+ * @param _options The existing options container.
123
+ * @return options The updated options container.
124
+ */
125
+ function addExecutorOrderedExecutionOption(
126
+ bytes memory _options
127
+ ) internal pure onlyType3(_options) returns (bytes memory) {
128
+ return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(""));
129
+ }
130
+
131
+ /**
132
+ * @dev Adds a DVN pre-crime option to the existing options.
133
+ * @param _options The existing options container.
134
+ * @param _dvnIdx The DVN index for the pre-crime option.
135
+ * @return options The updated options container.
136
+ */
137
+ function addDVNPreCrimeOption(
138
+ bytes memory _options,
139
+ uint8 _dvnIdx
140
+ ) internal pure onlyType3(_options) returns (bytes memory) {
141
+ return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(""));
142
+ }
143
+
144
+ /**
145
+ * @dev Adds an executor option to the existing options.
146
+ * @param _options The existing options container.
147
+ * @param _optionType The type of the executor option.
148
+ * @param _option The encoded data for the executor option.
149
+ * @return options The updated options container.
150
+ */
151
+ function addExecutorOption(
152
+ bytes memory _options,
153
+ uint8 _optionType,
154
+ bytes memory _option
155
+ ) internal pure onlyType3(_options) returns (bytes memory) {
156
+ return
157
+ abi.encodePacked(
158
+ _options,
159
+ ExecutorOptions.WORKER_ID,
160
+ _option.length.toUint16() + 1, // +1 for optionType
161
+ _optionType,
162
+ _option
163
+ );
164
+ }
165
+
166
+ /**
167
+ * @dev Adds a DVN option to the existing options.
168
+ * @param _options The existing options container.
169
+ * @param _dvnIdx The DVN index for the DVN option.
170
+ * @param _optionType The type of the DVN option.
171
+ * @param _option The encoded data for the DVN option.
172
+ * @return options The updated options container.
173
+ */
174
+ function addDVNOption(
175
+ bytes memory _options,
176
+ uint8 _dvnIdx,
177
+ uint8 _optionType,
178
+ bytes memory _option
179
+ ) internal pure onlyType3(_options) returns (bytes memory) {
180
+ return
181
+ abi.encodePacked(
182
+ _options,
183
+ DVNOptions.WORKER_ID,
184
+ _option.length.toUint16() + 2, // +2 for optionType and dvnIdx
185
+ _dvnIdx,
186
+ _optionType,
187
+ _option
188
+ );
189
+ }
190
+
191
+ /**
192
+ * @dev Encodes legacy options of type 1.
193
+ * @param _executionGas The gasLimit value passed to lzReceive().
194
+ * @return legacyOptions The encoded legacy options.
195
+ */
196
+ function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {
197
+ if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);
198
+ return abi.encodePacked(TYPE_1, _executionGas);
199
+ }
200
+
201
+ /**
202
+ * @dev Encodes legacy options of type 2.
203
+ * @param _executionGas The gasLimit value passed to lzReceive().
204
+ * @param _nativeForDst The amount of native air dropped to the receiver.
205
+ * @param _receiver The _nativeForDst receiver address.
206
+ * @return legacyOptions The encoded legacy options of type 2.
207
+ */
208
+ function encodeLegacyOptionsType2(
209
+ uint256 _executionGas,
210
+ uint256 _nativeForDst,
211
+ bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.
212
+ ) internal pure returns (bytes memory) {
213
+ if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);
214
+ if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);
215
+ if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);
216
+ return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);
217
+ }
218
+ }
@@ -0,0 +1,231 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
6
+
7
+ struct EVMCallRequestV1 {
8
+ uint16 appRequestLabel; // Label identifying the application or type of request (can be use in lzCompute)
9
+ uint32 targetEid; // Target endpoint ID (representing a target blockchain)
10
+ bool isBlockNum; // True if the request = block number, false if timestamp
11
+ uint64 blockNumOrTimestamp; // Block number or timestamp to use in the request
12
+ uint16 confirmations; // Number of block confirmations on top of the requested block number or timestamp before the view function can be called
13
+ address to; // Address of the target contract on the target chain
14
+ bytes callData; // Calldata for the contract call
15
+ }
16
+
17
+ struct EVMCallComputeV1 {
18
+ uint8 computeSetting; // Compute setting (0 = map only, 1 = reduce only, 2 = map reduce)
19
+ uint32 targetEid; // Target endpoint ID (representing a target blockchain)
20
+ bool isBlockNum; // True if the request = block number, false if timestamp
21
+ uint64 blockNumOrTimestamp; // Block number or timestamp to use in the request
22
+ uint16 confirmations; // Number of block confirmations on top of the requested block number or timestamp before the view function can be called
23
+ address to; // Address of the target contract on the target chain
24
+ }
25
+
26
+ library ReadCmdCodecV1 {
27
+ using SafeCast for uint256;
28
+
29
+ uint16 internal constant CMD_VERSION = 1;
30
+
31
+ uint8 internal constant REQUEST_VERSION = 1;
32
+ uint16 internal constant RESOLVER_TYPE_SINGLE_VIEW_EVM_CALL = 1;
33
+
34
+ uint8 internal constant COMPUTE_VERSION = 1;
35
+ uint16 internal constant COMPUTE_TYPE_SINGLE_VIEW_EVM_CALL = 1;
36
+
37
+ error InvalidVersion();
38
+ error InvalidType();
39
+
40
+ function decode(
41
+ bytes calldata _cmd
42
+ )
43
+ internal
44
+ pure
45
+ returns (uint16 appCmdLabel, EVMCallRequestV1[] memory evmCallRequests, EVMCallComputeV1 memory compute)
46
+ {
47
+ uint256 offset = 0;
48
+ uint16 cmdVersion = uint16(bytes2(_cmd[offset:offset + 2]));
49
+ offset += 2;
50
+ if (cmdVersion != CMD_VERSION) revert InvalidVersion();
51
+
52
+ appCmdLabel = uint16(bytes2(_cmd[offset:offset + 2]));
53
+ offset += 2;
54
+
55
+ (evmCallRequests, offset) = decodeRequestsV1(_cmd, offset);
56
+
57
+ // decode the compute if it exists
58
+ if (offset < _cmd.length) {
59
+ (compute, ) = decodeEVMCallComputeV1(_cmd, offset);
60
+ }
61
+ }
62
+
63
+ function decodeRequestsV1(
64
+ bytes calldata _cmd,
65
+ uint256 _offset
66
+ ) internal pure returns (EVMCallRequestV1[] memory evmCallRequests, uint256 newOffset) {
67
+ newOffset = _offset;
68
+ uint16 requestCount = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
69
+ newOffset += 2;
70
+
71
+ evmCallRequests = new EVMCallRequestV1[](requestCount);
72
+ for (uint16 i = 0; i < requestCount; i++) {
73
+ uint8 requestVersion = uint8(_cmd[newOffset]);
74
+ newOffset += 1;
75
+ if (requestVersion != REQUEST_VERSION) revert InvalidVersion();
76
+
77
+ uint16 appRequestLabel = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
78
+ newOffset += 2;
79
+
80
+ uint16 resolverType = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
81
+ newOffset += 2;
82
+
83
+ if (resolverType == RESOLVER_TYPE_SINGLE_VIEW_EVM_CALL) {
84
+ (EVMCallRequestV1 memory request, uint256 nextOffset) = decodeEVMCallRequestV1(
85
+ _cmd,
86
+ newOffset,
87
+ appRequestLabel
88
+ );
89
+ newOffset = nextOffset;
90
+ evmCallRequests[i] = request;
91
+ } else {
92
+ revert InvalidType();
93
+ }
94
+ }
95
+ }
96
+
97
+ function decodeEVMCallRequestV1(
98
+ bytes calldata _cmd,
99
+ uint256 _offset,
100
+ uint16 _appRequestLabel
101
+ ) internal pure returns (EVMCallRequestV1 memory request, uint256 newOffset) {
102
+ newOffset = _offset;
103
+ request.appRequestLabel = _appRequestLabel;
104
+
105
+ uint16 requestSize = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
106
+ newOffset += 2;
107
+ request.targetEid = uint32(bytes4(_cmd[newOffset:newOffset + 4]));
108
+ newOffset += 4;
109
+ request.isBlockNum = uint8(_cmd[newOffset]) == 1;
110
+ newOffset += 1;
111
+ request.blockNumOrTimestamp = uint64(bytes8(_cmd[newOffset:newOffset + 8]));
112
+ newOffset += 8;
113
+ request.confirmations = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
114
+ newOffset += 2;
115
+ request.to = address(bytes20(_cmd[newOffset:newOffset + 20]));
116
+ newOffset += 20;
117
+ uint16 callDataSize = requestSize - 35;
118
+ request.callData = _cmd[newOffset:newOffset + callDataSize];
119
+ newOffset += callDataSize;
120
+ }
121
+
122
+ function decodeEVMCallComputeV1(
123
+ bytes calldata _cmd,
124
+ uint256 _offset
125
+ ) internal pure returns (EVMCallComputeV1 memory compute, uint256 newOffset) {
126
+ newOffset = _offset;
127
+ uint8 computeVersion = uint8(_cmd[newOffset]);
128
+ newOffset += 1;
129
+ if (computeVersion != COMPUTE_VERSION) revert InvalidVersion();
130
+ uint16 computeType = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
131
+ newOffset += 2;
132
+ if (computeType != COMPUTE_TYPE_SINGLE_VIEW_EVM_CALL) revert InvalidType();
133
+
134
+ compute.computeSetting = uint8(_cmd[newOffset]);
135
+ newOffset += 1;
136
+ compute.targetEid = uint32(bytes4(_cmd[newOffset:newOffset + 4]));
137
+ newOffset += 4;
138
+ compute.isBlockNum = uint8(_cmd[newOffset]) == 1;
139
+ newOffset += 1;
140
+ compute.blockNumOrTimestamp = uint64(bytes8(_cmd[newOffset:newOffset + 8]));
141
+ newOffset += 8;
142
+ compute.confirmations = uint16(bytes2(_cmd[newOffset:newOffset + 2]));
143
+ newOffset += 2;
144
+ compute.to = address(bytes20(_cmd[newOffset:newOffset + 20]));
145
+ newOffset += 20;
146
+ }
147
+
148
+ function decodeCmdAppLabel(bytes calldata _cmd) internal pure returns (uint16) {
149
+ uint256 offset = 0;
150
+ uint16 cmdVersion = uint16(bytes2(_cmd[offset:offset + 2]));
151
+ offset += 2;
152
+ if (cmdVersion != CMD_VERSION) revert InvalidVersion();
153
+
154
+ return uint16(bytes2(_cmd[offset:offset + 2]));
155
+ }
156
+
157
+ function decodeRequestV1AppRequestLabel(bytes calldata _request) internal pure returns (uint16) {
158
+ uint256 offset = 0;
159
+ uint8 requestVersion = uint8(_request[offset]);
160
+ offset += 1;
161
+ if (requestVersion != REQUEST_VERSION) revert InvalidVersion();
162
+
163
+ return uint16(bytes2(_request[offset:offset + 2]));
164
+ }
165
+
166
+ function encode(
167
+ uint16 _appCmdLabel,
168
+ EVMCallRequestV1[] memory _evmCallRequests,
169
+ EVMCallComputeV1 memory _evmCallCompute
170
+ ) internal pure returns (bytes memory) {
171
+ bytes memory cmd = encode(_appCmdLabel, _evmCallRequests);
172
+ if (_evmCallCompute.targetEid != 0) {
173
+ // if eid is 0, it means no compute
174
+ cmd = appendEVMCallComputeV1(cmd, _evmCallCompute);
175
+ }
176
+ return cmd;
177
+ }
178
+
179
+ function encode(
180
+ uint16 _appCmdLabel,
181
+ EVMCallRequestV1[] memory _evmCallRequests
182
+ ) internal pure returns (bytes memory) {
183
+ bytes memory cmd = abi.encodePacked(CMD_VERSION, _appCmdLabel, _evmCallRequests.length.toUint16());
184
+ for (uint256 i = 0; i < _evmCallRequests.length; i++) {
185
+ cmd = appendEVMCallRequestV1(cmd, _evmCallRequests[i]);
186
+ }
187
+ return cmd;
188
+ }
189
+
190
+ // todo: optimize this with Buffer
191
+ function appendEVMCallRequestV1(
192
+ bytes memory _cmd,
193
+ EVMCallRequestV1 memory _request
194
+ ) internal pure returns (bytes memory) {
195
+ bytes memory newCmd = abi.encodePacked(
196
+ _cmd,
197
+ REQUEST_VERSION,
198
+ _request.appRequestLabel,
199
+ RESOLVER_TYPE_SINGLE_VIEW_EVM_CALL,
200
+ (_request.callData.length + 35).toUint16(),
201
+ _request.targetEid
202
+ );
203
+ return
204
+ abi.encodePacked(
205
+ newCmd,
206
+ _request.isBlockNum,
207
+ _request.blockNumOrTimestamp,
208
+ _request.confirmations,
209
+ _request.to,
210
+ _request.callData
211
+ );
212
+ }
213
+
214
+ function appendEVMCallComputeV1(
215
+ bytes memory _cmd,
216
+ EVMCallComputeV1 memory _compute
217
+ ) internal pure returns (bytes memory) {
218
+ return
219
+ abi.encodePacked(
220
+ _cmd,
221
+ COMPUTE_VERSION,
222
+ COMPUTE_TYPE_SINGLE_VIEW_EVM_CALL,
223
+ _compute.computeSetting,
224
+ _compute.targetEid,
225
+ _compute.isBlockNum,
226
+ _compute.blockNumOrTimestamp,
227
+ _compute.confirmations,
228
+ _compute.to
229
+ );
230
+ }
231
+ }