@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,393 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { ILayerZeroUltraLightNodeV2 } from "@layerzerolabs/lz-evm-v1-0.7/contracts/interfaces/ILayerZeroUltraLightNodeV2.sol";
|
|
6
|
+
|
|
7
|
+
import { Worker } from "../../Worker.sol";
|
|
8
|
+
import { MultiSig } from "./MultiSig.sol";
|
|
9
|
+
import { ReadLib1002 } from "../readlib/ReadLib1002.sol";
|
|
10
|
+
import { IDVN } from "../interfaces/IDVN.sol";
|
|
11
|
+
import { IDVNFeeLib } from "../interfaces/IDVNFeeLib.sol";
|
|
12
|
+
import { IReceiveUlnE2 } from "../interfaces/IReceiveUlnE2.sol";
|
|
13
|
+
|
|
14
|
+
struct ExecuteParam {
|
|
15
|
+
uint32 vid;
|
|
16
|
+
address target;
|
|
17
|
+
bytes callData;
|
|
18
|
+
uint256 expiration;
|
|
19
|
+
bytes signatures;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
contract DVN is Worker, MultiSig, IDVN {
|
|
23
|
+
// to uniquely identify this DVN instance
|
|
24
|
+
// set to endpoint v1 eid if available OR endpoint v2 eid % 30_000
|
|
25
|
+
uint32 public immutable vid;
|
|
26
|
+
uint32 public immutable localEidV2; // endpoint-v2 only, for read call
|
|
27
|
+
|
|
28
|
+
mapping(uint32 dstEid => DstConfig) public dstConfig;
|
|
29
|
+
mapping(bytes32 executableHash => bool used) public usedHashes;
|
|
30
|
+
|
|
31
|
+
error DVN_OnlySelf();
|
|
32
|
+
error DVN_InvalidRole(bytes32 role);
|
|
33
|
+
error DVN_InstructionExpired();
|
|
34
|
+
error DVN_InvalidTarget(address target);
|
|
35
|
+
error DVN_InvalidVid(uint32 vid);
|
|
36
|
+
error DVN_InvalidSignatures();
|
|
37
|
+
error DVN_DuplicatedHash(bytes32 executableHash);
|
|
38
|
+
|
|
39
|
+
event VerifySignaturesFailed(uint256 idx);
|
|
40
|
+
event ExecuteFailed(uint256 _index, bytes _data);
|
|
41
|
+
event HashAlreadyUsed(ExecuteParam param, bytes32 _hash);
|
|
42
|
+
// same as DVNFeePaid, but for ULNv2
|
|
43
|
+
event VerifierFeePaid(uint256 fee);
|
|
44
|
+
|
|
45
|
+
// ========================= Constructor =========================
|
|
46
|
+
|
|
47
|
+
/// @dev DVN doesn't have a roleAdmin (address(0x0))
|
|
48
|
+
/// @dev Supports all of ULNv2, ULN301, ULN302 and more
|
|
49
|
+
/// @param _localEidV2 local endpoint-v2 eid
|
|
50
|
+
/// @param _vid unique identifier for this DVN instance
|
|
51
|
+
/// @param _messageLibs array of message lib addresses that are granted the MESSAGE_LIB_ROLE
|
|
52
|
+
/// @param _priceFeed price feed address
|
|
53
|
+
/// @param _signers array of signer addresses for multisig
|
|
54
|
+
/// @param _quorum quorum for multisig
|
|
55
|
+
/// @param _admins array of admin addresses that are granted the ADMIN_ROLE
|
|
56
|
+
constructor(
|
|
57
|
+
uint32 _localEidV2,
|
|
58
|
+
uint32 _vid,
|
|
59
|
+
address[] memory _messageLibs,
|
|
60
|
+
address _priceFeed,
|
|
61
|
+
address[] memory _signers,
|
|
62
|
+
uint64 _quorum,
|
|
63
|
+
address[] memory _admins
|
|
64
|
+
) Worker(_messageLibs, _priceFeed, 12000, address(0x0), _admins) MultiSig(_signers, _quorum) {
|
|
65
|
+
vid = _vid;
|
|
66
|
+
localEidV2 = _localEidV2;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ========================= Modifier =========================
|
|
70
|
+
|
|
71
|
+
/// @dev depending on role, restrict access to only self or admin
|
|
72
|
+
/// @dev ALLOWLIST, DENYLIST, MESSAGE_LIB_ROLE can only be granted/revoked by self
|
|
73
|
+
/// @dev ADMIN_ROLE can only be granted/revoked by admin
|
|
74
|
+
/// @dev reverts if not one of the above roles
|
|
75
|
+
/// @param _role role to check
|
|
76
|
+
modifier onlySelfOrAdmin(bytes32 _role) {
|
|
77
|
+
if (_role == ALLOWLIST || _role == DENYLIST || _role == MESSAGE_LIB_ROLE) {
|
|
78
|
+
// self required
|
|
79
|
+
if (address(this) != msg.sender) {
|
|
80
|
+
revert DVN_OnlySelf();
|
|
81
|
+
}
|
|
82
|
+
} else if (_role == ADMIN_ROLE) {
|
|
83
|
+
// admin required
|
|
84
|
+
_checkRole(ADMIN_ROLE);
|
|
85
|
+
} else {
|
|
86
|
+
revert DVN_InvalidRole(_role);
|
|
87
|
+
}
|
|
88
|
+
_;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
modifier onlySelf() {
|
|
92
|
+
if (address(this) != msg.sender) {
|
|
93
|
+
revert DVN_OnlySelf();
|
|
94
|
+
}
|
|
95
|
+
_;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ========================= OnlySelf =========================
|
|
99
|
+
|
|
100
|
+
/// @dev set signers for multisig
|
|
101
|
+
/// @dev function sig 0x31cb6105
|
|
102
|
+
/// @param _signer signer address
|
|
103
|
+
/// @param _active true to add, false to remove
|
|
104
|
+
function setSigner(address _signer, bool _active) external onlySelf {
|
|
105
|
+
_setSigner(_signer, _active);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// @dev set quorum for multisig
|
|
109
|
+
/// @dev function sig 0x8585c945
|
|
110
|
+
/// @param _quorum to set
|
|
111
|
+
function setQuorum(uint64 _quorum) external onlySelf {
|
|
112
|
+
_setQuorum(_quorum);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ========================= OnlySelf / OnlyAdmin =========================
|
|
116
|
+
|
|
117
|
+
/// @dev overrides AccessControl to allow self/admin to grant role'
|
|
118
|
+
/// @dev function sig 0x2f2ff15d
|
|
119
|
+
/// @param _role role to grant
|
|
120
|
+
/// @param _account account to grant role to
|
|
121
|
+
function grantRole(bytes32 _role, address _account) public override onlySelfOrAdmin(_role) {
|
|
122
|
+
_grantRole(_role, _account);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// @dev overrides AccessControl to allow self/admin to revoke role
|
|
126
|
+
/// @dev function sig 0xd547741f
|
|
127
|
+
/// @param _role role to revoke
|
|
128
|
+
/// @param _account account to revoke role from
|
|
129
|
+
function revokeRole(bytes32 _role, address _account) public override onlySelfOrAdmin(_role) {
|
|
130
|
+
_revokeRole(_role, _account);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ========================= OnlyQuorum =========================
|
|
134
|
+
|
|
135
|
+
/// @notice function for quorum to change admin without going through execute function
|
|
136
|
+
/// @dev calldata in the case is abi.encode new admin address
|
|
137
|
+
function quorumChangeAdmin(ExecuteParam calldata _param) external {
|
|
138
|
+
if (_param.expiration <= block.timestamp) {
|
|
139
|
+
revert DVN_InstructionExpired();
|
|
140
|
+
}
|
|
141
|
+
if (_param.target != address(this)) {
|
|
142
|
+
revert DVN_InvalidTarget(_param.target);
|
|
143
|
+
}
|
|
144
|
+
if (_param.vid != vid) {
|
|
145
|
+
revert DVN_InvalidVid(_param.vid);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// generate and validate hash
|
|
149
|
+
bytes32 hash = hashCallData(_param.vid, _param.target, _param.callData, _param.expiration);
|
|
150
|
+
(bool sigsValid, ) = verifySignatures(hash, _param.signatures);
|
|
151
|
+
if (!sigsValid) {
|
|
152
|
+
revert DVN_InvalidSignatures();
|
|
153
|
+
}
|
|
154
|
+
if (usedHashes[hash]) {
|
|
155
|
+
revert DVN_DuplicatedHash(hash);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
usedHashes[hash] = true;
|
|
159
|
+
_grantRole(ADMIN_ROLE, abi.decode(_param.callData, (address)));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ========================= OnlyAdmin =========================
|
|
163
|
+
|
|
164
|
+
/// @param _params array of DstConfigParam
|
|
165
|
+
function setDstConfig(DstConfigParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
|
|
166
|
+
for (uint256 i = 0; i < _params.length; ++i) {
|
|
167
|
+
DstConfigParam calldata param = _params[i];
|
|
168
|
+
dstConfig[param.dstEid] = DstConfig(param.gas, param.multiplierBps, param.floorMarginUSD);
|
|
169
|
+
}
|
|
170
|
+
emit SetDstConfig(_params);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// @dev takes a list of instructions and executes them in order
|
|
174
|
+
/// @dev if any of the instructions fail, it will emit an error event and continue to execute the rest of the instructions
|
|
175
|
+
/// @param _params array of ExecuteParam, includes target, callData, expiration, signatures
|
|
176
|
+
function execute(ExecuteParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
|
|
177
|
+
for (uint256 i = 0; i < _params.length; ++i) {
|
|
178
|
+
ExecuteParam calldata param = _params[i];
|
|
179
|
+
// 1. skip if invalid vid
|
|
180
|
+
if (param.vid != vid) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 2. skip if expired
|
|
185
|
+
if (param.expiration <= block.timestamp) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// generate and validate hash
|
|
190
|
+
bytes32 hash = hashCallData(param.vid, param.target, param.callData, param.expiration);
|
|
191
|
+
|
|
192
|
+
// 3. check signatures
|
|
193
|
+
(bool sigsValid, ) = verifySignatures(hash, param.signatures);
|
|
194
|
+
if (!sigsValid) {
|
|
195
|
+
emit VerifySignaturesFailed(i);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 4. should check hash
|
|
200
|
+
bool shouldCheckHash = _shouldCheckHash(bytes4(param.callData));
|
|
201
|
+
if (shouldCheckHash) {
|
|
202
|
+
if (usedHashes[hash]) {
|
|
203
|
+
emit HashAlreadyUsed(param, hash);
|
|
204
|
+
continue;
|
|
205
|
+
} else {
|
|
206
|
+
usedHashes[hash] = true; // prevent reentry and replay attack
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
(bool success, bytes memory rtnData) = param.target.call(param.callData);
|
|
211
|
+
if (!success) {
|
|
212
|
+
if (shouldCheckHash) {
|
|
213
|
+
// need to unset the usedHash otherwise it cant be used
|
|
214
|
+
usedHashes[hash] = false;
|
|
215
|
+
}
|
|
216
|
+
// emit an event in any case
|
|
217
|
+
emit ExecuteFailed(i, rtnData);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/// @dev to support ULNv2
|
|
223
|
+
/// @dev the withdrawFee function for ULN30X is built in the Worker contract
|
|
224
|
+
/// @param _lib message lib address
|
|
225
|
+
/// @param _to address to withdraw to
|
|
226
|
+
/// @param _amount amount to withdraw
|
|
227
|
+
function withdrawFeeFromUlnV2(address _lib, address payable _to, uint256 _amount) external onlyRole(ADMIN_ROLE) {
|
|
228
|
+
if (!hasRole(MESSAGE_LIB_ROLE, _lib)) {
|
|
229
|
+
revert Worker_OnlyMessageLib();
|
|
230
|
+
}
|
|
231
|
+
ILayerZeroUltraLightNodeV2(_lib).withdrawNative(_to, _amount);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ========================= OnlyMessageLib =========================
|
|
235
|
+
|
|
236
|
+
/// @dev for ULN301, ULN302 and more to assign job
|
|
237
|
+
/// @dev dvn network can reject job from _sender by adding/removing them from allowlist/denylist
|
|
238
|
+
/// @param _param assign job param
|
|
239
|
+
/// @param _options dvn options
|
|
240
|
+
function assignJob(
|
|
241
|
+
AssignJobParam calldata _param,
|
|
242
|
+
bytes calldata _options
|
|
243
|
+
) external payable onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_param.sender) returns (uint256 totalFee) {
|
|
244
|
+
IDVNFeeLib.FeeParams memory feeParams = IDVNFeeLib.FeeParams(
|
|
245
|
+
priceFeed,
|
|
246
|
+
_param.dstEid,
|
|
247
|
+
_param.confirmations,
|
|
248
|
+
_param.sender,
|
|
249
|
+
quorum,
|
|
250
|
+
defaultMultiplierBps
|
|
251
|
+
);
|
|
252
|
+
totalFee = IDVNFeeLib(workerFeeLib).getFeeOnSend(feeParams, dstConfig[_param.dstEid], _options);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// @dev to support ULNv2
|
|
256
|
+
/// @dev dvn network can reject job from _sender by adding/removing them from allowlist/denylist
|
|
257
|
+
/// @param _dstEid destination EndpointId
|
|
258
|
+
/// @param //_outboundProofType outbound proof type
|
|
259
|
+
/// @param _confirmations block confirmations
|
|
260
|
+
/// @param _sender message sender address
|
|
261
|
+
function assignJob(
|
|
262
|
+
uint16 _dstEid,
|
|
263
|
+
uint16 /*_outboundProofType*/,
|
|
264
|
+
uint64 _confirmations,
|
|
265
|
+
address _sender
|
|
266
|
+
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) returns (uint256 totalFee) {
|
|
267
|
+
IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
|
|
268
|
+
priceFeed,
|
|
269
|
+
_dstEid,
|
|
270
|
+
_confirmations,
|
|
271
|
+
_sender,
|
|
272
|
+
quorum,
|
|
273
|
+
defaultMultiplierBps
|
|
274
|
+
);
|
|
275
|
+
// ULNV2 does not have dvn options
|
|
276
|
+
totalFee = IDVNFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[_dstEid], bytes(""));
|
|
277
|
+
emit VerifierFeePaid(totalFee);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/// @dev to support ReadLib
|
|
281
|
+
// @param _packetHeader - version + nonce + path
|
|
282
|
+
// @param _cmd - the command to be executed to obtain the payload
|
|
283
|
+
// @param _options - options
|
|
284
|
+
function assignJob(
|
|
285
|
+
address _sender,
|
|
286
|
+
bytes calldata /*_packetHeader*/,
|
|
287
|
+
bytes calldata _cmd,
|
|
288
|
+
bytes calldata _options
|
|
289
|
+
) external payable onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) returns (uint256 fee) {
|
|
290
|
+
IDVNFeeLib.FeeParamsForRead memory feeParams = IDVNFeeLib.FeeParamsForRead(
|
|
291
|
+
priceFeed,
|
|
292
|
+
_sender,
|
|
293
|
+
quorum,
|
|
294
|
+
defaultMultiplierBps
|
|
295
|
+
);
|
|
296
|
+
fee = IDVNFeeLib(workerFeeLib).getFeeOnSend(feeParams, dstConfig[localEidV2], _cmd, _options);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ========================= View =========================
|
|
300
|
+
|
|
301
|
+
/// @dev getFee can revert if _sender doesn't pass ACL
|
|
302
|
+
/// @param _dstEid destination EndpointId
|
|
303
|
+
/// @param _confirmations block confirmations
|
|
304
|
+
/// @param _sender message sender address
|
|
305
|
+
/// @param _options dvn options
|
|
306
|
+
/// @return fee fee in native amount
|
|
307
|
+
function getFee(
|
|
308
|
+
uint32 _dstEid,
|
|
309
|
+
uint64 _confirmations,
|
|
310
|
+
address _sender,
|
|
311
|
+
bytes calldata _options
|
|
312
|
+
) external view onlyAcl(_sender) returns (uint256 fee) {
|
|
313
|
+
IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
|
|
314
|
+
priceFeed,
|
|
315
|
+
_dstEid,
|
|
316
|
+
_confirmations,
|
|
317
|
+
_sender,
|
|
318
|
+
quorum,
|
|
319
|
+
defaultMultiplierBps
|
|
320
|
+
);
|
|
321
|
+
fee = IDVNFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], _options);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/// @dev to support ULNv2
|
|
325
|
+
/// @dev getFee can revert if _sender doesn't pass ACL
|
|
326
|
+
/// @param _dstEid destination EndpointId
|
|
327
|
+
/// @param //_outboundProofType outbound proof type
|
|
328
|
+
/// @param _confirmations block confirmations
|
|
329
|
+
/// @param _sender message sender address
|
|
330
|
+
function getFee(
|
|
331
|
+
uint16 _dstEid,
|
|
332
|
+
uint16 /*_outboundProofType*/,
|
|
333
|
+
uint64 _confirmations,
|
|
334
|
+
address _sender
|
|
335
|
+
) public view onlyAcl(_sender) returns (uint256 fee) {
|
|
336
|
+
IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
|
|
337
|
+
priceFeed,
|
|
338
|
+
_dstEid,
|
|
339
|
+
_confirmations,
|
|
340
|
+
_sender,
|
|
341
|
+
quorum,
|
|
342
|
+
defaultMultiplierBps
|
|
343
|
+
);
|
|
344
|
+
fee = IDVNFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], bytes(""));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/// @dev to support ReadLib
|
|
348
|
+
// @param _packetHeader - version + nonce + path
|
|
349
|
+
// @param _cmd - the command to be executed to obtain the payload
|
|
350
|
+
// @param _options - options
|
|
351
|
+
function getFee(
|
|
352
|
+
address _sender,
|
|
353
|
+
bytes calldata /*_packetHeader*/,
|
|
354
|
+
bytes calldata _cmd,
|
|
355
|
+
bytes calldata _options
|
|
356
|
+
) external view onlyAcl(_sender) returns (uint256 fee) {
|
|
357
|
+
IDVNFeeLib.FeeParamsForRead memory feeParams = IDVNFeeLib.FeeParamsForRead(
|
|
358
|
+
priceFeed,
|
|
359
|
+
_sender,
|
|
360
|
+
quorum,
|
|
361
|
+
defaultMultiplierBps
|
|
362
|
+
);
|
|
363
|
+
fee = IDVNFeeLib(workerFeeLib).getFee(feeParams, dstConfig[localEidV2], _cmd, _options);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/// @param _target target address
|
|
367
|
+
/// @param _callData call data
|
|
368
|
+
/// @param _expiration expiration timestamp
|
|
369
|
+
/// @return hash of above
|
|
370
|
+
function hashCallData(
|
|
371
|
+
uint32 _vid,
|
|
372
|
+
address _target,
|
|
373
|
+
bytes calldata _callData,
|
|
374
|
+
uint256 _expiration
|
|
375
|
+
) public pure returns (bytes32) {
|
|
376
|
+
return keccak256(abi.encodePacked(_vid, _target, _expiration, _callData));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ========================= Internal =========================
|
|
380
|
+
|
|
381
|
+
/// @dev to save gas, we don't check hash for some functions (where replaying won't change the state)
|
|
382
|
+
/// @dev for example, some administrative functions like changing signers, the contract should check hash to double spending
|
|
383
|
+
/// @dev should ensure that all onlySelf functions have unique functionSig
|
|
384
|
+
/// @param _functionSig function signature
|
|
385
|
+
/// @return true if should check hash
|
|
386
|
+
function _shouldCheckHash(bytes4 _functionSig) internal pure returns (bool) {
|
|
387
|
+
// never check for these selectors to save gas
|
|
388
|
+
return
|
|
389
|
+
_functionSig != IReceiveUlnE2.verify.selector && // 0x0223536e, replaying won't change the state
|
|
390
|
+
_functionSig != ReadLib1002.verify.selector && // 0xab750e75, replaying won't change the state
|
|
391
|
+
_functionSig != ILayerZeroUltraLightNodeV2.updateHash.selector; // 0x704316e5, replaying will be revert at uln
|
|
392
|
+
}
|
|
393
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
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 { ILayerZeroPriceFeed } from "../../interfaces/ILayerZeroPriceFeed.sol";
|
|
9
|
+
import { IDVN } from "../interfaces/IDVN.sol";
|
|
10
|
+
import { IDVNFeeLib } from "../interfaces/IDVNFeeLib.sol";
|
|
11
|
+
import { DVNOptions } from "../libs/DVNOptions.sol";
|
|
12
|
+
import { ReadCmdCodecV1 } from "../libs/ReadCmdCodecV1.sol";
|
|
13
|
+
import { SupportedCmdTypesLib, SupportedCmdTypes, BitMap256 } from "../libs/SupportedCmdTypes.sol";
|
|
14
|
+
|
|
15
|
+
contract DVNFeeLib is Ownable, IDVNFeeLib {
|
|
16
|
+
using DVNOptions for bytes;
|
|
17
|
+
|
|
18
|
+
struct SetSupportedCmdTypesParam {
|
|
19
|
+
uint32 targetEid;
|
|
20
|
+
BitMap256 types;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
struct BlockTimeConfig {
|
|
24
|
+
uint32 avgBlockTime; // milliseconds
|
|
25
|
+
uint64 blockNum; // the block number of the reference timestamp
|
|
26
|
+
uint64 timestamp; // second, the reference timestamp of the block number
|
|
27
|
+
uint32 maxPastRetention; // second, the max retention time the DVN will accept read requests/compute from the past time
|
|
28
|
+
uint32 maxFutureRetention; // second, the max retention time the DVN will accept read requests/compute from the future time
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
uint16 internal constant BPS_BASE = 10000;
|
|
32
|
+
|
|
33
|
+
// encoded( execute(ExecuteParam[]) ): funcSigHash + params -> 4 + 32(Offset of the array) + 32(array size) + 32(first element start offset)\
|
|
34
|
+
// + 32(vid) + 32(target) + 32(calldata-offset) + 32(expiration) + 32(signatures-offset) = 260
|
|
35
|
+
uint16 internal constant EXECUTE_FIXED_BYTES = 260;
|
|
36
|
+
uint16 internal constant SIGNATURE_RAW_BYTES = 65; // not encoded
|
|
37
|
+
// verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations)\
|
|
38
|
+
// 4 + 32(header offset) + 32(payloadHash) + 32(confirmations, 8 -> 32 padded) + 32(header-size) + 96(81 -> header-padded) = 228,
|
|
39
|
+
// padded to multiples of 32 = 256, encoded as bytes with an 32 byte for the bytes size = 288
|
|
40
|
+
uint16 internal constant VERIFY_BYTES_ULN = 288;
|
|
41
|
+
// verify(bytes calldata _packetHeader, bytes32 _cmdHash, bytes32 _payloadHash)\
|
|
42
|
+
// 4 + 32(header offset) + 32(cmdHash) + 32(payloadHash) + 32(header-size) + 96(81 -> header-padded) = 228,
|
|
43
|
+
// padded to multiples of 32 = 256, encoded as bytes with an 32 byte for the bytes size = 288
|
|
44
|
+
uint16 internal constant VERIFY_BYTES_CMD_LIB = 288;
|
|
45
|
+
|
|
46
|
+
uint256 internal immutable nativeDecimalsRate;
|
|
47
|
+
uint32 internal immutable localEidV2; // endpoint-v2 only, for read call
|
|
48
|
+
|
|
49
|
+
SupportedCmdTypes internal supportedCmdTypes;
|
|
50
|
+
|
|
51
|
+
uint120 internal evmCallRequestV1FeeUSD;
|
|
52
|
+
uint120 internal evmCallComputeV1ReduceFeeUSD;
|
|
53
|
+
uint16 internal evmCallComputeV1MapBps;
|
|
54
|
+
|
|
55
|
+
mapping(uint32 dstEid => BlockTimeConfig) public dstBlockTimeConfigs;
|
|
56
|
+
|
|
57
|
+
constructor(uint32 _localEidV2, uint256 _nativeDecimalsRate) {
|
|
58
|
+
localEidV2 = _localEidV2;
|
|
59
|
+
nativeDecimalsRate = _nativeDecimalsRate;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ================================ OnlyOwner ================================
|
|
63
|
+
function setSupportedCmdTypes(SetSupportedCmdTypesParam[] calldata _params) external onlyOwner {
|
|
64
|
+
for (uint256 i = 0; i < _params.length; i++) {
|
|
65
|
+
supportedCmdTypes.cmdTypes[_params[i].targetEid] = _params[i].types;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getSupportedCmdTypes(uint32 _targetEid) external view returns (BitMap256) {
|
|
70
|
+
return supportedCmdTypes.cmdTypes[_targetEid];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setDstBlockTimeConfigs(
|
|
74
|
+
uint32[] calldata dstEids,
|
|
75
|
+
BlockTimeConfig[] calldata _blockConfigs
|
|
76
|
+
) external onlyOwner {
|
|
77
|
+
if (dstEids.length != _blockConfigs.length) revert DVN_INVALID_INPUT_LENGTH();
|
|
78
|
+
for (uint256 i = 0; i < dstEids.length; i++) {
|
|
79
|
+
dstBlockTimeConfigs[dstEids[i]] = _blockConfigs[i];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function withdrawToken(address _token, address _to, uint256 _amount) external onlyOwner {
|
|
84
|
+
// transfers native if _token is address(0x0)
|
|
85
|
+
Transfer.nativeOrToken(_token, _to, _amount);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function setCmdFees(
|
|
89
|
+
uint120 _evmCallRequestV1FeeUSD,
|
|
90
|
+
uint120 _evmCallComputeV1ReduceFeeUSD,
|
|
91
|
+
uint16 _evmCallComputeV1MapBps
|
|
92
|
+
) external onlyOwner {
|
|
93
|
+
evmCallRequestV1FeeUSD = _evmCallRequestV1FeeUSD;
|
|
94
|
+
evmCallComputeV1ReduceFeeUSD = _evmCallComputeV1ReduceFeeUSD;
|
|
95
|
+
evmCallComputeV1MapBps = _evmCallComputeV1MapBps;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getCmdFees() external view returns (uint120, uint120, uint16) {
|
|
99
|
+
return (evmCallRequestV1FeeUSD, evmCallComputeV1ReduceFeeUSD, evmCallComputeV1MapBps);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ========================= External =========================
|
|
103
|
+
/// @dev get fee function that can change state. e.g. paying priceFeed
|
|
104
|
+
/// @param _params fee params
|
|
105
|
+
/// @param _dstConfig dst config
|
|
106
|
+
/// @param //_options options
|
|
107
|
+
function getFeeOnSend(
|
|
108
|
+
FeeParams calldata _params,
|
|
109
|
+
IDVN.DstConfig calldata _dstConfig,
|
|
110
|
+
bytes calldata _options
|
|
111
|
+
) external payable returns (uint256) {
|
|
112
|
+
return getFee(_params, _dstConfig, _options);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function getFeeOnSend(
|
|
116
|
+
FeeParamsForRead calldata _params,
|
|
117
|
+
IDVN.DstConfig calldata _dstConfig,
|
|
118
|
+
bytes calldata _cmd,
|
|
119
|
+
bytes calldata _options
|
|
120
|
+
) external payable returns (uint256 fee) {
|
|
121
|
+
fee = getFee(_params, _dstConfig, _cmd, _options);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ========================= View =========================
|
|
125
|
+
/// @dev get fee view function
|
|
126
|
+
/// @param _params fee params
|
|
127
|
+
/// @param _dstConfig dst config
|
|
128
|
+
/// @param //_options options
|
|
129
|
+
function getFee(
|
|
130
|
+
FeeParams calldata _params,
|
|
131
|
+
IDVN.DstConfig calldata _dstConfig,
|
|
132
|
+
bytes calldata _options
|
|
133
|
+
) public view returns (uint256) {
|
|
134
|
+
if (_dstConfig.gas == 0) revert DVN_EidNotSupported(_params.dstEid);
|
|
135
|
+
|
|
136
|
+
_decodeDVNOptions(_options); // validate options
|
|
137
|
+
|
|
138
|
+
uint256 callDataSize = _getCallDataSize(_params.quorum);
|
|
139
|
+
(uint256 fee, , , uint128 nativePriceUSD) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(
|
|
140
|
+
_params.dstEid,
|
|
141
|
+
callDataSize,
|
|
142
|
+
_dstConfig.gas
|
|
143
|
+
);
|
|
144
|
+
return
|
|
145
|
+
_applyPremium(
|
|
146
|
+
fee,
|
|
147
|
+
_dstConfig.multiplierBps,
|
|
148
|
+
_params.defaultMultiplierBps,
|
|
149
|
+
_dstConfig.floorMarginUSD,
|
|
150
|
+
nativePriceUSD
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getFee(
|
|
155
|
+
FeeParamsForRead calldata _params,
|
|
156
|
+
IDVN.DstConfig calldata _dstConfig,
|
|
157
|
+
bytes calldata _cmd,
|
|
158
|
+
bytes calldata _options
|
|
159
|
+
) public view returns (uint256) {
|
|
160
|
+
if (_dstConfig.gas == 0) revert DVN_EidNotSupported(localEidV2);
|
|
161
|
+
|
|
162
|
+
_decodeDVNOptions(_options); // validate options
|
|
163
|
+
|
|
164
|
+
uint256 callDataSize = _getReadCallDataSize(_params.quorum);
|
|
165
|
+
(uint256 fee, , , uint128 nativePriceUSD) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(
|
|
166
|
+
localEidV2,
|
|
167
|
+
callDataSize,
|
|
168
|
+
_dstConfig.gas
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// cmdFeeUSD -> cmdFee native final
|
|
172
|
+
uint256 cmdFeeUSD = _estimateCmdFee(_cmd);
|
|
173
|
+
uint256 cmdFee = (cmdFeeUSD * nativeDecimalsRate) / nativePriceUSD;
|
|
174
|
+
|
|
175
|
+
return
|
|
176
|
+
_applyPremium(
|
|
177
|
+
fee + cmdFee,
|
|
178
|
+
_dstConfig.multiplierBps,
|
|
179
|
+
_params.defaultMultiplierBps,
|
|
180
|
+
_dstConfig.floorMarginUSD,
|
|
181
|
+
nativePriceUSD
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ========================= Internal =========================
|
|
186
|
+
function _getCallDataSize(uint256 _quorum) internal pure returns (uint256) {
|
|
187
|
+
return _getCallDataSizeByQuorumAndVerifyBytes(_quorum, VERIFY_BYTES_ULN);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function _getReadCallDataSize(uint256 _quorum) internal pure returns (uint256) {
|
|
191
|
+
return _getCallDataSizeByQuorumAndVerifyBytes(_quorum, VERIFY_BYTES_CMD_LIB);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function _getCallDataSizeByQuorumAndVerifyBytes(
|
|
195
|
+
uint256 _quorum,
|
|
196
|
+
uint256 verifyBytes
|
|
197
|
+
) internal pure returns (uint256) {
|
|
198
|
+
uint256 totalSignatureBytes = _quorum * SIGNATURE_RAW_BYTES;
|
|
199
|
+
if (totalSignatureBytes % 32 != 0) {
|
|
200
|
+
totalSignatureBytes = totalSignatureBytes - (totalSignatureBytes % 32) + 32;
|
|
201
|
+
}
|
|
202
|
+
// getFee should charge on execute(updateHash)
|
|
203
|
+
// totalSignatureBytesPadded also has 32 as size of the bytes
|
|
204
|
+
return uint256(EXECUTE_FIXED_BYTES) + verifyBytes + totalSignatureBytes + 32;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function _applyPremium(
|
|
208
|
+
uint256 _fee,
|
|
209
|
+
uint16 _bps,
|
|
210
|
+
uint16 _defaultBps,
|
|
211
|
+
uint128 _marginUSD,
|
|
212
|
+
uint128 _nativePriceUSD
|
|
213
|
+
) internal view returns (uint256) {
|
|
214
|
+
uint16 multiplierBps = _bps == 0 ? _defaultBps : _bps;
|
|
215
|
+
|
|
216
|
+
uint256 feeWithMultiplier = (_fee * multiplierBps) / 10000;
|
|
217
|
+
if (_nativePriceUSD == 0 || _marginUSD == 0) {
|
|
218
|
+
return feeWithMultiplier;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
uint256 feeWithFloorMargin = _fee + (_marginUSD * nativeDecimalsRate) / _nativePriceUSD;
|
|
222
|
+
|
|
223
|
+
return feeWithFloorMargin > feeWithMultiplier ? feeWithFloorMargin : feeWithMultiplier;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function _decodeDVNOptions(bytes calldata _options) internal pure returns (uint256) {
|
|
227
|
+
uint256 cursor;
|
|
228
|
+
while (cursor < _options.length) {
|
|
229
|
+
(uint8 optionType, , uint256 newCursor) = _options.nextDVNOption(cursor);
|
|
230
|
+
cursor = newCursor;
|
|
231
|
+
revert DVN_UnsupportedOptionType(optionType);
|
|
232
|
+
}
|
|
233
|
+
if (cursor != _options.length) revert DVNOptions.DVN_InvalidDVNOptions(cursor);
|
|
234
|
+
|
|
235
|
+
return 0; // todo: precrime fee model
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function _estimateCmdFee(bytes calldata _cmd) internal view returns (uint256 fee) {
|
|
239
|
+
ReadCmdCodecV1.Cmd memory cmd = ReadCmdCodecV1.decode(_cmd, _assertCmdTypeSupported);
|
|
240
|
+
fee = cmd.numEvmCallRequestV1 * evmCallRequestV1FeeUSD;
|
|
241
|
+
if (cmd.evmCallComputeV1Map) {
|
|
242
|
+
fee += (fee * evmCallComputeV1MapBps) / BPS_BASE;
|
|
243
|
+
}
|
|
244
|
+
if (cmd.evmCallComputeV1Reduce) {
|
|
245
|
+
fee += evmCallComputeV1ReduceFeeUSD;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function _assertCmdTypeSupported(
|
|
250
|
+
uint32 _targetEid,
|
|
251
|
+
bool _isBlockNum,
|
|
252
|
+
uint64 _blockNumOrTimestamp,
|
|
253
|
+
uint8 _cmdType
|
|
254
|
+
) internal view {
|
|
255
|
+
supportedCmdTypes.assertSupported(_targetEid, _cmdType);
|
|
256
|
+
if (supportedCmdTypes.isSupported(_targetEid, SupportedCmdTypesLib.CMD_V1__TIMESTAMP_VALIDATE)) {
|
|
257
|
+
BlockTimeConfig memory blockCnf = dstBlockTimeConfigs[_targetEid];
|
|
258
|
+
uint64 timestamp = _blockNumOrTimestamp;
|
|
259
|
+
if (_isBlockNum) {
|
|
260
|
+
// convert the blockNum to the timestamp
|
|
261
|
+
if (_blockNumOrTimestamp > blockCnf.blockNum) {
|
|
262
|
+
timestamp =
|
|
263
|
+
blockCnf.timestamp +
|
|
264
|
+
((_blockNumOrTimestamp - blockCnf.blockNum) * blockCnf.avgBlockTime) /
|
|
265
|
+
1000;
|
|
266
|
+
} else {
|
|
267
|
+
timestamp =
|
|
268
|
+
blockCnf.timestamp -
|
|
269
|
+
((blockCnf.blockNum - _blockNumOrTimestamp) * blockCnf.avgBlockTime) /
|
|
270
|
+
1000;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (
|
|
274
|
+
timestamp + blockCnf.maxPastRetention < block.timestamp ||
|
|
275
|
+
timestamp > block.timestamp + blockCnf.maxFutureRetention
|
|
276
|
+
) {
|
|
277
|
+
revert DVN_TimestampOutOfRange(_targetEid, timestamp);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function version() external pure returns (uint64 major, uint8 minor) {
|
|
283
|
+
return (1, 1);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// send funds here to pay for price feed directly
|
|
287
|
+
receive() external payable {}
|
|
288
|
+
}
|