@layerzerolabs/lz-evm-protocol-v2 2.0.2

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 (51) hide show
  1. package/artifacts/contracts/EndpointV2.sol/EndpointV2.json +2364 -0
  2. package/artifacts/contracts/EndpointV2Alt.sol/EndpointV2Alt.json +2379 -0
  3. package/artifacts/contracts/MessageLibManager.sol/MessageLibManager.json +783 -0
  4. package/artifacts/contracts/MessagingChannel.sol/MessagingChannel.json +426 -0
  5. package/artifacts/contracts/MessagingComposer.sol/MessagingComposer.json +320 -0
  6. package/artifacts/contracts/MessagingContext.sol/MessagingContext.json +42 -0
  7. package/artifacts/contracts/interfaces/ILayerZeroComposer.sol/ILayerZeroComposer.json +44 -0
  8. package/artifacts/contracts/interfaces/ILayerZeroEndpointV2.sol/ILayerZeroEndpointV2.json +1844 -0
  9. package/artifacts/contracts/interfaces/ILayerZeroReceiver.sol/ILayerZeroReceiver.json +121 -0
  10. package/artifacts/contracts/interfaces/IMessageLib.sol/IMessageLib.json +149 -0
  11. package/artifacts/contracts/interfaces/IMessageLibManager.sol/IMessageLibManager.json +629 -0
  12. package/artifacts/contracts/interfaces/IMessagingChannel.sol/IMessagingChannel.json +344 -0
  13. package/artifacts/contracts/interfaces/IMessagingComposer.sol/IMessagingComposer.json +246 -0
  14. package/artifacts/contracts/interfaces/IMessagingContext.sol/IMessagingContext.json +42 -0
  15. package/artifacts/contracts/interfaces/ISendLib.sol/ISendLib.json +364 -0
  16. package/artifacts/contracts/libs/AddressCast.sol/AddressCast.json +10 -0
  17. package/artifacts/contracts/libs/CalldataBytesLib.sol/CalldataBytesLib.json +10 -0
  18. package/artifacts/contracts/libs/Errors.sol/Errors.json +226 -0
  19. package/artifacts/contracts/libs/GUID.sol/GUID.json +10 -0
  20. package/artifacts/contracts/libs/Transfer.sol/Transfer.json +32 -0
  21. package/artifacts/contracts/messagelib/BlockedMessageLib.sol/BlockedMessageLib.json +94 -0
  22. package/artifacts/contracts/messagelib/SimpleMessageLib.sol/SimpleMessageLib.json +591 -0
  23. package/artifacts/contracts/messagelib/libs/BitMaps.sol/BitMaps.json +10 -0
  24. package/artifacts/contracts/messagelib/libs/ExecutorOptions.sol/ExecutorOptions.json +26 -0
  25. package/artifacts/contracts/messagelib/libs/PacketV1Codec.sol/PacketV1Codec.json +10 -0
  26. package/contracts/EndpointV2.sol +403 -0
  27. package/contracts/EndpointV2Alt.sol +50 -0
  28. package/contracts/MessageLibManager.sol +326 -0
  29. package/contracts/MessagingChannel.sol +161 -0
  30. package/contracts/MessagingComposer.sol +80 -0
  31. package/contracts/MessagingContext.sol +36 -0
  32. package/contracts/interfaces/ILayerZeroComposer.sol +24 -0
  33. package/contracts/interfaces/ILayerZeroEndpointV2.sol +98 -0
  34. package/contracts/interfaces/ILayerZeroReceiver.sol +20 -0
  35. package/contracts/interfaces/IMessageLib.sol +26 -0
  36. package/contracts/interfaces/IMessageLibManager.sol +68 -0
  37. package/contracts/interfaces/IMessagingChannel.sol +32 -0
  38. package/contracts/interfaces/IMessagingComposer.sol +38 -0
  39. package/contracts/interfaces/IMessagingContext.sol +9 -0
  40. package/contracts/interfaces/ISendLib.sol +36 -0
  41. package/contracts/libs/AddressCast.sol +40 -0
  42. package/contracts/libs/CalldataBytesLib.sol +58 -0
  43. package/contracts/libs/Errors.sol +42 -0
  44. package/contracts/libs/GUID.sol +19 -0
  45. package/contracts/libs/Transfer.sol +34 -0
  46. package/contracts/messagelib/BlockedMessageLib.sol +30 -0
  47. package/contracts/messagelib/SimpleMessageLib.sol +149 -0
  48. package/contracts/messagelib/libs/BitMaps.sol +26 -0
  49. package/contracts/messagelib/libs/ExecutorOptions.sol +85 -0
  50. package/contracts/messagelib/libs/PacketV1Codec.sol +108 -0
  51. package/package.json +27 -0
@@ -0,0 +1,10 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "PacketV1Codec",
4
+ "sourceName": "contracts/messagelib/libs/PacketV1Codec.sol",
5
+ "abi": [],
6
+ "bytecode": "0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220558e797330ba36a43d9725d88890206b33f4a424d7cc6f00abac75cbee08993064736f6c63430008160033",
7
+ "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220558e797330ba36a43d9725d88890206b33f4a424d7cc6f00abac75cbee08993064736f6c63430008160033",
8
+ "linkReferences": {},
9
+ "deployedLinkReferences": {}
10
+ }
@@ -0,0 +1,403 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity 0.8.22;
4
+
5
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+
7
+ import { MessagingFee, MessagingParams, MessagingReceipt, Origin, ExecutionState, ILayerZeroEndpointV2 } from "./interfaces/ILayerZeroEndpointV2.sol";
8
+ import { ISendLib, Packet } from "./interfaces/ISendLib.sol";
9
+ import { ILayerZeroReceiver } from "./interfaces/ILayerZeroReceiver.sol";
10
+ import { Errors } from "./libs/Errors.sol";
11
+ import { GUID } from "./libs/GUID.sol";
12
+ import { Transfer } from "./libs/Transfer.sol";
13
+ import { MessagingChannel } from "./MessagingChannel.sol";
14
+ import { MessagingComposer } from "./MessagingComposer.sol";
15
+ import { MessageLibManager } from "./MessageLibManager.sol";
16
+ import { MessagingContext } from "./MessagingContext.sol";
17
+
18
+ // LayerZero EndpointV2 is fully backward compatible with LayerZero Endpoint(V1), but it also supports additional
19
+ // features that Endpoint(V1) does not support now and may not in the future. We have also changed some terminology
20
+ // to clarify pre-existing language that might have been confusing.
21
+ //
22
+ // The following is a list of terminology changes:
23
+ // -chainId -> eid
24
+ // - Rationale: chainId was a term we initially used to describe an endpoint on a specific chain. Since
25
+ // LayerZero supports non-EVMs we could not map the classic EVM chainIds to the LayerZero chainIds, making it
26
+ // confusing for developers. With the addition of EndpointV2 and its backward compatible nature, we would have
27
+ // two chainIds per chain that has Endpoint(V1), further confusing developers. We have decided to change the
28
+ // name to Endpoint Id, or eid, for simplicity and clarity.
29
+ // -adapterParams -> options
30
+ // -userApplication -> oapp. Omnichain Application
31
+ // -srcAddress -> sender
32
+ // -dstAddress -> receiver
33
+ // - Rationale: The sender/receiver on EVM is the address. However, on non-EVM chains, the sender/receiver could
34
+ // represented as a public key, or some other identifier. The term sender/receiver is more generic
35
+ // -payload -> message.
36
+ // - Rationale: The term payload is used in the context of a packet, which is a combination of the message and GUID
37
+ contract EndpointV2 is ILayerZeroEndpointV2, MessagingChannel, MessageLibManager, MessagingComposer, MessagingContext {
38
+ address public lzToken;
39
+
40
+ mapping(address oapp => address delegate) public delegates;
41
+
42
+ /// @param _eid the unique Endpoint Id for this deploy that all other Endpoints can use to send to it
43
+ constructor(uint32 _eid, address _owner) MessagingChannel(_eid) {
44
+ _transferOwnership(_owner);
45
+ }
46
+
47
+ /// @dev MESSAGING STEP 0
48
+ /// @notice This view function gives the application built on top of LayerZero the ability to requests a quote
49
+ /// with the same parameters as they would to send their message. Since the quotes are given on chain there is a
50
+ /// race condition in which the prices could change between the time the user gets their quote and the time they
51
+ /// submit their message. If the price moves up and the user doesn't send enough funds the transaction will revert,
52
+ /// if the price goes down the _refundAddress provided by the app will be refunded the difference.
53
+ /// @param _params the messaging parameters
54
+ /// @param _sender the sender of the message
55
+ function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory) {
56
+ // lzToken must be set to support payInLzToken
57
+ if (_params.payInLzToken && lzToken == address(0x0)) revert Errors.LzTokenUnavailable();
58
+
59
+ // get the correct outbound nonce
60
+ uint64 nonce = outboundNonce[_sender][_params.dstEid][_params.receiver] + 1;
61
+
62
+ // construct the packet with a GUID
63
+ Packet memory packet = Packet({
64
+ nonce: nonce,
65
+ srcEid: eid,
66
+ sender: _sender,
67
+ dstEid: _params.dstEid,
68
+ receiver: _params.receiver,
69
+ guid: GUID.generate(nonce, eid, _sender, _params.dstEid, _params.receiver),
70
+ message: _params.message
71
+ });
72
+
73
+ // get the send library by sender and dst eid
74
+ // use _ to avoid variable shadowing
75
+ address _sendLibrary = getSendLibrary(_sender, _params.dstEid);
76
+
77
+ return ISendLib(_sendLibrary).quote(packet, _params.options, _params.payInLzToken);
78
+ }
79
+
80
+ /// @dev MESSAGING STEP 1 - OApp need to transfer the fees to the endpoint before sending the message
81
+ /// @param _params the messaging parameters
82
+ /// @param _refundAddress the address to refund both the native and lzToken
83
+ function send(
84
+ MessagingParams calldata _params,
85
+ address _refundAddress
86
+ ) external payable sendContext(_params.dstEid, msg.sender) returns (MessagingReceipt memory) {
87
+ if (_params.payInLzToken && lzToken == address(0x0)) revert Errors.LzTokenUnavailable();
88
+
89
+ // send message
90
+ (MessagingReceipt memory receipt, address _sendLibrary) = _send(msg.sender, _params);
91
+
92
+ // OApp can simulate with 0 native value it will fail with error including the required fee, which can be provided in the actual call
93
+ // this trick can be used to avoid the need to write the quote() function
94
+ // however, without the quote view function it will be hard to compose an oapp on chain
95
+ uint256 suppliedNative = _suppliedNative();
96
+ uint256 suppliedLzToken = _suppliedLzToken(_params.payInLzToken);
97
+ _assertMessagingFee(receipt.fee, suppliedNative, suppliedLzToken);
98
+
99
+ // handle lz token fees
100
+ _payToken(lzToken, receipt.fee.lzTokenFee, suppliedLzToken, _sendLibrary, _refundAddress);
101
+
102
+ // handle native fees
103
+ _payNative(receipt.fee.nativeFee, suppliedNative, _sendLibrary, _refundAddress);
104
+
105
+ return receipt;
106
+ }
107
+
108
+ /// @dev internal function for sending the messages used by all external send methods
109
+ /// @param _sender the address of the application sending the message to the destination chain
110
+ /// @param _params the messaging parameters
111
+ function _send(
112
+ address _sender,
113
+ MessagingParams calldata _params
114
+ ) internal returns (MessagingReceipt memory, address) {
115
+ // get the correct outbound nonce
116
+ uint64 latestNonce = _outbound(_sender, _params.dstEid, _params.receiver);
117
+
118
+ // construct the packet with a GUID
119
+ Packet memory packet = Packet({
120
+ nonce: latestNonce,
121
+ srcEid: eid,
122
+ sender: _sender,
123
+ dstEid: _params.dstEid,
124
+ receiver: _params.receiver,
125
+ guid: GUID.generate(latestNonce, eid, _sender, _params.dstEid, _params.receiver),
126
+ message: _params.message
127
+ });
128
+
129
+ // get the send library by sender and dst eid
130
+ address _sendLibrary = getSendLibrary(_sender, _params.dstEid);
131
+
132
+ // messageLib always returns encodedPacket with guid
133
+ (MessagingFee memory fee, bytes memory encodedPacket) = ISendLib(_sendLibrary).send(
134
+ packet,
135
+ _params.options,
136
+ _params.payInLzToken
137
+ );
138
+
139
+ // Emit packet information for DVNs, Executors, and any other offchain infrastructure to only listen
140
+ // for this one event to perform their actions.
141
+ emit PacketSent(encodedPacket, _params.options, _sendLibrary);
142
+
143
+ return (MessagingReceipt(packet.guid, latestNonce, fee), _sendLibrary);
144
+ }
145
+
146
+ /// @dev MESSAGING STEP 2 - on the destination chain
147
+ /// @dev configured receive library verifies a message
148
+ /// @param _origin a struct holding the srcEid, nonce, and sender of the message
149
+ /// @param _receiver the receiver of the message
150
+ /// @param _payloadHash the payload hash of the message
151
+ function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external {
152
+ if (!isValidReceiveLibrary(_receiver, _origin.srcEid, msg.sender)) revert Errors.InvalidReceiveLibrary();
153
+
154
+ uint64 lazyNonce = lazyInboundNonce[_receiver][_origin.srcEid][_origin.sender];
155
+ if (!_initializable(_origin, _receiver, lazyNonce)) revert Errors.PathNotInitializable();
156
+ if (!_verifiable(_origin, _receiver, lazyNonce)) revert Errors.PathNotVerifiable();
157
+
158
+ // insert the message into the message channel
159
+ _inbound(_receiver, _origin.srcEid, _origin.sender, _origin.nonce, _payloadHash);
160
+ emit PacketVerified(_origin, _receiver, _payloadHash);
161
+ }
162
+
163
+ /// @dev MESSAGING STEP 3 - the last step
164
+ /// @dev execute a verified message to the designated receiver
165
+ /// @dev the execution provides the execution context (caller, extraData) to the receiver. the receiver can optionally assert the caller and validate the untrusted extraData
166
+ /// @dev cant reentrant because the payload is cleared before execution
167
+ /// @param _origin the origin of the message
168
+ /// @param _receiver the receiver of the message
169
+ /// @param _guid the guid of the message
170
+ /// @param _message the message
171
+ /// @param _extraData the extra data provided by the executor. this data is untrusted and should be validated.
172
+ function lzReceive(
173
+ Origin calldata _origin,
174
+ address _receiver,
175
+ bytes32 _guid,
176
+ bytes calldata _message,
177
+ bytes calldata _extraData
178
+ ) external payable {
179
+ // clear the payload first to prevent reentrancy, and then execute the message
180
+ _clearPayload(_receiver, _origin.srcEid, _origin.sender, _origin.nonce, abi.encodePacked(_guid, _message));
181
+ ILayerZeroReceiver(_receiver).lzReceive{ value: msg.value }(_origin, _guid, _message, msg.sender, _extraData);
182
+ emit PacketDelivered(_origin, _receiver);
183
+ }
184
+
185
+ /// @param _origin the origin of the message
186
+ /// @param _receiver the receiver of the message
187
+ /// @param _guid the guid of the message
188
+ /// @param _message the message
189
+ /// @param _extraData the extra data provided by the executor.
190
+ /// @param _reason the reason for failure
191
+ function lzReceiveAlert(
192
+ Origin calldata _origin,
193
+ address _receiver,
194
+ bytes32 _guid,
195
+ uint256 _gas,
196
+ uint256 _value,
197
+ bytes calldata _message,
198
+ bytes calldata _extraData,
199
+ bytes calldata _reason
200
+ ) external {
201
+ emit LzReceiveAlert(_receiver, msg.sender, _origin, _guid, _gas, _value, _message, _extraData, _reason);
202
+ }
203
+
204
+ /// @dev Oapp uses this interface to clear a message.
205
+ /// @dev this is a PULL mode versus the PUSH mode of lzReceive
206
+ /// @dev the cleared message can be ignored by the app (effectively burnt)
207
+ /// @dev authenticated by oapp
208
+ /// @param _origin the origin of the message
209
+ /// @param _guid the guid of the message
210
+ /// @param _message the message
211
+ function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external {
212
+ _assertAuthorized(_oapp);
213
+
214
+ bytes memory payload = abi.encodePacked(_guid, _message);
215
+ _clearPayload(_oapp, _origin.srcEid, _origin.sender, _origin.nonce, payload);
216
+ emit PacketDelivered(_origin, _oapp);
217
+ }
218
+
219
+ /// @dev allows reconfiguration to recover from wrong configurations
220
+ /// @dev users should never approve the EndpointV2 contract to spend their non-layerzero tokens
221
+ /// @dev override this function if the endpoint is charging ERC20 tokens as native
222
+ /// @dev only owner
223
+ /// @param _lzToken the new layer zero token address
224
+ function setLzToken(address _lzToken) public virtual onlyOwner {
225
+ lzToken = _lzToken;
226
+ emit LzTokenSet(_lzToken);
227
+ }
228
+
229
+ /// @dev recover the token sent to this contract by mistake
230
+ /// @dev only owner
231
+ /// @param _token the token to recover. if 0x0 then it is native token
232
+ /// @param _to the address to send the token to
233
+ /// @param _amount the amount to send
234
+ function recoverToken(address _token, address _to, uint256 _amount) external onlyOwner {
235
+ Transfer.nativeOrToken(_token, _to, _amount);
236
+ }
237
+
238
+ /// @dev handling token payments on endpoint. the sender must approve the endpoint to spend the token
239
+ /// @dev internal function
240
+ /// @param _token the token to pay
241
+ /// @param _required the amount required
242
+ /// @param _supplied the amount supplied
243
+ /// @param _receiver the receiver of the token
244
+ function _payToken(
245
+ address _token,
246
+ uint256 _required,
247
+ uint256 _supplied,
248
+ address _receiver,
249
+ address _refundAddress
250
+ ) internal {
251
+ if (_required > 0) {
252
+ Transfer.token(_token, _receiver, _required);
253
+ }
254
+ if (_required < _supplied) {
255
+ unchecked {
256
+ // refund the excess
257
+ Transfer.token(_token, _refundAddress, _supplied - _required);
258
+ }
259
+ }
260
+ }
261
+
262
+ /// @dev handling native token payments on endpoint
263
+ /// @dev override this if the endpoint is charging ERC20 tokens as native
264
+ /// @dev internal function
265
+ /// @param _required the amount required
266
+ /// @param _supplied the amount supplied
267
+ /// @param _receiver the receiver of the native token
268
+ /// @param _refundAddress the address to refund the excess to
269
+ function _payNative(
270
+ uint256 _required,
271
+ uint256 _supplied,
272
+ address _receiver,
273
+ address _refundAddress
274
+ ) internal virtual {
275
+ if (_required > 0) {
276
+ Transfer.native(_receiver, _required);
277
+ }
278
+ if (_required < _supplied) {
279
+ unchecked {
280
+ // refund the excess
281
+ Transfer.native(_refundAddress, _supplied - _required);
282
+ }
283
+ }
284
+ }
285
+
286
+ /// @dev get the balance of the lzToken as the supplied lzToken fee if payInLzToken is true
287
+ function _suppliedLzToken(bool _payInLzToken) internal view returns (uint256 supplied) {
288
+ if (_payInLzToken) {
289
+ supplied = IERC20(lzToken).balanceOf(address(this));
290
+
291
+ // if payInLzToken is true, the supplied fee must be greater than 0 to prevent a race condition
292
+ // in which an oapp sending a message with lz token and the lz token is set to a new token between the tx
293
+ // being sent and the tx being mined. if the required lz token fee is 0 and the old lz token would be
294
+ // locked in the contract instead of being refunded
295
+ if (supplied == 0) revert Errors.ZeroLzTokenFee();
296
+ }
297
+ }
298
+
299
+ /// @dev override this if the endpoint is charging ERC20 tokens as native
300
+ function _suppliedNative() internal view virtual returns (uint256) {
301
+ return msg.value;
302
+ }
303
+
304
+ /// @dev Assert the required fees and the supplied fees are enough
305
+ function _assertMessagingFee(
306
+ MessagingFee memory _required,
307
+ uint256 _suppliedNativeFee,
308
+ uint256 _suppliedLzTokenFee
309
+ ) internal pure {
310
+ if (_required.nativeFee > _suppliedNativeFee || _required.lzTokenFee > _suppliedLzTokenFee) {
311
+ revert Errors.InsufficientFee(
312
+ _required.nativeFee,
313
+ _suppliedNativeFee,
314
+ _required.lzTokenFee,
315
+ _suppliedLzTokenFee
316
+ );
317
+ }
318
+ }
319
+
320
+ /// @dev override this if the endpoint is charging ERC20 tokens as native
321
+ /// @return 0x0 if using native. otherwise the address of the native ERC20 token
322
+ function nativeToken() external view virtual returns (address) {
323
+ return address(0x0);
324
+ }
325
+
326
+ function setDelegate(address _delegate) external {
327
+ delegates[msg.sender] = _delegate;
328
+ }
329
+
330
+ // ========================= Internal =========================
331
+ function _initializable(
332
+ Origin calldata _origin,
333
+ address _receiver,
334
+ uint64 _lazyInboundNonce
335
+ ) internal view returns (bool) {
336
+ return
337
+ _lazyInboundNonce > 0 || // allowInitializePath already checked
338
+ ILayerZeroReceiver(_receiver).allowInitializePath(_origin);
339
+ }
340
+
341
+ /// @dev bytes(0) payloadHash can never be submitted
342
+ function _verifiable(
343
+ Origin calldata _origin,
344
+ address _receiver,
345
+ uint64 _lazyInboundNonce
346
+ ) internal view returns (bool) {
347
+ return
348
+ _origin.nonce > _lazyInboundNonce || // either initializing an empty slot or reverifying
349
+ inboundPayloadHash[_receiver][_origin.srcEid][_origin.sender][_origin.nonce] != EMPTY_PAYLOAD_HASH; // only allow reverifying if it hasn't been executed
350
+ }
351
+
352
+ /// @dev assert the caller to either be the oapp or the delegate
353
+ function _assertAuthorized(address _oapp) internal view override(MessagingChannel, MessageLibManager) {
354
+ if (msg.sender != _oapp && msg.sender != delegates[_oapp]) revert Errors.Unauthorized();
355
+ }
356
+
357
+ // ========================= VIEW FUNCTIONS FOR OFFCHAIN ONLY =========================
358
+ // Not involved in any state transition function.
359
+ // ====================================================================================
360
+
361
+ /// @dev check if a message is verifiable.
362
+ function verifiable(
363
+ Origin calldata _origin,
364
+ address _receiver,
365
+ address _receiveLib,
366
+ bytes32 _payloadHash
367
+ ) external view returns (bool) {
368
+ if (!isValidReceiveLibrary(_receiver, _origin.srcEid, _receiveLib)) return false;
369
+
370
+ uint64 lazyNonce = lazyInboundNonce[_receiver][_origin.srcEid][_origin.sender];
371
+ if (!_initializable(_origin, _receiver, lazyNonce)) return false;
372
+ if (!_verifiable(_origin, _receiver, lazyNonce)) return false;
373
+
374
+ // checked in _inbound for verify
375
+ if (_payloadHash == EMPTY_PAYLOAD_HASH) return false;
376
+
377
+ return true;
378
+ }
379
+
380
+ /// @dev check if a message is executable.
381
+ /// @return ExecutionState of Executed, Executable, or NotExecutable
382
+ function executable(Origin calldata _origin, address _receiver) external view returns (ExecutionState) {
383
+ bytes32 payloadHash = inboundPayloadHash[_receiver][_origin.srcEid][_origin.sender][_origin.nonce];
384
+
385
+ // executed if the payload hash has been cleared and the nonce is less than or equal to lazyInboundNonce
386
+ if (
387
+ payloadHash == EMPTY_PAYLOAD_HASH &&
388
+ _origin.nonce <= lazyInboundNonce[_receiver][_origin.srcEid][_origin.sender]
389
+ ) {
390
+ return ExecutionState.Executed;
391
+ }
392
+
393
+ // executable if nonce has not been executed and is not nil and nonce is less than or equal to inboundNonce
394
+ if (
395
+ payloadHash != NIL_PAYLOAD_HASH && _origin.nonce <= inboundNonce(_receiver, _origin.srcEid, _origin.sender)
396
+ ) {
397
+ return ExecutionState.Executable;
398
+ }
399
+
400
+ // return NotExecutable as a catch-all
401
+ return ExecutionState.NotExecutable;
402
+ }
403
+ }
@@ -0,0 +1,50 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity 0.8.22;
4
+
5
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+
7
+ import { EndpointV2 } from "./EndpointV2.sol";
8
+ import { Errors } from "./libs/Errors.sol";
9
+
10
+ /// @notice this is the endpoint contract for layerzero v2 deployed on chains using ERC20 as native tokens
11
+ contract EndpointV2Alt is EndpointV2 {
12
+ /// @dev the altFeeToken is used for fees when the native token has no value
13
+ /// @dev it is immutable for gas saving. only 1 endpoint for such chains
14
+ address internal immutable nativeErc20;
15
+
16
+ constructor(uint32 _eid, address _owner, address _altToken) EndpointV2(_eid, _owner) {
17
+ nativeErc20 = _altToken;
18
+ }
19
+
20
+ /// @dev handling native token payments on endpoint
21
+ /// @dev internal function
22
+ /// @param _required the amount required
23
+ /// @param _supplied the amount supplied
24
+ /// @param _receiver the receiver of the native token
25
+ /// @param _refundAddress the address to refund the excess to
26
+ function _payNative(
27
+ uint256 _required,
28
+ uint256 _supplied,
29
+ address _receiver,
30
+ address _refundAddress
31
+ ) internal override {
32
+ if (msg.value > 0) revert Errors.OnlyAltToken();
33
+ _payToken(nativeErc20, _required, _supplied, _receiver, _refundAddress);
34
+ }
35
+
36
+ /// @dev return the balance of the native token
37
+ function _suppliedNative() internal view override returns (uint256) {
38
+ return IERC20(nativeErc20).balanceOf(address(this));
39
+ }
40
+
41
+ /// @dev check if lzToken is set to the same address
42
+ function setLzToken(address _lzToken) public override onlyOwner {
43
+ if (_lzToken == nativeErc20) revert Errors.InvalidArgument();
44
+ super.setLzToken(_lzToken);
45
+ }
46
+
47
+ function nativeToken() external view override returns (address) {
48
+ return nativeErc20;
49
+ }
50
+ }