@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.
- package/artifacts/contracts/EndpointV2.sol/EndpointV2.json +2364 -0
- package/artifacts/contracts/EndpointV2Alt.sol/EndpointV2Alt.json +2379 -0
- package/artifacts/contracts/MessageLibManager.sol/MessageLibManager.json +783 -0
- package/artifacts/contracts/MessagingChannel.sol/MessagingChannel.json +426 -0
- package/artifacts/contracts/MessagingComposer.sol/MessagingComposer.json +320 -0
- package/artifacts/contracts/MessagingContext.sol/MessagingContext.json +42 -0
- package/artifacts/contracts/interfaces/ILayerZeroComposer.sol/ILayerZeroComposer.json +44 -0
- package/artifacts/contracts/interfaces/ILayerZeroEndpointV2.sol/ILayerZeroEndpointV2.json +1844 -0
- package/artifacts/contracts/interfaces/ILayerZeroReceiver.sol/ILayerZeroReceiver.json +121 -0
- package/artifacts/contracts/interfaces/IMessageLib.sol/IMessageLib.json +149 -0
- package/artifacts/contracts/interfaces/IMessageLibManager.sol/IMessageLibManager.json +629 -0
- package/artifacts/contracts/interfaces/IMessagingChannel.sol/IMessagingChannel.json +344 -0
- package/artifacts/contracts/interfaces/IMessagingComposer.sol/IMessagingComposer.json +246 -0
- package/artifacts/contracts/interfaces/IMessagingContext.sol/IMessagingContext.json +42 -0
- package/artifacts/contracts/interfaces/ISendLib.sol/ISendLib.json +364 -0
- package/artifacts/contracts/libs/AddressCast.sol/AddressCast.json +10 -0
- package/artifacts/contracts/libs/CalldataBytesLib.sol/CalldataBytesLib.json +10 -0
- package/artifacts/contracts/libs/Errors.sol/Errors.json +226 -0
- package/artifacts/contracts/libs/GUID.sol/GUID.json +10 -0
- package/artifacts/contracts/libs/Transfer.sol/Transfer.json +32 -0
- package/artifacts/contracts/messagelib/BlockedMessageLib.sol/BlockedMessageLib.json +94 -0
- package/artifacts/contracts/messagelib/SimpleMessageLib.sol/SimpleMessageLib.json +591 -0
- package/artifacts/contracts/messagelib/libs/BitMaps.sol/BitMaps.json +10 -0
- package/artifacts/contracts/messagelib/libs/ExecutorOptions.sol/ExecutorOptions.json +26 -0
- package/artifacts/contracts/messagelib/libs/PacketV1Codec.sol/PacketV1Codec.json +10 -0
- package/contracts/EndpointV2.sol +403 -0
- package/contracts/EndpointV2Alt.sol +50 -0
- package/contracts/MessageLibManager.sol +326 -0
- package/contracts/MessagingChannel.sol +161 -0
- package/contracts/MessagingComposer.sol +80 -0
- package/contracts/MessagingContext.sol +36 -0
- package/contracts/interfaces/ILayerZeroComposer.sol +24 -0
- package/contracts/interfaces/ILayerZeroEndpointV2.sol +98 -0
- package/contracts/interfaces/ILayerZeroReceiver.sol +20 -0
- package/contracts/interfaces/IMessageLib.sol +26 -0
- package/contracts/interfaces/IMessageLibManager.sol +68 -0
- package/contracts/interfaces/IMessagingChannel.sol +32 -0
- package/contracts/interfaces/IMessagingComposer.sol +38 -0
- package/contracts/interfaces/IMessagingContext.sol +9 -0
- package/contracts/interfaces/ISendLib.sol +36 -0
- package/contracts/libs/AddressCast.sol +40 -0
- package/contracts/libs/CalldataBytesLib.sol +58 -0
- package/contracts/libs/Errors.sol +42 -0
- package/contracts/libs/GUID.sol +19 -0
- package/contracts/libs/Transfer.sol +34 -0
- package/contracts/messagelib/BlockedMessageLib.sol +30 -0
- package/contracts/messagelib/SimpleMessageLib.sol +149 -0
- package/contracts/messagelib/libs/BitMaps.sol +26 -0
- package/contracts/messagelib/libs/ExecutorOptions.sol +85 -0
- package/contracts/messagelib/libs/PacketV1Codec.sol +108 -0
- 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
|
+
}
|