@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
@@ -0,0 +1,304 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
6
+ import { Proxied } from "hardhat-deploy/solc_0.8/proxy/Proxied.sol";
7
+
8
+ import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
9
+ import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
10
+
11
+ import { ILayerZeroPriceFeed } from "./interfaces/ILayerZeroPriceFeed.sol";
12
+
13
+ enum ModelType {
14
+ DEFAULT,
15
+ ARB_STACK,
16
+ OP_STACK
17
+ }
18
+
19
+ struct SetEidToModelTypeParam {
20
+ uint32 dstEid;
21
+ ModelType modelType;
22
+ }
23
+
24
+ // PriceFeed is updated based on v1 eids
25
+ // v2 eids will fall to the convention of v1 eid + 30,000
26
+ contract PriceFeed is ILayerZeroPriceFeed, OwnableUpgradeable, Proxied {
27
+ uint128 internal PRICE_RATIO_DENOMINATOR;
28
+
29
+ // sets pricing
30
+ mapping(address updater => bool active) public priceUpdater;
31
+
32
+ mapping(uint32 dstEid => Price) internal _defaultModelPrice;
33
+ ArbitrumPriceExt internal _arbitrumPriceExt;
34
+
35
+ uint128 internal _nativePriceUSD; // uses PRICE_RATIO_DENOMINATOR
36
+
37
+ // upgrade: arbitrum compression - percentage of callDataSize after brotli compression
38
+ uint128 public ARBITRUM_COMPRESSION_PERCENT;
39
+
40
+ ILayerZeroEndpointV2 public endpoint;
41
+
42
+ // for the destination endpoint id, return the fee model type
43
+ mapping(uint32 => ModelType) public eidToModelType;
44
+
45
+ // ============================ Constructor ===================================
46
+
47
+ function initialize(address _priceUpdater) public proxied initializer {
48
+ __Ownable_init();
49
+ priceUpdater[_priceUpdater] = true;
50
+ PRICE_RATIO_DENOMINATOR = 1e20;
51
+ ARBITRUM_COMPRESSION_PERCENT = 47;
52
+ }
53
+
54
+ // ============================ Modifier ======================================
55
+
56
+ // owner is always approved
57
+ modifier onlyPriceUpdater() {
58
+ if (owner() != msg.sender) {
59
+ if (!priceUpdater[msg.sender]) {
60
+ revert LZ_PriceFeed_OnlyPriceUpdater();
61
+ }
62
+ }
63
+ _;
64
+ }
65
+
66
+ // ============================ OnlyOwner =====================================
67
+
68
+ function setPriceUpdater(address _addr, bool _active) external onlyOwner {
69
+ priceUpdater[_addr] = _active;
70
+ }
71
+
72
+ function setPriceRatioDenominator(uint128 _denominator) external onlyOwner {
73
+ PRICE_RATIO_DENOMINATOR = _denominator;
74
+ }
75
+
76
+ function setArbitrumCompressionPercent(uint128 _compressionPercent) external onlyOwner {
77
+ ARBITRUM_COMPRESSION_PERCENT = _compressionPercent;
78
+ }
79
+
80
+ // set the fee ModelType for the destination eid
81
+ function setEidToModelType(SetEidToModelTypeParam[] calldata _params) external onlyOwner {
82
+ for (uint i = 0; i < _params.length; i++) {
83
+ eidToModelType[_params[i].dstEid] = _params[i].modelType;
84
+ }
85
+ }
86
+
87
+ function setEndpoint(address _endpoint) external onlyOwner {
88
+ endpoint = ILayerZeroEndpointV2(_endpoint);
89
+ }
90
+
91
+ function withdrawFee(address _to, uint256 _amount) external onlyOwner {
92
+ Transfer.native(_to, _amount);
93
+ }
94
+
95
+ // ============================ OnlyPriceUpdater =====================================
96
+
97
+ function setPrice(UpdatePrice[] calldata _price) external onlyPriceUpdater {
98
+ for (uint256 i = 0; i < _price.length; i++) {
99
+ UpdatePrice calldata _update = _price[i];
100
+ _setPrice(_update.eid, _update.price);
101
+ }
102
+ }
103
+
104
+ function setPriceForArbitrum(UpdatePriceExt calldata _update) external onlyPriceUpdater {
105
+ _setPrice(_update.eid, _update.price);
106
+
107
+ uint64 gasPerL2Tx = _update.extend.gasPerL2Tx;
108
+ uint32 gasPerL1CalldataByte = _update.extend.gasPerL1CallDataByte;
109
+
110
+ _arbitrumPriceExt.gasPerL2Tx = gasPerL2Tx;
111
+ _arbitrumPriceExt.gasPerL1CallDataByte = gasPerL1CalldataByte;
112
+ }
113
+
114
+ function setNativeTokenPriceUSD(uint128 _nativeTokenPriceUSD) external onlyPriceUpdater {
115
+ _nativePriceUSD = _nativeTokenPriceUSD;
116
+ }
117
+
118
+ // ============================ External =====================================
119
+
120
+ function estimateFeeOnSend(
121
+ uint32 _dstEid,
122
+ uint256 _callDataSize,
123
+ uint256 _gas
124
+ ) external payable returns (uint256, uint128, uint128, uint128) {
125
+ uint256 fee = getFee(_dstEid, _callDataSize, _gas);
126
+ if (msg.value < fee) revert LZ_PriceFeed_InsufficientFee(msg.value, fee);
127
+ return _estimateFeeByEid(_dstEid, _callDataSize, _gas);
128
+ }
129
+
130
+ // ============================ View ==========================================
131
+
132
+ // get fee for calling estimateFeeOnSend
133
+ function getFee(uint32 /*_dstEid*/, uint256 /*_callDataSize*/, uint256 /*_gas*/) public pure returns (uint256) {
134
+ return 0;
135
+ }
136
+
137
+ function getPriceRatioDenominator() external view returns (uint128) {
138
+ return PRICE_RATIO_DENOMINATOR;
139
+ }
140
+
141
+ // NOTE: to be reverted when endpoint is in sendContext
142
+ function nativeTokenPriceUSD() external view returns (uint128) {
143
+ return _nativePriceUSD;
144
+ }
145
+
146
+ // NOTE: to be reverted when endpoint is in sendContext
147
+ function arbitrumPriceExt() external view returns (ArbitrumPriceExt memory) {
148
+ return _arbitrumPriceExt;
149
+ }
150
+
151
+ // NOTE: to be reverted when endpoint is in sendContext
152
+ function getPrice(uint32 _dstEid) external view returns (Price memory price) {
153
+ price = _defaultModelPrice[_dstEid];
154
+ }
155
+
156
+ // NOTE: to be reverted when endpoint is in sendContext
157
+ function estimateFeeByEid(
158
+ uint32 _dstEid,
159
+ uint256 _callDataSize,
160
+ uint256 _gas
161
+ ) external view returns (uint256, uint128, uint128, uint128) {
162
+ return _estimateFeeByEid(_dstEid, _callDataSize, _gas);
163
+ }
164
+
165
+ // NOTE: to be reverted when endpoint is in sendContext
166
+ // NOTE: to support legacy
167
+ function getPrice(uint16 _dstEid) external view returns (Price memory price) {
168
+ price = _defaultModelPrice[_dstEid];
169
+ }
170
+
171
+ // NOTE: to be reverted when endpoint is in sendContext
172
+ // NOTE: to support legacy
173
+ function estimateFeeByChain(
174
+ uint16 _dstEid,
175
+ uint256 _callDataSize,
176
+ uint256 _gas
177
+ ) external view returns (uint256 fee, uint128 priceRatio) {
178
+ // legacy if-statement uses very little gas, can keep using it until future upgrade
179
+ if (_dstEid == 110 || _dstEid == 10143 || _dstEid == 20143) {
180
+ return _estimateFeeWithArbitrumModel(_dstEid, _callDataSize, _gas);
181
+ } else if (_dstEid == 111 || _dstEid == 10132 || _dstEid == 20132) {
182
+ return _estimateFeeWithOptimismModel(_dstEid, _callDataSize, _gas);
183
+ }
184
+
185
+ // fee model type is configured per eid
186
+ ModelType _modelType = eidToModelType[_dstEid];
187
+ if (_modelType == ModelType.OP_STACK) {
188
+ return _estimateFeeWithOptimismModel(_dstEid, _callDataSize, _gas);
189
+ } else if (_modelType == ModelType.ARB_STACK) {
190
+ return _estimateFeeWithArbitrumModel(_dstEid, _callDataSize, _gas);
191
+ } else {
192
+ return _estimateFeeWithDefaultModel(_dstEid, _callDataSize, _gas);
193
+ }
194
+ }
195
+
196
+ // ============================ Internal ==========================================
197
+
198
+ function _setPrice(uint32 _dstEid, Price memory _price) internal {
199
+ uint128 priceRatio = _price.priceRatio;
200
+ uint64 gasPriceInUnit = _price.gasPriceInUnit;
201
+ uint32 gasPerByte = _price.gasPerByte;
202
+ _defaultModelPrice[_dstEid] = Price(priceRatio, gasPriceInUnit, gasPerByte);
203
+ }
204
+
205
+ function _getL1LookupIdForOptimismModel(uint32 _l2Eid) internal view returns (uint32) {
206
+ uint32 l2Eid = _l2Eid % 30_000;
207
+ if (l2Eid == 111) {
208
+ return 101;
209
+ } else if (l2Eid == 10132) {
210
+ return 10121; // ethereum-goerli
211
+ } else if (l2Eid == 20132) {
212
+ return 20121; // ethereum-goerli
213
+ }
214
+
215
+ if (eidToModelType[l2Eid] != ModelType.OP_STACK) revert LZ_PriceFeed_NotAnOPStack(_l2Eid);
216
+ if (l2Eid < 10000) {
217
+ return 101;
218
+ } else if (l2Eid < 20000) {
219
+ return 10161; // ethereum-sepolia
220
+ } else {
221
+ return 20121; // ethereum-goerli
222
+ }
223
+ }
224
+
225
+ function _estimateFeeWithDefaultModel(
226
+ uint32 _dstEid,
227
+ uint256 _callDataSize,
228
+ uint256 _gas
229
+ ) internal view returns (uint256 fee, uint128 priceRatio) {
230
+ Price storage remotePrice = _defaultModelPrice[_dstEid];
231
+
232
+ // assuming the _gas includes (1) the 21,000 overhead and (2) not the calldata gas
233
+ uint256 gasForCallData = _callDataSize * remotePrice.gasPerByte;
234
+ uint256 remoteFee = (gasForCallData + _gas) * remotePrice.gasPriceInUnit;
235
+ return ((remoteFee * remotePrice.priceRatio) / PRICE_RATIO_DENOMINATOR, remotePrice.priceRatio);
236
+ }
237
+
238
+ function _estimateFeeByEid(
239
+ uint32 _dstEid,
240
+ uint256 _callDataSize,
241
+ uint256 _gas
242
+ ) internal view returns (uint256 fee, uint128 priceRatio, uint128 priceRatioDenominator, uint128 priceUSD) {
243
+ uint32 dstEid = _dstEid % 30_000;
244
+ if (dstEid == 110 || dstEid == 10143 || dstEid == 20143) {
245
+ (fee, priceRatio) = _estimateFeeWithArbitrumModel(dstEid, _callDataSize, _gas);
246
+ } else if (dstEid == 111 || dstEid == 10132 || dstEid == 20132) {
247
+ (fee, priceRatio) = _estimateFeeWithOptimismModel(dstEid, _callDataSize, _gas);
248
+ }
249
+
250
+ // lookup map stuff
251
+ ModelType _modelType = eidToModelType[dstEid];
252
+ if (_modelType == ModelType.OP_STACK) {
253
+ (fee, priceRatio) = _estimateFeeWithOptimismModel(dstEid, _callDataSize, _gas);
254
+ } else if (_modelType == ModelType.ARB_STACK) {
255
+ (fee, priceRatio) = _estimateFeeWithArbitrumModel(dstEid, _callDataSize, _gas);
256
+ } else {
257
+ (fee, priceRatio) = _estimateFeeWithDefaultModel(dstEid, _callDataSize, _gas);
258
+ }
259
+
260
+ priceRatioDenominator = PRICE_RATIO_DENOMINATOR;
261
+ priceUSD = _nativePriceUSD;
262
+ }
263
+
264
+ function _estimateFeeWithOptimismModel(
265
+ uint32 _dstEid,
266
+ uint256 _callDataSize,
267
+ uint256 _gas
268
+ ) internal view returns (uint256 fee, uint128 priceRatio) {
269
+ uint32 ethereumId = _getL1LookupIdForOptimismModel(_dstEid);
270
+
271
+ // L1 fee
272
+ Price storage ethereumPrice = _defaultModelPrice[ethereumId];
273
+ uint256 gasForL1CallData = (_callDataSize * ethereumPrice.gasPerByte) + 3188; // 2100 + 68 * 16
274
+ uint256 l1Fee = gasForL1CallData * ethereumPrice.gasPriceInUnit;
275
+
276
+ // L2 fee
277
+ Price storage optimismPrice = _defaultModelPrice[_dstEid];
278
+ uint256 gasForL2CallData = _callDataSize * optimismPrice.gasPerByte;
279
+ uint256 l2Fee = (gasForL2CallData + _gas) * optimismPrice.gasPriceInUnit;
280
+
281
+ uint256 l1FeeInSrcPrice = (l1Fee * ethereumPrice.priceRatio) / PRICE_RATIO_DENOMINATOR;
282
+ uint256 l2FeeInSrcPrice = (l2Fee * optimismPrice.priceRatio) / PRICE_RATIO_DENOMINATOR;
283
+ uint256 gasFee = l1FeeInSrcPrice + l2FeeInSrcPrice;
284
+ return (gasFee, optimismPrice.priceRatio);
285
+ }
286
+
287
+ function _estimateFeeWithArbitrumModel(
288
+ uint32 _dstEid,
289
+ uint256 _callDataSize,
290
+ uint256 _gas
291
+ ) internal view returns (uint256 fee, uint128 priceRatio) {
292
+ Price storage arbitrumPrice = _defaultModelPrice[_dstEid];
293
+
294
+ // L1 fee
295
+ uint256 gasForL1CallData = ((_callDataSize * ARBITRUM_COMPRESSION_PERCENT) / 100) *
296
+ _arbitrumPriceExt.gasPerL1CallDataByte;
297
+ // L2 Fee
298
+ uint256 gasForL2CallData = _callDataSize * arbitrumPrice.gasPerByte;
299
+ uint256 gasFee = (_gas + _arbitrumPriceExt.gasPerL2Tx + gasForL1CallData + gasForL2CallData) *
300
+ arbitrumPrice.gasPriceInUnit;
301
+
302
+ return ((gasFee * arbitrumPrice.priceRatio) / PRICE_RATIO_DENOMINATOR, arbitrumPrice.priceRatio);
303
+ }
304
+ }
@@ -0,0 +1,28 @@
1
+ // SPDX-License-Identifier: LZBL-1.2
2
+
3
+ pragma solidity ^0.8.20;
4
+
5
+ import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
6
+ import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
7
+
8
+ import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
9
+ import { IMessageLib, MessageLibType } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol";
10
+ import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
11
+
12
+ import { MessageLibBase } from "./MessageLibBase.sol";
13
+
14
+ /// @dev receive-side message library base contract on endpoint v2.
15
+ /// it does not have the complication as the one of endpoint v1, such as nonce, executor whitelist, etc.
16
+ abstract contract ReceiveLibBaseE2 is MessageLibBase, ERC165, IMessageLib {
17
+ using PacketV1Codec for bytes;
18
+
19
+ constructor(address _endpoint) MessageLibBase(_endpoint, ILayerZeroEndpointV2(_endpoint).eid()) {}
20
+
21
+ function supportsInterface(bytes4 _interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
22
+ return _interfaceId == type(IMessageLib).interfaceId || super.supportsInterface(_interfaceId);
23
+ }
24
+
25
+ function messageLibType() external pure virtual override returns (MessageLibType) {
26
+ return MessageLibType.Receive;
27
+ }
28
+ }
@@ -0,0 +1,258 @@
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
+ import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
7
+
8
+ import { ILayerZeroExecutor } from "./interfaces/ILayerZeroExecutor.sol";
9
+ import { ILayerZeroTreasury } from "./interfaces/ILayerZeroTreasury.sol";
10
+ import { SafeCall } from "./libs/SafeCall.sol";
11
+ import { MessageLibBase } from "./MessageLibBase.sol";
12
+
13
+ struct WorkerOptions {
14
+ uint8 workerId;
15
+ bytes options;
16
+ }
17
+
18
+ struct SetDefaultExecutorConfigParam {
19
+ uint32 eid;
20
+ ExecutorConfig config;
21
+ }
22
+
23
+ struct ExecutorConfig {
24
+ uint32 maxMessageSize;
25
+ address executor;
26
+ }
27
+
28
+ /// @dev base contract for both SendLibBaseE1 and SendLibBaseE2
29
+ abstract contract SendLibBase is MessageLibBase, Ownable {
30
+ using SafeCall for address;
31
+
32
+ address private constant DEFAULT_CONFIG = address(0);
33
+ uint16 internal constant TREASURY_MAX_COPY = 32;
34
+
35
+ uint256 internal immutable treasuryGasLimit;
36
+ uint256 internal treasuryNativeFeeCap;
37
+
38
+ // config
39
+ address public treasury;
40
+ mapping(address oapp => mapping(uint32 eid => ExecutorConfig)) public executorConfigs;
41
+
42
+ // accumulated fees for workers and treasury
43
+ mapping(address worker => uint256) public fees;
44
+
45
+ event ExecutorFeePaid(address executor, uint256 fee);
46
+ event TreasurySet(address treasury);
47
+ event DefaultExecutorConfigsSet(SetDefaultExecutorConfigParam[] params);
48
+ event ExecutorConfigSet(address oapp, uint32 eid, ExecutorConfig config);
49
+ event TreasuryNativeFeeCapSet(uint256 newTreasuryNativeFeeCap);
50
+
51
+ error LZ_MessageLib_InvalidMessageSize(uint256 actual, uint256 max);
52
+ error LZ_MessageLib_InvalidAmount(uint256 requested, uint256 available);
53
+ error LZ_MessageLib_TransferFailed();
54
+ error LZ_MessageLib_InvalidExecutor();
55
+ error LZ_MessageLib_ZeroMessageSize();
56
+
57
+ constructor(
58
+ address _endpoint,
59
+ uint32 _localEid,
60
+ uint256 _treasuryGasLimit,
61
+ uint256 _treasuryNativeFeeCap
62
+ ) MessageLibBase(_endpoint, _localEid) {
63
+ treasuryGasLimit = _treasuryGasLimit;
64
+ treasuryNativeFeeCap = _treasuryNativeFeeCap;
65
+ }
66
+
67
+ function setDefaultExecutorConfigs(SetDefaultExecutorConfigParam[] calldata _params) external onlyOwner {
68
+ for (uint256 i = 0; i < _params.length; ++i) {
69
+ SetDefaultExecutorConfigParam calldata param = _params[i];
70
+
71
+ if (param.config.executor == address(0x0)) revert LZ_MessageLib_InvalidExecutor();
72
+ if (param.config.maxMessageSize == 0) revert LZ_MessageLib_ZeroMessageSize();
73
+
74
+ executorConfigs[DEFAULT_CONFIG][param.eid] = param.config;
75
+ }
76
+ emit DefaultExecutorConfigsSet(_params);
77
+ }
78
+
79
+ /// @dev the new value can not be greater than the old value, i.e. down only
80
+ function setTreasuryNativeFeeCap(uint256 _newTreasuryNativeFeeCap) external onlyOwner {
81
+ // assert the new value is no greater than the old value
82
+ if (_newTreasuryNativeFeeCap > treasuryNativeFeeCap)
83
+ revert LZ_MessageLib_InvalidAmount(_newTreasuryNativeFeeCap, treasuryNativeFeeCap);
84
+ treasuryNativeFeeCap = _newTreasuryNativeFeeCap;
85
+ emit TreasuryNativeFeeCapSet(_newTreasuryNativeFeeCap);
86
+ }
87
+
88
+ // ============================ View ===================================
89
+ // @dev get the executor config and if not set, return the default config
90
+ function getExecutorConfig(address _oapp, uint32 _remoteEid) public view returns (ExecutorConfig memory rtnConfig) {
91
+ ExecutorConfig storage defaultConfig = executorConfigs[DEFAULT_CONFIG][_remoteEid];
92
+ ExecutorConfig storage customConfig = executorConfigs[_oapp][_remoteEid];
93
+
94
+ uint32 maxMessageSize = customConfig.maxMessageSize;
95
+ rtnConfig.maxMessageSize = maxMessageSize != 0 ? maxMessageSize : defaultConfig.maxMessageSize;
96
+
97
+ address executor = customConfig.executor;
98
+ rtnConfig.executor = executor != address(0x0) ? executor : defaultConfig.executor;
99
+ }
100
+
101
+ // ======================= Internal =======================
102
+ function _assertMessageSize(uint256 _actual, uint256 _max) internal pure {
103
+ if (_actual > _max) revert LZ_MessageLib_InvalidMessageSize(_actual, _max);
104
+ }
105
+
106
+ function _payExecutor(
107
+ address _executor,
108
+ uint32 _dstEid,
109
+ address _sender,
110
+ uint256 _msgSize,
111
+ bytes memory _executorOptions
112
+ ) internal returns (uint256 executorFee) {
113
+ executorFee = ILayerZeroExecutor(_executor).assignJob(_dstEid, _sender, _msgSize, _executorOptions);
114
+ if (executorFee > 0) {
115
+ fees[_executor] += executorFee;
116
+ }
117
+ emit ExecutorFeePaid(_executor, executorFee);
118
+ }
119
+
120
+ function _payTreasury(
121
+ address _sender,
122
+ uint32 _dstEid,
123
+ uint256 _totalNativeFee,
124
+ bool _payInLzToken
125
+ ) internal returns (uint256 treasuryNativeFee, uint256 lzTokenFee) {
126
+ if (treasury != address(0x0)) {
127
+ bytes memory callData = abi.encodeCall(
128
+ ILayerZeroTreasury.payFee,
129
+ (_sender, _dstEid, _totalNativeFee, _payInLzToken)
130
+ );
131
+ (bool success, bytes memory result) = treasury.safeCall(treasuryGasLimit, 0, TREASURY_MAX_COPY, callData);
132
+
133
+ (treasuryNativeFee, lzTokenFee) = _parseTreasuryResult(_totalNativeFee, _payInLzToken, success, result);
134
+ // fee should be in lzTokenFee if payInLzToken, otherwise in native
135
+ if (treasuryNativeFee > 0) {
136
+ fees[treasury] += treasuryNativeFee;
137
+ }
138
+ }
139
+ }
140
+
141
+ /// @dev the abstract process for quote() is:
142
+ /// 0/ split out the executor options and options of other workers
143
+ /// 1/ quote workers
144
+ /// 2/ quote executor
145
+ /// 3/ quote treasury
146
+ /// @return nativeFee, lzTokenFee
147
+ function _quote(
148
+ address _sender,
149
+ uint32 _dstEid,
150
+ uint256 _msgSize,
151
+ bool _payInLzToken,
152
+ bytes calldata _options
153
+ ) internal view returns (uint256, uint256) {
154
+ (bytes memory executorOptions, WorkerOptions[] memory validationOptions) = _splitOptions(_options);
155
+
156
+ // quote the verifier used in the library. for ULN, it is a list of DVNs
157
+ uint256 nativeFee = _quoteVerifier(_sender, _dstEid, validationOptions);
158
+
159
+ // quote executor
160
+ ExecutorConfig memory config = getExecutorConfig(_sender, _dstEid);
161
+ // assert msg size
162
+ _assertMessageSize(_msgSize, config.maxMessageSize);
163
+
164
+ nativeFee += ILayerZeroExecutor(config.executor).getFee(_dstEid, _sender, _msgSize, executorOptions);
165
+
166
+ // quote treasury
167
+ (uint256 treasuryNativeFee, uint256 lzTokenFee) = _quoteTreasury(_sender, _dstEid, nativeFee, _payInLzToken);
168
+ nativeFee += treasuryNativeFee;
169
+
170
+ return (nativeFee, lzTokenFee);
171
+ }
172
+
173
+ /// @dev this interface should be DoS-free if the user is paying with native. properties
174
+ /// 1/ treasury can return an overly high lzToken fee
175
+ /// 2/ if treasury returns an overly high native fee, it will be capped by maxNativeFee,
176
+ /// which can be reasoned with the configurations
177
+ /// 3/ the owner can not configure the treasury in a way that force this function to revert
178
+ function _quoteTreasury(
179
+ address _sender,
180
+ uint32 _dstEid,
181
+ uint256 _totalNativeFee,
182
+ bool _payInLzToken
183
+ ) internal view returns (uint256 nativeFee, uint256 lzTokenFee) {
184
+ // treasury must be set, and it has to be a contract
185
+ if (treasury != address(0x0)) {
186
+ bytes memory callData = abi.encodeCall(
187
+ ILayerZeroTreasury.getFee,
188
+ (_sender, _dstEid, _totalNativeFee, _payInLzToken)
189
+ );
190
+ (bool success, bytes memory result) = treasury.safeStaticCall(
191
+ treasuryGasLimit,
192
+ TREASURY_MAX_COPY,
193
+ callData
194
+ );
195
+
196
+ return _parseTreasuryResult(_totalNativeFee, _payInLzToken, success, result);
197
+ }
198
+ }
199
+
200
+ function _parseTreasuryResult(
201
+ uint256 _totalNativeFee,
202
+ bool _payInLzToken,
203
+ bool _success,
204
+ bytes memory _result
205
+ ) internal view returns (uint256 nativeFee, uint256 lzTokenFee) {
206
+ // failure, charges nothing
207
+ if (!_success || _result.length < TREASURY_MAX_COPY) return (0, 0);
208
+
209
+ // parse the result
210
+ uint256 treasureFeeQuote = abi.decode(_result, (uint256));
211
+ if (_payInLzToken) {
212
+ lzTokenFee = treasureFeeQuote;
213
+ } else {
214
+ // pay in native
215
+ // we must prevent high-treasuryFee Dos attack
216
+ // nativeFee = min(treasureFeeQuote, maxNativeFee)
217
+ // opportunistically raise the maxNativeFee to be the same as _totalNativeFee
218
+ // can't use the _totalNativeFee alone because the oapp can use custom workers to force the fee to 0.
219
+ // maxNativeFee = max (_totalNativeFee, treasuryNativeFeeCap)
220
+ uint256 maxNativeFee = _totalNativeFee > treasuryNativeFeeCap ? _totalNativeFee : treasuryNativeFeeCap;
221
+
222
+ // min (treasureFeeQuote, nativeFeeCap)
223
+ nativeFee = treasureFeeQuote > maxNativeFee ? maxNativeFee : treasureFeeQuote;
224
+ }
225
+ }
226
+
227
+ /// @dev authenticated by msg.sender only
228
+ function _debitFee(uint256 _amount) internal {
229
+ uint256 fee = fees[msg.sender];
230
+ if (_amount > fee) revert LZ_MessageLib_InvalidAmount(_amount, fee);
231
+ unchecked {
232
+ fees[msg.sender] = fee - _amount;
233
+ }
234
+ }
235
+
236
+ function _setTreasury(address _treasury) internal {
237
+ treasury = _treasury;
238
+ emit TreasurySet(_treasury);
239
+ }
240
+
241
+ function _setExecutorConfig(uint32 _remoteEid, address _oapp, ExecutorConfig memory _config) internal {
242
+ executorConfigs[_oapp][_remoteEid] = _config;
243
+ emit ExecutorConfigSet(_oapp, _remoteEid, _config);
244
+ }
245
+
246
+ // ======================= Virtual =======================
247
+ /// @dev these two functions will be overridden with specific logics of the library function
248
+ function _quoteVerifier(
249
+ address _oapp,
250
+ uint32 _eid,
251
+ WorkerOptions[] memory _options
252
+ ) internal view virtual returns (uint256 nativeFee);
253
+
254
+ /// @dev this function will split the options into executorOptions and validationOptions
255
+ function _splitOptions(
256
+ bytes calldata _options
257
+ ) internal view virtual returns (bytes memory executorOptions, WorkerOptions[] memory validationOptions);
258
+ }