@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.
- package/README.md +15 -0
- package/contracts/Executor.sol +339 -0
- package/contracts/ExecutorFeeLib.sol +241 -0
- package/contracts/MessageLibBase.sol +21 -0
- package/contracts/PriceFeed.sol +304 -0
- package/contracts/ReceiveLibBaseE2.sol +28 -0
- package/contracts/SendLibBase.sol +258 -0
- package/contracts/SendLibBaseE2.sol +140 -0
- package/contracts/SimpleReadExecutor.sol +110 -0
- package/contracts/Treasury.sol +74 -0
- package/contracts/Worker.sol +167 -0
- package/contracts/decompressor/DVNDecoder.sol +52 -0
- package/contracts/decompressor/DVNDecompressor.sol +70 -0
- package/contracts/decompressor/DecompressorExtension.sol +204 -0
- package/contracts/decompressor/ExecutorDecoder.sol +147 -0
- package/contracts/decompressor/ExecutorDecompressor.sol +58 -0
- package/contracts/interfaces/IExecutor.sol +47 -0
- package/contracts/interfaces/IExecutorFeeLib.sol +56 -0
- package/contracts/interfaces/ILayerZeroExecutor.sol +29 -0
- package/contracts/interfaces/ILayerZeroPriceFeed.sol +61 -0
- package/contracts/interfaces/ILayerZeroReadExecutor.sol +15 -0
- package/contracts/interfaces/ILayerZeroTreasury.sol +19 -0
- package/contracts/interfaces/IWorker.sol +29 -0
- package/contracts/libs/ExecutorOptions.sol +104 -0
- package/contracts/libs/SafeCall.sol +123 -0
- package/contracts/lowlat/EssenceDVNWrapper.sol +77 -0
- package/contracts/lowlat/MultiCall.sol +42 -0
- package/contracts/mocks/DVNMock.sol +28 -0
- package/contracts/mocks/ExecutorMock.sol +95 -0
- package/contracts/uln/LzExecutor.sol +129 -0
- package/contracts/uln/ReceiveUlnBase.sol +125 -0
- package/contracts/uln/SendUlnBase.sol +129 -0
- package/contracts/uln/UlnBase.sol +195 -0
- package/contracts/uln/dvn/DVN.sol +393 -0
- package/contracts/uln/dvn/DVNFeeLib.sol +288 -0
- package/contracts/uln/dvn/DeadDVN.sol +30 -0
- package/contracts/uln/dvn/MultiSig.sol +135 -0
- package/contracts/uln/dvn/SimpleReadDVN.sol +112 -0
- package/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapter.sol +151 -0
- package/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapterFeeLib.sol +71 -0
- package/contracts/uln/dvn/adapters/DVNAdapterBase.sol +114 -0
- package/contracts/uln/dvn/adapters/arbitrum/ArbitrumDVNAdapterL1.sol +126 -0
- package/contracts/uln/dvn/adapters/arbitrum/ArbitrumDVNAdapterL2.sol +50 -0
- package/contracts/uln/dvn/adapters/avalanche/NOTES.md +4 -0
- package/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapter.sol +152 -0
- package/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapterFeeLib.sol +127 -0
- package/contracts/uln/dvn/adapters/libs/DVNAdapterMessageCodec.sol +41 -0
- package/contracts/uln/dvn/adapters/optimism/OptimismDVNAdapterL1.sol +86 -0
- package/contracts/uln/dvn/adapters/optimism/OptimismDVNAdapterL2.sol +58 -0
- package/contracts/uln/interfaces/IDVN.sol +26 -0
- package/contracts/uln/interfaces/IDVNFeeLib.sol +56 -0
- package/contracts/uln/interfaces/ILayerZeroDVN.sol +34 -0
- package/contracts/uln/interfaces/ILayerZeroReadDVN.sol +27 -0
- package/contracts/uln/interfaces/IReceiveUlnE2.sol +13 -0
- package/contracts/uln/interfaces/adapters/IAxelarDVNAdapter.sol +50 -0
- package/contracts/uln/interfaces/adapters/IAxelarDVNAdapterFeeLib.sol +49 -0
- package/contracts/uln/interfaces/adapters/ICCIPDVNAdapter.sol +34 -0
- package/contracts/uln/interfaces/adapters/ICCIPDVNAdapterFeeLib.sol +46 -0
- package/contracts/uln/libs/DVNOptions.sol +181 -0
- package/contracts/uln/libs/ReadCmdCodecV1.sol +136 -0
- package/contracts/uln/libs/SupportedCmdTypes.sol +29 -0
- package/contracts/uln/libs/UlnOptions.sol +176 -0
- package/contracts/uln/readlib/ReadLib1002.sol +518 -0
- package/contracts/uln/readlib/ReadLib1002View.sol +98 -0
- package/contracts/uln/readlib/ReadLibBase.sol +187 -0
- package/contracts/uln/uln301/AddressSizeConfig.sol +22 -0
- package/contracts/uln/uln301/ReceiveLibBaseE1.sol +103 -0
- package/contracts/uln/uln301/ReceiveUln301.sol +83 -0
- package/contracts/uln/uln301/ReceiveUln301View.sol +89 -0
- package/contracts/uln/uln301/SendLibBaseE1.sol +194 -0
- package/contracts/uln/uln301/SendUln301.sol +99 -0
- package/contracts/uln/uln301/TreasuryFeeHandler.sol +41 -0
- package/contracts/uln/uln301/interfaces/IMessageLibE1.sol +22 -0
- package/contracts/uln/uln301/interfaces/INonceContract.sol +7 -0
- package/contracts/uln/uln301/interfaces/ITreasuryFeeHandler.sol +13 -0
- package/contracts/uln/uln301/interfaces/IUltraLightNode301.sol +7 -0
- package/contracts/uln/uln301/mocks/NonceContractMock.sol +23 -0
- package/contracts/uln/uln302/ReceiveUln302.sol +85 -0
- package/contracts/uln/uln302/ReceiveUln302View.sol +89 -0
- package/contracts/uln/uln302/SendUln302.sol +86 -0
- package/contracts/upgradeable/WorkerUpgradeable.sol +186 -0
- package/contracts/upgradeable/proxy/ProxyAdmin.sol +96 -0
- package/contracts/upgradeable/proxy/TransparentUpgradeableProxy.sol +131 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- 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
|
+
}
|