@layerzerolabs/protocol-v2-messagelib-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 (88) hide show
  1. package/README.md +15 -0
  2. package/contracts/Executor.sol +339 -0
  3. package/contracts/ExecutorFeeLib.sol +241 -0
  4. package/contracts/MessageLibBase.sol +21 -0
  5. package/contracts/PriceFeed.sol +304 -0
  6. package/contracts/ReceiveLibBaseE2.sol +28 -0
  7. package/contracts/SendLibBase.sol +258 -0
  8. package/contracts/SendLibBaseE2.sol +140 -0
  9. package/contracts/SimpleReadExecutor.sol +110 -0
  10. package/contracts/Treasury.sol +74 -0
  11. package/contracts/Worker.sol +167 -0
  12. package/contracts/decompressor/DVNDecoder.sol +52 -0
  13. package/contracts/decompressor/DVNDecompressor.sol +70 -0
  14. package/contracts/decompressor/DecompressorExtension.sol +204 -0
  15. package/contracts/decompressor/ExecutorDecoder.sol +147 -0
  16. package/contracts/decompressor/ExecutorDecompressor.sol +58 -0
  17. package/contracts/interfaces/IExecutor.sol +47 -0
  18. package/contracts/interfaces/IExecutorFeeLib.sol +56 -0
  19. package/contracts/interfaces/ILayerZeroExecutor.sol +29 -0
  20. package/contracts/interfaces/ILayerZeroPriceFeed.sol +61 -0
  21. package/contracts/interfaces/ILayerZeroReadExecutor.sol +15 -0
  22. package/contracts/interfaces/ILayerZeroTreasury.sol +19 -0
  23. package/contracts/interfaces/IWorker.sol +29 -0
  24. package/contracts/libs/ExecutorOptions.sol +104 -0
  25. package/contracts/libs/SafeCall.sol +123 -0
  26. package/contracts/lowlat/EssenceDVNWrapper.sol +77 -0
  27. package/contracts/lowlat/MultiCall.sol +42 -0
  28. package/contracts/mocks/DVNMock.sol +28 -0
  29. package/contracts/mocks/ExecutorMock.sol +95 -0
  30. package/contracts/uln/LzExecutor.sol +129 -0
  31. package/contracts/uln/ReceiveUlnBase.sol +125 -0
  32. package/contracts/uln/SendUlnBase.sol +129 -0
  33. package/contracts/uln/UlnBase.sol +195 -0
  34. package/contracts/uln/dvn/DVN.sol +393 -0
  35. package/contracts/uln/dvn/DVNFeeLib.sol +288 -0
  36. package/contracts/uln/dvn/DeadDVN.sol +30 -0
  37. package/contracts/uln/dvn/MultiSig.sol +135 -0
  38. package/contracts/uln/dvn/SimpleReadDVN.sol +112 -0
  39. package/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapter.sol +151 -0
  40. package/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapterFeeLib.sol +71 -0
  41. package/contracts/uln/dvn/adapters/DVNAdapterBase.sol +114 -0
  42. package/contracts/uln/dvn/adapters/arbitrum/ArbitrumDVNAdapterL1.sol +126 -0
  43. package/contracts/uln/dvn/adapters/arbitrum/ArbitrumDVNAdapterL2.sol +50 -0
  44. package/contracts/uln/dvn/adapters/avalanche/NOTES.md +4 -0
  45. package/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapter.sol +152 -0
  46. package/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapterFeeLib.sol +127 -0
  47. package/contracts/uln/dvn/adapters/libs/DVNAdapterMessageCodec.sol +41 -0
  48. package/contracts/uln/dvn/adapters/optimism/OptimismDVNAdapterL1.sol +86 -0
  49. package/contracts/uln/dvn/adapters/optimism/OptimismDVNAdapterL2.sol +58 -0
  50. package/contracts/uln/interfaces/IDVN.sol +26 -0
  51. package/contracts/uln/interfaces/IDVNFeeLib.sol +56 -0
  52. package/contracts/uln/interfaces/ILayerZeroDVN.sol +34 -0
  53. package/contracts/uln/interfaces/ILayerZeroReadDVN.sol +27 -0
  54. package/contracts/uln/interfaces/IReceiveUlnE2.sol +13 -0
  55. package/contracts/uln/interfaces/adapters/IAxelarDVNAdapter.sol +50 -0
  56. package/contracts/uln/interfaces/adapters/IAxelarDVNAdapterFeeLib.sol +49 -0
  57. package/contracts/uln/interfaces/adapters/ICCIPDVNAdapter.sol +34 -0
  58. package/contracts/uln/interfaces/adapters/ICCIPDVNAdapterFeeLib.sol +46 -0
  59. package/contracts/uln/libs/DVNOptions.sol +181 -0
  60. package/contracts/uln/libs/ReadCmdCodecV1.sol +136 -0
  61. package/contracts/uln/libs/SupportedCmdTypes.sol +29 -0
  62. package/contracts/uln/libs/UlnOptions.sol +176 -0
  63. package/contracts/uln/readlib/ReadLib1002.sol +518 -0
  64. package/contracts/uln/readlib/ReadLib1002View.sol +98 -0
  65. package/contracts/uln/readlib/ReadLibBase.sol +187 -0
  66. package/contracts/uln/uln301/AddressSizeConfig.sol +22 -0
  67. package/contracts/uln/uln301/ReceiveLibBaseE1.sol +103 -0
  68. package/contracts/uln/uln301/ReceiveUln301.sol +83 -0
  69. package/contracts/uln/uln301/ReceiveUln301View.sol +89 -0
  70. package/contracts/uln/uln301/SendLibBaseE1.sol +194 -0
  71. package/contracts/uln/uln301/SendUln301.sol +99 -0
  72. package/contracts/uln/uln301/TreasuryFeeHandler.sol +41 -0
  73. package/contracts/uln/uln301/interfaces/IMessageLibE1.sol +22 -0
  74. package/contracts/uln/uln301/interfaces/INonceContract.sol +7 -0
  75. package/contracts/uln/uln301/interfaces/ITreasuryFeeHandler.sol +13 -0
  76. package/contracts/uln/uln301/interfaces/IUltraLightNode301.sol +7 -0
  77. package/contracts/uln/uln301/mocks/NonceContractMock.sol +23 -0
  78. package/contracts/uln/uln302/ReceiveUln302.sol +85 -0
  79. package/contracts/uln/uln302/ReceiveUln302View.sol +89 -0
  80. package/contracts/uln/uln302/SendUln302.sol +86 -0
  81. package/contracts/upgradeable/WorkerUpgradeable.sol +186 -0
  82. package/contracts/upgradeable/proxy/ProxyAdmin.sol +96 -0
  83. package/contracts/upgradeable/proxy/TransparentUpgradeableProxy.sol +131 -0
  84. package/dist/index.d.ts +7 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +6 -0
  87. package/dist/index.js.map +1 -0
  88. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `@layerzerolabs/protocol-v2-messagelib-evm-contracts`
2
+
3
+ LayerZero V2 EVM message libraries (ULN 301/302, read library), DVN, executor, price feed and treasury contracts. Its sources import `@layerzerolabs/lz-evm-protocol-v2` and `@layerzerolabs/lz-evm-v1-0.7`, which resolve to the workspace copies through package aliases, so the source names, and with them the metadata hashes, stay as published.
4
+
5
+ Copied byte for byte from `@layerzerolabs/lz-evm-messagelib-v2` (`packages/layerzero-v2/evm/messagelib`) in the old LayerZero-Labs/monorepo at `origin/cb/executor-upgrade` (`d976cafeaf`). Only `contracts/` and the Foundry tests are copied; the old deploy scripts, deployment records, typechain bindings and Hardhat tests are not.
6
+
7
+ ## Build
8
+
9
+ `pnpm build` compiles with the old compiler settings (`hardhat.config.cjs`) into `artifacts/` (EVM), `artifacts-zk/` (zkSync) and `artifacts-tron/` (Tron), which the package publishes for consumers to read by path, e.g. `@layerzerolabs/protocol-v2-messagelib-evm-contracts/artifacts/contracts/<Source>.sol/<Contract>.json`.
10
+
11
+ `../scripts/compare-artifacts.mjs` compares the compiled bytecode with the artifacts the old monorepo published.
12
+
13
+ ## Test
14
+
15
+ `pnpm test` runs the Foundry tests.
@@ -0,0 +1,339 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
6
+ import { Proxied } from "hardhat-deploy/solc_0.8/proxy/Proxied.sol";
7
+
8
+ import { Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
9
+ import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
10
+
11
+ import { IUltraLightNode301 } from "./uln/uln301/interfaces/IUltraLightNode301.sol";
12
+ import { IExecutor } from "./interfaces/IExecutor.sol";
13
+ import { IExecutorFeeLib } from "./interfaces/IExecutorFeeLib.sol";
14
+ import { WorkerUpgradeable } from "./upgradeable/WorkerUpgradeable.sol";
15
+
16
+ interface ILayerZeroEndpointV2 {
17
+ function eid() external view returns (uint32);
18
+
19
+ function lzReceive(
20
+ Origin calldata _origin,
21
+ address _receiver,
22
+ bytes32 _guid,
23
+ bytes calldata _message,
24
+ bytes calldata _extraData
25
+ ) external payable;
26
+
27
+ function lzReceiveAlert(
28
+ Origin calldata _origin,
29
+ address _receiver,
30
+ bytes32 _guid,
31
+ uint256 _gas,
32
+ uint256 _value,
33
+ bytes calldata _message,
34
+ bytes calldata _extraData,
35
+ bytes calldata _reason
36
+ ) external;
37
+
38
+ function lzCompose(
39
+ address _from,
40
+ address _to,
41
+ bytes32 _guid,
42
+ uint16 _index,
43
+ bytes calldata _message,
44
+ bytes calldata _extraData
45
+ ) external payable;
46
+
47
+ function lzComposeAlert(
48
+ address _from,
49
+ address _to,
50
+ bytes32 _guid,
51
+ uint16 _index,
52
+ uint256 _gas,
53
+ uint256 _value,
54
+ bytes calldata _message,
55
+ bytes calldata _extraData,
56
+ bytes calldata _reason
57
+ ) external;
58
+ }
59
+
60
+ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IExecutor {
61
+ using PacketV1Codec for bytes;
62
+
63
+ mapping(uint32 dstEid => DstConfig) public dstConfig;
64
+
65
+ // endpoint v2
66
+ address public endpoint;
67
+ uint32 public localEidV2;
68
+
69
+ // endpoint v1
70
+ address public receiveUln301;
71
+
72
+ event ReceiveUln301Set(address receiveUln301);
73
+ event PriceFloorWhitelistSet(address indexed sender, bool whitelisted);
74
+
75
+ // Senders quoted and charged the multiplier price, with no USD floor.
76
+ mapping(address sender => bool whitelisted) public priceFloorWhitelist;
77
+
78
+ struct SetPriceFloorWhitelistParam {
79
+ address sender;
80
+ bool whitelisted;
81
+ }
82
+
83
+ function initialize(
84
+ address _endpoint,
85
+ address _receiveUln301,
86
+ address[] memory _messageLibs,
87
+ address _priceFeed,
88
+ address _roleAdmin,
89
+ address[] memory _admins
90
+ ) external proxied initializer {
91
+ __ReentrancyGuard_init();
92
+ __Worker_init(_messageLibs, _priceFeed, 12000, _roleAdmin, _admins);
93
+ endpoint = _endpoint;
94
+ localEidV2 = ILayerZeroEndpointV2(_endpoint).eid();
95
+ receiveUln301 = _receiveUln301;
96
+ }
97
+
98
+ function onUpgrade(address _receiveUln301) external proxied {
99
+ receiveUln301 = _receiveUln301;
100
+ }
101
+
102
+ // --- Admin ---
103
+ function setDstConfig(DstConfigParam[] memory _params) external onlyRole(ADMIN_ROLE) {
104
+ for (uint256 i = 0; i < _params.length; i++) {
105
+ DstConfigParam memory param = _params[i];
106
+ dstConfig[param.dstEid] = DstConfig(
107
+ param.lzReceiveBaseGas,
108
+ param.multiplierBps,
109
+ param.floorMarginUSD,
110
+ param.nativeCap,
111
+ param.lzComposeBaseGas
112
+ );
113
+ }
114
+ emit DstConfigSet(_params);
115
+ }
116
+
117
+ function setReceiveUln301(address _receiveUln301) external onlyRole(ADMIN_ROLE) {
118
+ receiveUln301 = _receiveUln301;
119
+ emit ReceiveUln301Set(_receiveUln301);
120
+ }
121
+
122
+ /// @notice Mark senders that pay the default multiplier price and skip the USD price floor.
123
+ function setPriceFloorWhitelist(SetPriceFloorWhitelistParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
124
+ for (uint256 i = 0; i < _params.length; i++) {
125
+ SetPriceFloorWhitelistParam calldata param = _params[i];
126
+ priceFloorWhitelist[param.sender] = param.whitelisted;
127
+ emit PriceFloorWhitelistSet(param.sender, param.whitelisted);
128
+ }
129
+ }
130
+
131
+ function nativeDrop(
132
+ Origin calldata _origin,
133
+ uint32 _dstEid,
134
+ address _oapp,
135
+ NativeDropParams[] calldata _nativeDropParams,
136
+ uint256 _nativeDropGasLimit
137
+ ) external payable onlyRole(ADMIN_ROLE) nonReentrant {
138
+ _nativeDrop(_origin, _dstEid, _oapp, _nativeDropParams, _nativeDropGasLimit);
139
+ }
140
+
141
+ function nativeDropAndExecute301(
142
+ Origin calldata _origin,
143
+ NativeDropParams[] calldata _nativeDropParams,
144
+ uint256 _nativeDropGasLimit,
145
+ bytes calldata _packet,
146
+ uint256 _gasLimit
147
+ ) external payable onlyRole(ADMIN_ROLE) nonReentrant {
148
+ _nativeDrop(_origin, _packet.dstEid(), _packet.receiverB20(), _nativeDropParams, _nativeDropGasLimit);
149
+ IUltraLightNode301(receiveUln301).commitVerification(_packet, _gasLimit);
150
+ }
151
+
152
+ function execute301(bytes calldata _packet, uint256 _gasLimit) external onlyRole(ADMIN_ROLE) nonReentrant {
153
+ IUltraLightNode301(receiveUln301).commitVerification(_packet, _gasLimit);
154
+ }
155
+
156
+ function execute302(ExecutionParams calldata _executionParams) external payable onlyRole(ADMIN_ROLE) nonReentrant {
157
+ try
158
+ ILayerZeroEndpointV2(endpoint).lzReceive{ value: msg.value, gas: _executionParams.gasLimit }(
159
+ _executionParams.origin,
160
+ _executionParams.receiver,
161
+ _executionParams.guid,
162
+ _executionParams.message,
163
+ _executionParams.extraData
164
+ )
165
+ {
166
+ // do nothing
167
+ } catch (bytes memory reason) {
168
+ ILayerZeroEndpointV2(endpoint).lzReceiveAlert(
169
+ _executionParams.origin,
170
+ _executionParams.receiver,
171
+ _executionParams.guid,
172
+ _executionParams.gasLimit,
173
+ msg.value,
174
+ _executionParams.message,
175
+ _executionParams.extraData,
176
+ reason
177
+ );
178
+ }
179
+ }
180
+
181
+ function compose302(
182
+ address _from,
183
+ address _to,
184
+ bytes32 _guid,
185
+ uint16 _index,
186
+ bytes calldata _message,
187
+ bytes calldata _extraData,
188
+ uint256 _gasLimit
189
+ ) external payable onlyRole(ADMIN_ROLE) nonReentrant {
190
+ try
191
+ ILayerZeroEndpointV2(endpoint).lzCompose{ value: msg.value, gas: _gasLimit }(
192
+ _from,
193
+ _to,
194
+ _guid,
195
+ _index,
196
+ _message,
197
+ _extraData
198
+ )
199
+ {
200
+ // do nothing
201
+ } catch (bytes memory reason) {
202
+ ILayerZeroEndpointV2(endpoint).lzComposeAlert(
203
+ _from,
204
+ _to,
205
+ _guid,
206
+ _index,
207
+ _gasLimit,
208
+ msg.value,
209
+ _message,
210
+ _extraData,
211
+ reason
212
+ );
213
+ }
214
+ }
215
+
216
+ function nativeDropAndExecute302(
217
+ NativeDropParams[] calldata _nativeDropParams,
218
+ uint256 _nativeDropGasLimit,
219
+ ExecutionParams calldata _executionParams
220
+ ) external payable onlyRole(ADMIN_ROLE) nonReentrant {
221
+ uint256 spent = _nativeDrop(
222
+ _executionParams.origin,
223
+ localEidV2,
224
+ _executionParams.receiver,
225
+ _nativeDropParams,
226
+ _nativeDropGasLimit
227
+ );
228
+
229
+ uint256 value = msg.value - spent;
230
+ try
231
+ ILayerZeroEndpointV2(endpoint).lzReceive{ value: value, gas: _executionParams.gasLimit }(
232
+ _executionParams.origin,
233
+ _executionParams.receiver,
234
+ _executionParams.guid,
235
+ _executionParams.message,
236
+ _executionParams.extraData
237
+ )
238
+ {
239
+ // do nothing
240
+ } catch (bytes memory reason) {
241
+ ILayerZeroEndpointV2(endpoint).lzReceiveAlert(
242
+ _executionParams.origin,
243
+ _executionParams.receiver,
244
+ _executionParams.guid,
245
+ _executionParams.gasLimit,
246
+ value,
247
+ _executionParams.message,
248
+ _executionParams.extraData,
249
+ reason
250
+ );
251
+ }
252
+ }
253
+
254
+ // --- Message Lib ---
255
+ function assignJob(
256
+ uint32 _dstEid,
257
+ address _sender,
258
+ uint256 _calldataSize,
259
+ bytes calldata _options
260
+ ) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
261
+ IExecutorFeeLib.FeeParams memory params = IExecutorFeeLib.FeeParams(
262
+ priceFeed,
263
+ _dstEid,
264
+ _sender,
265
+ _calldataSize,
266
+ defaultMultiplierBps
267
+ );
268
+ fee = IExecutorFeeLib(workerFeeLib).getFeeOnSend(params, _configForSender(_dstEid, _sender), _options);
269
+ }
270
+
271
+ // assignJob for ReadLib
272
+ function assignJob(
273
+ address _sender,
274
+ bytes calldata _options
275
+ ) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
276
+ IExecutorFeeLib.FeeParamsForRead memory params = IExecutorFeeLib.FeeParamsForRead(
277
+ priceFeed,
278
+ _sender,
279
+ defaultMultiplierBps
280
+ );
281
+ fee = IExecutorFeeLib(workerFeeLib).getFeeOnSend(params, _configForSender(localEidV2, _sender), _options);
282
+ }
283
+
284
+ // --- Only ACL ---
285
+ function getFee(
286
+ uint32 _dstEid,
287
+ address _sender,
288
+ uint256 _calldataSize,
289
+ bytes calldata _options
290
+ ) external view onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
291
+ IExecutorFeeLib.FeeParams memory params = IExecutorFeeLib.FeeParams(
292
+ priceFeed,
293
+ _dstEid,
294
+ _sender,
295
+ _calldataSize,
296
+ defaultMultiplierBps
297
+ );
298
+ fee = IExecutorFeeLib(workerFeeLib).getFee(params, _configForSender(_dstEid, _sender), _options);
299
+ }
300
+
301
+ function getFee(
302
+ address _sender,
303
+ bytes calldata _options
304
+ ) external view onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
305
+ IExecutorFeeLib.FeeParamsForRead memory params = IExecutorFeeLib.FeeParamsForRead(
306
+ priceFeed,
307
+ _sender,
308
+ defaultMultiplierBps
309
+ );
310
+ fee = IExecutorFeeLib(workerFeeLib).getFee(params, _configForSender(localEidV2, _sender), _options);
311
+ }
312
+
313
+ /// @dev Whitelisted senders keep multiplier pricing and have the USD floor cleared.
314
+ function _configForSender(uint32 _dstEid, address _sender) internal view returns (DstConfig memory config) {
315
+ config = dstConfig[_dstEid];
316
+ if (priceFloorWhitelist[_sender]) {
317
+ config.floorMarginUSD = 0;
318
+ }
319
+ }
320
+
321
+ function _nativeDrop(
322
+ Origin calldata _origin,
323
+ uint32 _dstEid,
324
+ address _oapp,
325
+ NativeDropParams[] calldata _nativeDropParams,
326
+ uint256 _nativeDropGasLimit
327
+ ) internal returns (uint256 spent) {
328
+ bool[] memory success = new bool[](_nativeDropParams.length);
329
+ for (uint256 i = 0; i < _nativeDropParams.length; i++) {
330
+ NativeDropParams memory param = _nativeDropParams[i];
331
+
332
+ (bool sent, ) = param.receiver.call{ value: param.amount, gas: _nativeDropGasLimit }("");
333
+
334
+ success[i] = sent;
335
+ spent += param.amount;
336
+ }
337
+ emit NativeDropApplied(_origin, _dstEid, _oapp, _nativeDropParams, success);
338
+ }
339
+ }
@@ -0,0 +1,241 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
6
+
7
+ import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
8
+
9
+ import { ILayerZeroPriceFeed } from "./interfaces/ILayerZeroPriceFeed.sol";
10
+ import { IExecutor } from "./interfaces/IExecutor.sol";
11
+ import { IExecutorFeeLib } from "./interfaces/IExecutorFeeLib.sol";
12
+ import { ExecutorOptions } from "./libs/ExecutorOptions.sol";
13
+
14
+ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
15
+ using ExecutorOptions for bytes;
16
+
17
+ uint256 private immutable nativeDecimalsRate;
18
+ uint32 private immutable localEidV2; // endpoint-v2 only, for read call
19
+
20
+ constructor(uint32 _localEidV2, uint256 _nativeDecimalsRate) {
21
+ localEidV2 = _localEidV2;
22
+ nativeDecimalsRate = _nativeDecimalsRate;
23
+ }
24
+
25
+ // ================================ OnlyOwner ================================
26
+ function withdrawToken(address _token, address _to, uint256 _amount) external onlyOwner {
27
+ // transfers native if _token is address(0x0)
28
+ Transfer.nativeOrToken(_token, _to, _amount);
29
+ }
30
+
31
+ // ================================ External ================================
32
+ function getFeeOnSend(
33
+ FeeParams calldata _params,
34
+ IExecutor.DstConfig calldata _dstConfig,
35
+ bytes calldata _options
36
+ ) external view returns (uint256 fee) {
37
+ fee = getFee(_params, _dstConfig, _options);
38
+ }
39
+
40
+ function getFeeOnSend(
41
+ FeeParamsForRead calldata _params,
42
+ IExecutor.DstConfig calldata _dstConfig,
43
+ bytes calldata _options
44
+ ) external view returns (uint256 fee) {
45
+ fee = getFee(_params, _dstConfig, _options);
46
+ }
47
+
48
+ // ================================ View ================================
49
+ function getFee(
50
+ FeeParams calldata _params,
51
+ IExecutor.DstConfig calldata _dstConfig,
52
+ bytes calldata _options
53
+ ) public view returns (uint256 fee) {
54
+ if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);
55
+
56
+ (uint256 totalValue, uint256 totalGas, ) = _decodeExecutorOptions(
57
+ false,
58
+ _isV1Eid(_params.dstEid),
59
+ _dstConfig.lzReceiveBaseGas,
60
+ _dstConfig.lzComposeBaseGas,
61
+ _dstConfig.nativeCap,
62
+ _options
63
+ );
64
+
65
+ (
66
+ uint256 totalGasFee,
67
+ uint128 priceRatio,
68
+ uint128 priceRatioDenominator,
69
+ uint128 nativePriceUSD
70
+ ) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(_params.dstEid, _params.calldataSize, totalGas);
71
+
72
+ uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;
73
+
74
+ fee = _applyPremiumToGas(totalGasFee, multiplierBps, _dstConfig.floorMarginUSD, nativePriceUSD);
75
+ fee += _convertAndApplyPremiumToValue(totalValue, priceRatio, priceRatioDenominator, multiplierBps);
76
+ }
77
+
78
+ function getFee(
79
+ FeeParamsForRead calldata _params,
80
+ IExecutor.DstConfig calldata _dstConfig,
81
+ bytes calldata _options
82
+ ) public view returns (uint256 fee) {
83
+ if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(localEidV2);
84
+
85
+ (uint256 totalValue, uint256 totalGas, uint32 calldataSize) = _decodeExecutorOptions(
86
+ true,
87
+ false, // endpoint v2 only
88
+ _dstConfig.lzReceiveBaseGas,
89
+ _dstConfig.lzComposeBaseGas,
90
+ _dstConfig.nativeCap,
91
+ _options
92
+ );
93
+
94
+ (
95
+ uint256 totalGasFee,
96
+ uint128 priceRatio,
97
+ uint128 priceRatioDenominator,
98
+ uint128 nativePriceUSD
99
+ ) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(localEidV2, calldataSize, totalGas);
100
+
101
+ uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;
102
+
103
+ fee = _applyPremiumToGas(totalGasFee, multiplierBps, _dstConfig.floorMarginUSD, nativePriceUSD);
104
+ fee += _convertAndApplyPremiumToValue(totalValue, priceRatio, priceRatioDenominator, multiplierBps);
105
+ }
106
+
107
+ // ================================ Internal ================================
108
+ // @dev decode executor options into dstAmount and totalGas
109
+ function _decodeExecutorOptions(
110
+ bool _isRead,
111
+ bool _v1Eid,
112
+ uint64 _lzReceiveBaseGas,
113
+ uint64 _lzComposeBaseGas,
114
+ uint128 _nativeCap,
115
+ bytes calldata _options
116
+ ) internal pure returns (uint256 totalValue, uint256 totalGas, uint32 calldataSize) {
117
+ ExecutorOptionsAgg memory aggOptions = _parseExecutorOptions(_options, _isRead, _v1Eid, _nativeCap);
118
+ totalValue = aggOptions.totalValue;
119
+ calldataSize = aggOptions.calldataSize;
120
+
121
+ // lz receive only called once
122
+ // lz compose can be called multiple times, based on unique index
123
+ // to simplify the quoting, we add lzComposeBaseGas for each lzComposeOption received
124
+ // if the same index has multiple compose options, the gas will be added multiple times
125
+ totalGas = _lzReceiveBaseGas + aggOptions.totalGas + _lzComposeBaseGas * aggOptions.numLzCompose;
126
+ if (aggOptions.ordered) {
127
+ totalGas = (totalGas * 102) / 100;
128
+ }
129
+ }
130
+
131
+ struct ExecutorOptionsAgg {
132
+ uint256 totalValue;
133
+ uint256 totalGas;
134
+ bool ordered;
135
+ uint32 calldataSize;
136
+ uint256 numLzCompose;
137
+ }
138
+
139
+ function _parseExecutorOptions(
140
+ bytes calldata _options,
141
+ bool _isRead,
142
+ bool _v1Eid,
143
+ uint128 _nativeCap
144
+ ) internal pure returns (ExecutorOptionsAgg memory options) {
145
+ if (_options.length == 0) {
146
+ revert Executor_NoOptions();
147
+ }
148
+
149
+ uint256 cursor = 0;
150
+ uint256 lzReceiveGas;
151
+ uint32 calldataSize;
152
+ while (cursor < _options.length) {
153
+ (uint8 optionType, bytes calldata option, uint256 newCursor) = _options.nextExecutorOption(cursor);
154
+ cursor = newCursor;
155
+
156
+ if (optionType == ExecutorOptions.OPTION_TYPE_LZRECEIVE) {
157
+ // lzRead does not support lzReceive option
158
+ if (_isRead) revert Executor_UnsupportedOptionType(optionType);
159
+ (uint128 gas, uint128 value) = ExecutorOptions.decodeLzReceiveOption(option);
160
+
161
+ // endpoint v1 does not support lzReceive with value
162
+ if (_v1Eid && value > 0) revert Executor_UnsupportedOptionType(optionType);
163
+
164
+ options.totalValue += value;
165
+ lzReceiveGas += gas;
166
+ } else if (optionType == ExecutorOptions.OPTION_TYPE_NATIVE_DROP) {
167
+ // lzRead does not support nativeDrop option
168
+ if (_isRead) revert Executor_UnsupportedOptionType(optionType);
169
+
170
+ (uint128 nativeDropAmount, ) = ExecutorOptions.decodeNativeDropOption(option);
171
+ options.totalValue += nativeDropAmount;
172
+ } else if (optionType == ExecutorOptions.OPTION_TYPE_LZCOMPOSE) {
173
+ // endpoint v1 does not support lzCompose
174
+ if (_v1Eid) revert Executor_UnsupportedOptionType(optionType);
175
+
176
+ (, uint128 gas, uint128 value) = ExecutorOptions.decodeLzComposeOption(option);
177
+ if (gas == 0) revert Executor_ZeroLzComposeGasProvided();
178
+
179
+ options.totalValue += value;
180
+ options.totalGas += gas;
181
+ options.numLzCompose++;
182
+ } else if (optionType == ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION) {
183
+ options.ordered = true;
184
+ } else if (optionType == ExecutorOptions.OPTION_TYPE_LZREAD) {
185
+ if (!_isRead) revert Executor_UnsupportedOptionType(optionType);
186
+
187
+ (uint128 gas, uint32 size, uint128 value) = ExecutorOptions.decodeLzReadOption(option);
188
+ options.totalValue += value;
189
+ lzReceiveGas += gas;
190
+ calldataSize += size;
191
+ } else {
192
+ revert Executor_UnsupportedOptionType(optionType);
193
+ }
194
+ }
195
+ if (cursor != _options.length) revert Executor_InvalidExecutorOptions(cursor);
196
+ if (options.totalValue > _nativeCap) revert Executor_NativeAmountExceedsCap(options.totalValue, _nativeCap);
197
+ if (lzReceiveGas == 0) revert Executor_ZeroLzReceiveGasProvided();
198
+ if (_isRead && calldataSize == 0) revert Executor_ZeroCalldataSizeProvided();
199
+ options.totalGas += lzReceiveGas;
200
+ options.calldataSize = calldataSize;
201
+ }
202
+
203
+ function _applyPremiumToGas(
204
+ uint256 _fee,
205
+ uint16 _multiplierBps,
206
+ uint128 _marginUSD,
207
+ uint128 _nativePriceUSD
208
+ ) internal view returns (uint256) {
209
+ uint256 feeWithMultiplier = (_fee * _multiplierBps) / 10000;
210
+
211
+ if (_nativePriceUSD == 0 || _marginUSD == 0) {
212
+ return feeWithMultiplier;
213
+ }
214
+ uint256 feeWithMargin = (_marginUSD * nativeDecimalsRate) / _nativePriceUSD + _fee;
215
+ return feeWithMargin > feeWithMultiplier ? feeWithMargin : feeWithMultiplier;
216
+ }
217
+
218
+ // includes value and nativeDrop
219
+ function _convertAndApplyPremiumToValue(
220
+ uint256 _value,
221
+ uint128 _ratio,
222
+ uint128 _denom,
223
+ uint16 _multiplierBps
224
+ ) internal pure returns (uint256 fee) {
225
+ if (_value > 0) {
226
+ fee = (((_value * _ratio) / _denom) * _multiplierBps) / 10000;
227
+ }
228
+ }
229
+
230
+ function _isV1Eid(uint32 _eid) internal pure virtual returns (bool) {
231
+ // v1 eid is < 30000
232
+ return _eid < 30000;
233
+ }
234
+
235
+ function version() external pure returns (uint64 major, uint8 minor) {
236
+ return (1, 1);
237
+ }
238
+
239
+ // send funds here to pay for price feed directly
240
+ receive() external payable {}
241
+ }
@@ -0,0 +1,21 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ /// @dev simply a container of endpoint address and local eid
6
+ abstract contract MessageLibBase {
7
+ address internal immutable endpoint;
8
+ uint32 internal immutable localEid;
9
+
10
+ error LZ_MessageLib_OnlyEndpoint();
11
+
12
+ modifier onlyEndpoint() {
13
+ if (endpoint != msg.sender) revert LZ_MessageLib_OnlyEndpoint();
14
+ _;
15
+ }
16
+
17
+ constructor(address _endpoint, uint32 _localEid) {
18
+ endpoint = _endpoint;
19
+ localEid = _localEid;
20
+ }
21
+ }