@layerzerolabs/lz-evm-messagelib-v2 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/contracts/Executor.sol/Executor.json +1156 -0
- package/artifacts/contracts/ExecutorFeeLib.sol/ExecutorFeeLib.json +336 -0
- package/artifacts/contracts/MessageLibBase.sol/MessageLibBase.json +16 -0
- package/artifacts/contracts/PriceFeed.sol/PriceFeed.json +650 -0
- package/artifacts/contracts/ReceiveLibBaseE2.sol/ReceiveLibBaseE2.json +154 -0
- package/artifacts/contracts/SendLibBase.sol/SendLibBase.json +381 -0
- package/artifacts/contracts/SendLibBaseE2.sol/SendLibBaseE2.json +819 -0
- package/artifacts/contracts/Treasury.sol/Treasury.json +309 -0
- package/artifacts/contracts/Worker.sol/Worker.json +579 -0
- package/artifacts/contracts/interfaces/IExecutor.sol/IExecutor.json +429 -0
- package/artifacts/contracts/interfaces/IExecutorFeeLib.sol/IExecutorFeeLib.json +210 -0
- package/artifacts/contracts/interfaces/ILayerZeroExecutor.sol/ILayerZeroExecutor.json +79 -0
- package/artifacts/contracts/interfaces/ILayerZeroPriceFeed.sol/ILayerZeroPriceFeed.json +222 -0
- package/artifacts/contracts/interfaces/ILayerZeroTreasury.sol/ILayerZeroTreasury.json +79 -0
- package/artifacts/contracts/interfaces/IWorker.sol/IWorker.json +221 -0
- package/artifacts/contracts/libs/SafeCall.sol/SafeCall.json +10 -0
- package/artifacts/contracts/uln/LzExecutor.sol/LzExecutor.json +256 -0
- package/artifacts/contracts/uln/ReceiveUlnBase.sol/ReceiveUlnBase.json +472 -0
- package/artifacts/contracts/uln/SendUlnBase.sol/SendUlnBase.json +412 -0
- package/artifacts/contracts/uln/UlnBase.sol/UlnBase.json +387 -0
- package/artifacts/contracts/uln/dvn/DVN.sol/DVN.json +1370 -0
- package/artifacts/contracts/uln/dvn/DVNFeeLib.sol/DVNFeeLib.json +300 -0
- package/artifacts/contracts/uln/dvn/MultiSig.sol/MultiSig.json +164 -0
- package/artifacts/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapter.sol/CCIPDVNAdapter.json +742 -0
- package/artifacts/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapterFeeLib.sol/CCIPDVNAdapterFeeLib.json +50 -0
- package/artifacts/contracts/uln/dvn/adapters/DVNAdapterBase.sol/DVNAdapterBase.json +463 -0
- package/artifacts/contracts/uln/dvn/adapters/DVNAdapterFeeLibBase.sol/DVNAdapterFeeLibBase.json +50 -0
- package/artifacts/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapter.sol/AxelarDVNAdapter.json +804 -0
- package/artifacts/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapterFeeLib.sol/AxelarDVNAdapterFeeLib.json +50 -0
- package/artifacts/contracts/uln/interfaces/IDVN.sol/IDVN.json +370 -0
- package/artifacts/contracts/uln/interfaces/IDVNAdapterFeeLib.sol/IDVNAdapterFeeLib.json +50 -0
- package/artifacts/contracts/uln/interfaces/IDVNFeeLib.sol/IDVNFeeLib.json +178 -0
- package/artifacts/contracts/uln/interfaces/ILayerZeroDVN.sol/ILayerZeroDVN.json +96 -0
- package/artifacts/contracts/uln/interfaces/IReceiveUlnE2.sol/IReceiveUlnE2.json +76 -0
- package/artifacts/contracts/uln/libs/DVNOptions.sol/DVNOptions.json +27 -0
- package/artifacts/contracts/uln/libs/UlnOptions.sol/UlnOptions.json +54 -0
- package/artifacts/contracts/uln/uln301/AddressSizeConfig.sol/AddressSizeConfig.json +129 -0
- package/artifacts/contracts/uln/uln301/ReceiveLibBaseE1.sol/ILayerZeroReceiveLibrary.json +68 -0
- package/artifacts/contracts/uln/uln301/ReceiveLibBaseE1.sol/ReceiveLibBaseE1.json +416 -0
- package/artifacts/contracts/uln/uln301/ReceiveUln301.sol/ReceiveUln301.json +969 -0
- package/artifacts/contracts/uln/uln301/SendLibBaseE1.sol/SendLibBaseE1.json +804 -0
- package/artifacts/contracts/uln/uln301/SendUln301.sol/SendUln301.json +1278 -0
- package/artifacts/contracts/uln/uln301/TreasuryFeeHandler.sol/TreasuryFeeHandler.json +94 -0
- package/artifacts/contracts/uln/uln301/interfaces/IMessageLibE1.sol/IMessageLibE1.json +247 -0
- package/artifacts/contracts/uln/uln301/interfaces/INonceContract.sol/INonceContract.json +40 -0
- package/artifacts/contracts/uln/uln301/interfaces/ITreasuryFeeHandler.sol/ITreasuryFeeHandler.json +44 -0
- package/artifacts/contracts/uln/uln301/interfaces/IUltraLightNode301.sol/IUltraLightNode301.json +29 -0
- package/artifacts/contracts/uln/uln301/mocks/NonceContractMock.sol/NonceContractMock.json +93 -0
- package/artifacts/contracts/uln/uln302/ReceiveUln302.sol/ReceiveUln302.json +702 -0
- package/artifacts/contracts/uln/uln302/SendUln302.sol/SendUln302.json +1259 -0
- package/artifacts/contracts/upgradeable/WorkerUpgradeable.sol/WorkerUpgradeable.json +592 -0
- package/artifacts/contracts/upgradeable/proxy/ProxyAdmin.sol/ProxyAdmin.json +181 -0
- package/artifacts/contracts/upgradeable/proxy/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +160 -0
- package/contracts/Executor.sol +162 -0
- package/contracts/ExecutorFeeLib.sol +190 -0
- package/contracts/MessageLibBase.sol +21 -0
- package/contracts/PriceFeed.sol +257 -0
- package/contracts/ReceiveLibBaseE2.sol +54 -0
- package/contracts/SendLibBase.sol +258 -0
- package/contracts/SendLibBaseE2.sol +140 -0
- package/contracts/Treasury.sol +74 -0
- package/contracts/Worker.sol +167 -0
- package/contracts/interfaces/IExecutor.sol +44 -0
- package/contracts/interfaces/IExecutorFeeLib.sol +32 -0
- package/contracts/interfaces/ILayerZeroExecutor.sol +29 -0
- package/contracts/interfaces/ILayerZeroPriceFeed.sol +61 -0
- package/contracts/interfaces/ILayerZeroTreasury.sol +19 -0
- package/contracts/interfaces/IWorker.sol +29 -0
- package/contracts/libs/SafeCall.sol +123 -0
- package/contracts/uln/LzExecutor.sol +96 -0
- package/contracts/uln/ReceiveUlnBase.sol +118 -0
- package/contracts/uln/SendUlnBase.sol +129 -0
- package/contracts/uln/UlnBase.sol +195 -0
- package/contracts/uln/dvn/DVN.sol +349 -0
- package/contracts/uln/dvn/DVNFeeLib.sol +141 -0
- package/contracts/uln/dvn/MultiSig.sol +104 -0
- package/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapter.sol +152 -0
- package/contracts/uln/dvn/adapters/CCIP/CCIPDVNAdapterFeeLib.sol +7 -0
- package/contracts/uln/dvn/adapters/DVNAdapterBase.sol +162 -0
- package/contracts/uln/dvn/adapters/DVNAdapterFeeLibBase.sol +20 -0
- package/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapter.sol +156 -0
- package/contracts/uln/dvn/adapters/axelar/AxelarDVNAdapterFeeLib.sol +7 -0
- package/contracts/uln/interfaces/IDVN.sol +25 -0
- package/contracts/uln/interfaces/IDVNAdapterFeeLib.sol +13 -0
- package/contracts/uln/interfaces/IDVNFeeLib.sol +30 -0
- package/contracts/uln/interfaces/ILayerZeroDVN.sol +34 -0
- package/contracts/uln/interfaces/IReceiveUlnE2.sol +18 -0
- package/contracts/uln/libs/DVNOptions.sol +181 -0
- package/contracts/uln/libs/UlnOptions.sol +176 -0
- package/contracts/uln/uln301/AddressSizeConfig.sol +22 -0
- package/contracts/uln/uln301/ReceiveLibBaseE1.sol +103 -0
- package/contracts/uln/uln301/ReceiveUln301.sol +120 -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 +108 -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/package.json +51 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
|
+
|
|
7
|
+
// the formal properties are documented in the setter functions
|
|
8
|
+
struct UlnConfig {
|
|
9
|
+
uint64 confirmations;
|
|
10
|
+
// we store the length of required DVNs and optional DVNs instead of using DVN.length directly to save gas
|
|
11
|
+
uint8 requiredDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
|
|
12
|
+
uint8 optionalDVNCount; // 0 indicate DEFAULT, NIL_DVN_COUNT indicate NONE (to override the value of default)
|
|
13
|
+
uint8 optionalDVNThreshold; // (0, optionalDVNCount]
|
|
14
|
+
address[] requiredDVNs; // no duplicates. sorted an an ascending order. allowed overlap with optionalDVNs
|
|
15
|
+
address[] optionalDVNs; // no duplicates. sorted an an ascending order. allowed overlap with requiredDVNs
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
struct SetDefaultUlnConfigParam {
|
|
19
|
+
uint32 eid;
|
|
20
|
+
UlnConfig config;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// @dev includes the utility functions for checking ULN states and logics
|
|
24
|
+
abstract contract UlnBase is Ownable {
|
|
25
|
+
address private constant DEFAULT_CONFIG = address(0);
|
|
26
|
+
// reserved values for
|
|
27
|
+
uint8 internal constant DEFAULT = 0;
|
|
28
|
+
uint8 internal constant NIL_DVN_COUNT = type(uint8).max;
|
|
29
|
+
uint64 internal constant NIL_CONFIRMATIONS = type(uint64).max;
|
|
30
|
+
// 127 to prevent total number of DVNs (127 * 2) exceeding uint8.max (255)
|
|
31
|
+
// by limiting the total size, it would help constraint the design of DVNOptions
|
|
32
|
+
uint8 private constant MAX_COUNT = (type(uint8).max - 1) / 2;
|
|
33
|
+
|
|
34
|
+
mapping(address oapp => mapping(uint32 eid => UlnConfig)) internal ulnConfigs;
|
|
35
|
+
|
|
36
|
+
error Unsorted();
|
|
37
|
+
error InvalidRequiredDVNCount();
|
|
38
|
+
error InvalidOptionalDVNCount();
|
|
39
|
+
error AtLeastOneDVN();
|
|
40
|
+
error InvalidOptionalDVNThreshold();
|
|
41
|
+
error InvalidConfirmations();
|
|
42
|
+
error UnsupportedEid(uint32 eid);
|
|
43
|
+
|
|
44
|
+
event DefaultUlnConfigsSet(SetDefaultUlnConfigParam[] params);
|
|
45
|
+
event UlnConfigSet(address oapp, uint32 eid, UlnConfig config);
|
|
46
|
+
|
|
47
|
+
// ============================ OnlyOwner ===================================
|
|
48
|
+
|
|
49
|
+
/// @dev about the DEFAULT ULN config
|
|
50
|
+
/// 1) its values are all LITERAL (e.g. 0 is 0). whereas in the oapp ULN config, 0 (default value) points to the default ULN config
|
|
51
|
+
/// this design enables the oapp to point to DEFAULT config without explicitly setting the config
|
|
52
|
+
/// 2) its configuration is more restrictive than the oapp ULN config that
|
|
53
|
+
/// a) it must not use NIL value, where NIL is used only by oapps to indicate the LITERAL 0
|
|
54
|
+
/// b) it must have at least one DVN
|
|
55
|
+
function setDefaultUlnConfigs(SetDefaultUlnConfigParam[] calldata _params) external onlyOwner {
|
|
56
|
+
for (uint256 i = 0; i < _params.length; ++i) {
|
|
57
|
+
SetDefaultUlnConfigParam calldata param = _params[i];
|
|
58
|
+
|
|
59
|
+
// 2.a must not use NIL
|
|
60
|
+
if (param.config.requiredDVNCount == NIL_DVN_COUNT) revert InvalidRequiredDVNCount();
|
|
61
|
+
if (param.config.optionalDVNCount == NIL_DVN_COUNT) revert InvalidOptionalDVNCount();
|
|
62
|
+
if (param.config.confirmations == NIL_CONFIRMATIONS) revert InvalidConfirmations();
|
|
63
|
+
|
|
64
|
+
// 2.b must have at least one dvn
|
|
65
|
+
_assertAtLeastOneDVN(param.config);
|
|
66
|
+
|
|
67
|
+
_setConfig(DEFAULT_CONFIG, param.eid, param.config);
|
|
68
|
+
}
|
|
69
|
+
emit DefaultUlnConfigsSet(_params);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================ View ===================================
|
|
73
|
+
// @dev assuming most oapps use default, we get default as memory and custom as storage to save gas
|
|
74
|
+
function getUlnConfig(address _oapp, uint32 _remoteEid) public view returns (UlnConfig memory rtnConfig) {
|
|
75
|
+
UlnConfig storage defaultConfig = ulnConfigs[DEFAULT_CONFIG][_remoteEid];
|
|
76
|
+
UlnConfig storage customConfig = ulnConfigs[_oapp][_remoteEid];
|
|
77
|
+
|
|
78
|
+
// if confirmations is 0, use default
|
|
79
|
+
uint64 confirmations = customConfig.confirmations;
|
|
80
|
+
if (confirmations == DEFAULT) {
|
|
81
|
+
rtnConfig.confirmations = defaultConfig.confirmations;
|
|
82
|
+
} else if (confirmations != NIL_CONFIRMATIONS) {
|
|
83
|
+
// if confirmations is uint64.max, no block confirmations required
|
|
84
|
+
rtnConfig.confirmations = confirmations;
|
|
85
|
+
} // else do nothing, rtnConfig.confirmation is 0
|
|
86
|
+
|
|
87
|
+
if (customConfig.requiredDVNCount == DEFAULT) {
|
|
88
|
+
if (defaultConfig.requiredDVNCount > 0) {
|
|
89
|
+
// copy only if count > 0. save gas
|
|
90
|
+
rtnConfig.requiredDVNs = defaultConfig.requiredDVNs;
|
|
91
|
+
rtnConfig.requiredDVNCount = defaultConfig.requiredDVNCount;
|
|
92
|
+
} // else, do nothing
|
|
93
|
+
} else {
|
|
94
|
+
if (customConfig.requiredDVNCount != NIL_DVN_COUNT) {
|
|
95
|
+
rtnConfig.requiredDVNs = customConfig.requiredDVNs;
|
|
96
|
+
rtnConfig.requiredDVNCount = customConfig.requiredDVNCount;
|
|
97
|
+
} // else, do nothing
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (customConfig.optionalDVNCount == DEFAULT) {
|
|
101
|
+
if (defaultConfig.optionalDVNCount > 0) {
|
|
102
|
+
// copy only if count > 0. save gas
|
|
103
|
+
rtnConfig.optionalDVNs = defaultConfig.optionalDVNs;
|
|
104
|
+
rtnConfig.optionalDVNCount = defaultConfig.optionalDVNCount;
|
|
105
|
+
rtnConfig.optionalDVNThreshold = defaultConfig.optionalDVNThreshold;
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
if (customConfig.optionalDVNCount != NIL_DVN_COUNT) {
|
|
109
|
+
rtnConfig.optionalDVNs = customConfig.optionalDVNs;
|
|
110
|
+
rtnConfig.optionalDVNCount = customConfig.optionalDVNCount;
|
|
111
|
+
rtnConfig.optionalDVNThreshold = customConfig.optionalDVNThreshold;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// the final value must have at least one dvn
|
|
116
|
+
// it is possible that some default config result into 0 dvns
|
|
117
|
+
_assertAtLeastOneDVN(rtnConfig);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// @dev Get the uln config without the default config for the given remoteEid.
|
|
121
|
+
function getAppUlnConfig(address _oapp, uint32 _remoteEid) external view returns (UlnConfig memory) {
|
|
122
|
+
return ulnConfigs[_oapp][_remoteEid];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ============================ Internal ===================================
|
|
126
|
+
function _setUlnConfig(uint32 _remoteEid, address _oapp, UlnConfig memory _param) internal {
|
|
127
|
+
_setConfig(_oapp, _remoteEid, _param);
|
|
128
|
+
|
|
129
|
+
// get ULN config again as a catch all to ensure the config is valid
|
|
130
|
+
getUlnConfig(_oapp, _remoteEid);
|
|
131
|
+
emit UlnConfigSet(_oapp, _remoteEid, _param);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// @dev a supported Eid must have a valid default uln config, which has at least one dvn
|
|
135
|
+
function _isSupportedEid(uint32 _remoteEid) internal view returns (bool) {
|
|
136
|
+
UlnConfig storage defaultConfig = ulnConfigs[DEFAULT_CONFIG][_remoteEid];
|
|
137
|
+
return defaultConfig.requiredDVNCount > 0 || defaultConfig.optionalDVNThreshold > 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function _assertSupportedEid(uint32 _remoteEid) internal view {
|
|
141
|
+
if (!_isSupportedEid(_remoteEid)) revert UnsupportedEid(_remoteEid);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ============================ Private ===================================
|
|
145
|
+
|
|
146
|
+
function _assertAtLeastOneDVN(UlnConfig memory _config) private pure {
|
|
147
|
+
if (_config.requiredDVNCount == 0 && _config.optionalDVNThreshold == 0) revert AtLeastOneDVN();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// @dev this private function is used in both setDefaultUlnConfigs and setUlnConfig
|
|
151
|
+
function _setConfig(address _oapp, uint32 _eid, UlnConfig memory _param) private {
|
|
152
|
+
// @dev required dvns
|
|
153
|
+
// if dvnCount == NONE, dvns list must be empty
|
|
154
|
+
// if dvnCount == DEFAULT, dvn list must be empty
|
|
155
|
+
// otherwise, dvnList.length == dvnCount and assert the list is valid
|
|
156
|
+
if (_param.requiredDVNCount == NIL_DVN_COUNT || _param.requiredDVNCount == DEFAULT) {
|
|
157
|
+
if (_param.requiredDVNs.length != 0) revert InvalidRequiredDVNCount();
|
|
158
|
+
} else {
|
|
159
|
+
if (_param.requiredDVNs.length != _param.requiredDVNCount || _param.requiredDVNCount > MAX_COUNT)
|
|
160
|
+
revert InvalidRequiredDVNCount();
|
|
161
|
+
_assertNoDuplicates(_param.requiredDVNs);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// @dev optional dvns
|
|
165
|
+
// if optionalDVNCount == NONE, optionalDVNs list must be empty and threshold must be 0
|
|
166
|
+
// if optionalDVNCount == DEFAULT, optionalDVNs list must be empty and threshold must be 0
|
|
167
|
+
// otherwise, optionalDVNs.length == optionalDVNCount, threshold > 0 && threshold <= optionalDVNCount and assert the list is valid
|
|
168
|
+
|
|
169
|
+
// example use case: an oapp uses the DEFAULT 'required' but
|
|
170
|
+
// a) use a custom 1/1 dvn (practically a required dvn), or
|
|
171
|
+
// b) use a custom 2/3 dvn
|
|
172
|
+
if (_param.optionalDVNCount == NIL_DVN_COUNT || _param.optionalDVNCount == DEFAULT) {
|
|
173
|
+
if (_param.optionalDVNs.length != 0) revert InvalidOptionalDVNCount();
|
|
174
|
+
if (_param.optionalDVNThreshold != 0) revert InvalidOptionalDVNThreshold();
|
|
175
|
+
} else {
|
|
176
|
+
if (_param.optionalDVNs.length != _param.optionalDVNCount || _param.optionalDVNCount > MAX_COUNT)
|
|
177
|
+
revert InvalidOptionalDVNCount();
|
|
178
|
+
if (_param.optionalDVNThreshold == 0 || _param.optionalDVNThreshold > _param.optionalDVNCount)
|
|
179
|
+
revert InvalidOptionalDVNThreshold();
|
|
180
|
+
_assertNoDuplicates(_param.optionalDVNs);
|
|
181
|
+
}
|
|
182
|
+
// don't assert valid count here, as it needs to be validated along side default config
|
|
183
|
+
|
|
184
|
+
ulnConfigs[_oapp][_eid] = _param;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function _assertNoDuplicates(address[] memory _dvns) private pure {
|
|
188
|
+
address lastDVN = address(0);
|
|
189
|
+
for (uint256 i = 0; i < _dvns.length; i++) {
|
|
190
|
+
address dvn = _dvns[i];
|
|
191
|
+
if (dvn <= lastDVN) revert Unsorted(); // to ensure no duplicates
|
|
192
|
+
lastDVN = dvn;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity 0.8.22;
|
|
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 { IDVN } from "../interfaces/IDVN.sol";
|
|
10
|
+
import { IDVNFeeLib } from "../interfaces/IDVNFeeLib.sol";
|
|
11
|
+
import { IReceiveUlnE2 } from "../interfaces/IReceiveUlnE2.sol";
|
|
12
|
+
|
|
13
|
+
struct ExecuteParam {
|
|
14
|
+
uint32 vid;
|
|
15
|
+
address target;
|
|
16
|
+
bytes callData;
|
|
17
|
+
uint256 expiration;
|
|
18
|
+
bytes signatures;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
contract DVN is Worker, MultiSig, IDVN {
|
|
22
|
+
// to uniquely identify this DVN instance
|
|
23
|
+
// set to endpoint v1 eid if available OR endpoint v2 eid % 30_000
|
|
24
|
+
uint32 public immutable vid;
|
|
25
|
+
|
|
26
|
+
mapping(uint32 dstEid => DstConfig) public dstConfig;
|
|
27
|
+
mapping(bytes32 executableHash => bool used) public usedHashes;
|
|
28
|
+
|
|
29
|
+
error OnlySelf();
|
|
30
|
+
error InvalidRole(bytes32 role);
|
|
31
|
+
error InstructionExpired();
|
|
32
|
+
error InvalidTarget(address target);
|
|
33
|
+
error InvalidVid(uint32 vid);
|
|
34
|
+
error InvalidSignatures();
|
|
35
|
+
error DuplicatedHash(bytes32 executableHash);
|
|
36
|
+
|
|
37
|
+
event VerifySignaturesFailed(uint256 idx);
|
|
38
|
+
event ExecuteFailed(uint256 _index, bytes _data);
|
|
39
|
+
event HashAlreadyUsed(ExecuteParam param, bytes32 _hash);
|
|
40
|
+
// same as DVNFeePaid, but for ULNv2
|
|
41
|
+
event VerifierFeePaid(uint256 fee);
|
|
42
|
+
|
|
43
|
+
// ========================= Constructor =========================
|
|
44
|
+
|
|
45
|
+
/// @dev DVN doesn't have a roleAdmin (address(0x0))
|
|
46
|
+
/// @dev Supports all of ULNv2, ULN301, ULN302 and more
|
|
47
|
+
/// @param _vid unique identifier for this DVN instance
|
|
48
|
+
/// @param _messageLibs array of message lib addresses that are granted the MESSAGE_LIB_ROLE
|
|
49
|
+
/// @param _priceFeed price feed address
|
|
50
|
+
/// @param _signers array of signer addresses for multisig
|
|
51
|
+
/// @param _quorum quorum for multisig
|
|
52
|
+
/// @param _admins array of admin addresses that are granted the ADMIN_ROLE
|
|
53
|
+
constructor(
|
|
54
|
+
uint32 _vid,
|
|
55
|
+
address[] memory _messageLibs,
|
|
56
|
+
address _priceFeed,
|
|
57
|
+
address[] memory _signers,
|
|
58
|
+
uint64 _quorum,
|
|
59
|
+
address[] memory _admins
|
|
60
|
+
) Worker(_messageLibs, _priceFeed, 12000, address(0x0), _admins) MultiSig(_signers, _quorum) {
|
|
61
|
+
vid = _vid;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ========================= Modifier =========================
|
|
65
|
+
|
|
66
|
+
/// @dev depending on role, restrict access to only self or admin
|
|
67
|
+
/// @dev ALLOWLIST, DENYLIST, MESSAGE_LIB_ROLE can only be granted/revoked by self
|
|
68
|
+
/// @dev ADMIN_ROLE can only be granted/revoked by admin
|
|
69
|
+
/// @dev reverts if not one of the above roles
|
|
70
|
+
/// @param _role role to check
|
|
71
|
+
modifier onlySelfOrAdmin(bytes32 _role) {
|
|
72
|
+
if (_role == ALLOWLIST || _role == DENYLIST || _role == MESSAGE_LIB_ROLE) {
|
|
73
|
+
// self required
|
|
74
|
+
if (address(this) != msg.sender) {
|
|
75
|
+
revert OnlySelf();
|
|
76
|
+
}
|
|
77
|
+
} else if (_role == ADMIN_ROLE) {
|
|
78
|
+
// admin required
|
|
79
|
+
_checkRole(ADMIN_ROLE);
|
|
80
|
+
} else {
|
|
81
|
+
revert InvalidRole(_role);
|
|
82
|
+
}
|
|
83
|
+
_;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
modifier onlySelf() {
|
|
87
|
+
if (address(this) != msg.sender) {
|
|
88
|
+
revert OnlySelf();
|
|
89
|
+
}
|
|
90
|
+
_;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ========================= OnlySelf =========================
|
|
94
|
+
|
|
95
|
+
/// @dev set signers for multisig
|
|
96
|
+
/// @dev function sig 0x31cb6105
|
|
97
|
+
/// @param _signer signer address
|
|
98
|
+
/// @param _active true to add, false to remove
|
|
99
|
+
function setSigner(address _signer, bool _active) external onlySelf {
|
|
100
|
+
_setSigner(_signer, _active);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// @dev set quorum for multisig
|
|
104
|
+
/// @dev function sig 0x8585c945
|
|
105
|
+
/// @param _quorum to set
|
|
106
|
+
function setQuorum(uint64 _quorum) external onlySelf {
|
|
107
|
+
_setQuorum(_quorum);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ========================= OnlySelf / OnlyAdmin =========================
|
|
111
|
+
|
|
112
|
+
/// @dev overrides AccessControl to allow self/admin to grant role'
|
|
113
|
+
/// @dev function sig 0x2f2ff15d
|
|
114
|
+
/// @param _role role to grant
|
|
115
|
+
/// @param _account account to grant role to
|
|
116
|
+
function grantRole(bytes32 _role, address _account) public override onlySelfOrAdmin(_role) {
|
|
117
|
+
_grantRole(_role, _account);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// @dev overrides AccessControl to allow self/admin to revoke role
|
|
121
|
+
/// @dev function sig 0xd547741f
|
|
122
|
+
/// @param _role role to revoke
|
|
123
|
+
/// @param _account account to revoke role from
|
|
124
|
+
function revokeRole(bytes32 _role, address _account) public override onlySelfOrAdmin(_role) {
|
|
125
|
+
_revokeRole(_role, _account);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ========================= OnlyQuorum =========================
|
|
129
|
+
|
|
130
|
+
/// @notice function for quorum to change admin without going through execute function
|
|
131
|
+
/// @dev calldata in the case is abi.encode new admin address
|
|
132
|
+
function quorumChangeAdmin(ExecuteParam calldata _param) external {
|
|
133
|
+
if (_param.expiration <= block.timestamp) {
|
|
134
|
+
revert InstructionExpired();
|
|
135
|
+
}
|
|
136
|
+
if (_param.target != address(this)) {
|
|
137
|
+
revert InvalidTarget(_param.target);
|
|
138
|
+
}
|
|
139
|
+
if (_param.vid != vid) {
|
|
140
|
+
revert InvalidVid(_param.vid);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// generate and validate hash
|
|
144
|
+
bytes32 hash = hashCallData(_param.vid, _param.target, _param.callData, _param.expiration);
|
|
145
|
+
(bool sigsValid, ) = verifySignatures(hash, _param.signatures);
|
|
146
|
+
if (!sigsValid) {
|
|
147
|
+
revert InvalidSignatures();
|
|
148
|
+
}
|
|
149
|
+
if (usedHashes[hash]) {
|
|
150
|
+
revert DuplicatedHash(hash);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
usedHashes[hash] = true;
|
|
154
|
+
_grantRole(ADMIN_ROLE, abi.decode(_param.callData, (address)));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ========================= OnlyAdmin =========================
|
|
158
|
+
|
|
159
|
+
/// @param _params array of DstConfigParam
|
|
160
|
+
function setDstConfig(DstConfigParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
|
|
161
|
+
for (uint256 i = 0; i < _params.length; ++i) {
|
|
162
|
+
DstConfigParam calldata param = _params[i];
|
|
163
|
+
dstConfig[param.dstEid] = DstConfig(param.gas, param.multiplierBps, param.floorMarginUSD);
|
|
164
|
+
}
|
|
165
|
+
emit SetDstConfig(_params);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// @dev takes a list of instructions and executes them in order
|
|
169
|
+
/// @dev if any of the instructions fail, it will emit an error event and continue to execute the rest of the instructions
|
|
170
|
+
/// @param _params array of ExecuteParam, includes target, callData, expiration, signatures
|
|
171
|
+
function execute(ExecuteParam[] calldata _params) external onlyRole(ADMIN_ROLE) {
|
|
172
|
+
for (uint256 i = 0; i < _params.length; ++i) {
|
|
173
|
+
ExecuteParam calldata param = _params[i];
|
|
174
|
+
// 1. skip if invalid vid
|
|
175
|
+
if (param.vid != vid) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 2. skip if expired
|
|
180
|
+
if (param.expiration <= block.timestamp) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// generate and validate hash
|
|
185
|
+
bytes32 hash = hashCallData(param.vid, param.target, param.callData, param.expiration);
|
|
186
|
+
|
|
187
|
+
// 3. check signatures
|
|
188
|
+
(bool sigsValid, ) = verifySignatures(hash, param.signatures);
|
|
189
|
+
if (!sigsValid) {
|
|
190
|
+
emit VerifySignaturesFailed(i);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 4. should check hash
|
|
195
|
+
bool shouldCheckHash = _shouldCheckHash(bytes4(param.callData));
|
|
196
|
+
if (shouldCheckHash) {
|
|
197
|
+
if (usedHashes[hash]) {
|
|
198
|
+
emit HashAlreadyUsed(param, hash);
|
|
199
|
+
continue;
|
|
200
|
+
} else {
|
|
201
|
+
usedHashes[hash] = true; // prevent reentry and replay attack
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
(bool success, bytes memory rtnData) = param.target.call(param.callData);
|
|
206
|
+
if (!success) {
|
|
207
|
+
if (shouldCheckHash) {
|
|
208
|
+
// need to unset the usedHash otherwise it cant be used
|
|
209
|
+
usedHashes[hash] = false;
|
|
210
|
+
}
|
|
211
|
+
// emit an event in any case
|
|
212
|
+
emit ExecuteFailed(i, rtnData);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// @dev to support ULNv2
|
|
218
|
+
/// @dev the withdrawFee function for ULN30X is built in the Worker contract
|
|
219
|
+
/// @param _lib message lib address
|
|
220
|
+
/// @param _to address to withdraw to
|
|
221
|
+
/// @param _amount amount to withdraw
|
|
222
|
+
function withdrawFeeFromUlnV2(address _lib, address payable _to, uint256 _amount) external onlyRole(ADMIN_ROLE) {
|
|
223
|
+
if (!hasRole(MESSAGE_LIB_ROLE, _lib)) {
|
|
224
|
+
revert OnlyMessageLib();
|
|
225
|
+
}
|
|
226
|
+
ILayerZeroUltraLightNodeV2(_lib).withdrawNative(_to, _amount);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ========================= OnlyMessageLib =========================
|
|
230
|
+
|
|
231
|
+
/// @dev for ULN301, ULN302 and more to assign job
|
|
232
|
+
/// @dev dvn network can reject job from _sender by adding/removing them from allowlist/denylist
|
|
233
|
+
/// @param _param assign job param
|
|
234
|
+
/// @param _options dvn options
|
|
235
|
+
function assignJob(
|
|
236
|
+
AssignJobParam calldata _param,
|
|
237
|
+
bytes calldata _options
|
|
238
|
+
) external payable onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_param.sender) returns (uint256 totalFee) {
|
|
239
|
+
IDVNFeeLib.FeeParams memory feeParams = IDVNFeeLib.FeeParams(
|
|
240
|
+
priceFeed,
|
|
241
|
+
_param.dstEid,
|
|
242
|
+
_param.confirmations,
|
|
243
|
+
_param.sender,
|
|
244
|
+
quorum,
|
|
245
|
+
defaultMultiplierBps
|
|
246
|
+
);
|
|
247
|
+
totalFee = IDVNFeeLib(workerFeeLib).getFeeOnSend(feeParams, dstConfig[_param.dstEid], _options);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// @dev to support ULNv2
|
|
251
|
+
/// @dev dvn network can reject job from _sender by adding/removing them from allowlist/denylist
|
|
252
|
+
/// @param _dstEid destination EndpointId
|
|
253
|
+
/// @param //_outboundProofType outbound proof type
|
|
254
|
+
/// @param _confirmations block confirmations
|
|
255
|
+
/// @param _sender message sender address
|
|
256
|
+
function assignJob(
|
|
257
|
+
uint16 _dstEid,
|
|
258
|
+
uint16 /*_outboundProofType*/,
|
|
259
|
+
uint64 _confirmations,
|
|
260
|
+
address _sender
|
|
261
|
+
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) returns (uint256 totalFee) {
|
|
262
|
+
IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
|
|
263
|
+
priceFeed,
|
|
264
|
+
_dstEid,
|
|
265
|
+
_confirmations,
|
|
266
|
+
_sender,
|
|
267
|
+
quorum,
|
|
268
|
+
defaultMultiplierBps
|
|
269
|
+
);
|
|
270
|
+
// ULNV2 does not have dvn options
|
|
271
|
+
totalFee = IDVNFeeLib(workerFeeLib).getFeeOnSend(params, dstConfig[_dstEid], bytes(""));
|
|
272
|
+
emit VerifierFeePaid(totalFee);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ========================= View =========================
|
|
276
|
+
|
|
277
|
+
/// @dev getFee can revert if _sender doesn't pass ACL
|
|
278
|
+
/// @param _dstEid destination EndpointId
|
|
279
|
+
/// @param _confirmations block confirmations
|
|
280
|
+
/// @param _sender message sender address
|
|
281
|
+
/// @param _options dvn options
|
|
282
|
+
/// @return fee fee in native amount
|
|
283
|
+
function getFee(
|
|
284
|
+
uint32 _dstEid,
|
|
285
|
+
uint64 _confirmations,
|
|
286
|
+
address _sender,
|
|
287
|
+
bytes calldata _options
|
|
288
|
+
) external view onlyAcl(_sender) returns (uint256 fee) {
|
|
289
|
+
IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
|
|
290
|
+
priceFeed,
|
|
291
|
+
_dstEid,
|
|
292
|
+
_confirmations,
|
|
293
|
+
_sender,
|
|
294
|
+
quorum,
|
|
295
|
+
defaultMultiplierBps
|
|
296
|
+
);
|
|
297
|
+
return IDVNFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], _options);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// @dev to support ULNv2
|
|
301
|
+
/// @dev getFee can revert if _sender doesn't pass ACL
|
|
302
|
+
/// @param _dstEid destination EndpointId
|
|
303
|
+
/// @param //_outboundProofType outbound proof type
|
|
304
|
+
/// @param _confirmations block confirmations
|
|
305
|
+
/// @param _sender message sender address
|
|
306
|
+
function getFee(
|
|
307
|
+
uint16 _dstEid,
|
|
308
|
+
uint16 /*_outboundProofType*/,
|
|
309
|
+
uint64 _confirmations,
|
|
310
|
+
address _sender
|
|
311
|
+
) public view onlyAcl(_sender) returns (uint256 fee) {
|
|
312
|
+
IDVNFeeLib.FeeParams memory params = IDVNFeeLib.FeeParams(
|
|
313
|
+
priceFeed,
|
|
314
|
+
_dstEid,
|
|
315
|
+
_confirmations,
|
|
316
|
+
_sender,
|
|
317
|
+
quorum,
|
|
318
|
+
defaultMultiplierBps
|
|
319
|
+
);
|
|
320
|
+
return IDVNFeeLib(workerFeeLib).getFee(params, dstConfig[_dstEid], bytes(""));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/// @param _target target address
|
|
324
|
+
/// @param _callData call data
|
|
325
|
+
/// @param _expiration expiration timestamp
|
|
326
|
+
/// @return hash of above
|
|
327
|
+
function hashCallData(
|
|
328
|
+
uint32 _vid,
|
|
329
|
+
address _target,
|
|
330
|
+
bytes calldata _callData,
|
|
331
|
+
uint256 _expiration
|
|
332
|
+
) public pure returns (bytes32) {
|
|
333
|
+
return keccak256(abi.encodePacked(_vid, _target, _expiration, _callData));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ========================= Internal =========================
|
|
337
|
+
|
|
338
|
+
/// @dev to save gas, we don't check hash for some functions (where replaying won't change the state)
|
|
339
|
+
/// @dev for example, some administrative functions like changing signers, the contract should check hash to double spending
|
|
340
|
+
/// @dev should ensure that all onlySelf functions have unique functionSig
|
|
341
|
+
/// @param _functionSig function signature
|
|
342
|
+
/// @return true if should check hash
|
|
343
|
+
function _shouldCheckHash(bytes4 _functionSig) internal pure returns (bool) {
|
|
344
|
+
// never check for these selectors to save gas
|
|
345
|
+
return
|
|
346
|
+
_functionSig != IReceiveUlnE2.verify.selector && // 0x0223536e, replaying won't change the state
|
|
347
|
+
_functionSig != ILayerZeroUltraLightNodeV2.updateHash.selector; // 0x704316e5, replaying will be revert at uln
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity 0.8.22;
|
|
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
|
+
|
|
13
|
+
contract DVNFeeLib is Ownable, IDVNFeeLib {
|
|
14
|
+
using DVNOptions for bytes;
|
|
15
|
+
|
|
16
|
+
uint16 internal constant EXECUTE_FIXED_BYTES = 68; // encoded: funcSigHash + params -> 4 + (32 * 2)
|
|
17
|
+
uint16 internal constant SIGNATURE_RAW_BYTES = 65; // not encoded
|
|
18
|
+
// callData(updateHash) = 132 (4 + 32 * 4), padded to 32 = 160 and encoded as bytes with an 64 byte overhead = 224
|
|
19
|
+
uint16 internal constant UPDATE_HASH_BYTES = 224;
|
|
20
|
+
|
|
21
|
+
uint256 private immutable nativeDecimalsRate;
|
|
22
|
+
|
|
23
|
+
constructor(uint256 _nativeDecimalsRate) {
|
|
24
|
+
nativeDecimalsRate = _nativeDecimalsRate;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ================================ OnlyOwner ================================
|
|
28
|
+
function withdrawToken(address _token, address _to, uint256 _amount) external onlyOwner {
|
|
29
|
+
// transfers native if _token is address(0x0)
|
|
30
|
+
Transfer.nativeOrToken(_token, _to, _amount);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ========================= External =========================
|
|
34
|
+
/// @dev get fee function that can change state. e.g. paying priceFeed
|
|
35
|
+
/// @param _params fee params
|
|
36
|
+
/// @param _dstConfig dst config
|
|
37
|
+
/// @param //_options options
|
|
38
|
+
function getFeeOnSend(
|
|
39
|
+
FeeParams calldata _params,
|
|
40
|
+
IDVN.DstConfig calldata _dstConfig,
|
|
41
|
+
bytes calldata _options
|
|
42
|
+
) external payable returns (uint256) {
|
|
43
|
+
_decodeDVNOptions(_options); // todo: validate options
|
|
44
|
+
|
|
45
|
+
uint256 callDataSize = _getCallDataSize(_params.quorum);
|
|
46
|
+
|
|
47
|
+
// for future versions where priceFeed charges a fee
|
|
48
|
+
// uint256 priceFeedFee = ILayerZeroPriceFeed(_params.priceFeed).getFee(_params.dstEid, callDataSize, _dstConfig.gas);
|
|
49
|
+
// (uint256 fee, , , uint128 nativePriceUSD) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeOnSend{
|
|
50
|
+
// value: priceFeedFee
|
|
51
|
+
// }(_params.dstEid, callDataSize, _dstConfig.gas);
|
|
52
|
+
|
|
53
|
+
(uint256 fee, , , uint128 nativePriceUSD) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeOnSend(
|
|
54
|
+
_params.dstEid,
|
|
55
|
+
callDataSize,
|
|
56
|
+
_dstConfig.gas
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return
|
|
60
|
+
_applyPremium(
|
|
61
|
+
fee,
|
|
62
|
+
_dstConfig.multiplierBps,
|
|
63
|
+
_params.defaultMultiplierBps,
|
|
64
|
+
_dstConfig.floorMarginUSD,
|
|
65
|
+
nativePriceUSD
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ========================= View =========================
|
|
70
|
+
/// @dev get fee view function
|
|
71
|
+
/// @param _params fee params
|
|
72
|
+
/// @param _dstConfig dst config
|
|
73
|
+
/// @param //_options options
|
|
74
|
+
function getFee(
|
|
75
|
+
FeeParams calldata _params,
|
|
76
|
+
IDVN.DstConfig calldata _dstConfig,
|
|
77
|
+
bytes calldata _options
|
|
78
|
+
) external view returns (uint256) {
|
|
79
|
+
_decodeDVNOptions(_options); // validate options
|
|
80
|
+
|
|
81
|
+
uint256 callDataSize = _getCallDataSize(_params.quorum);
|
|
82
|
+
(uint256 fee, , , uint128 nativePriceUSD) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(
|
|
83
|
+
_params.dstEid,
|
|
84
|
+
callDataSize,
|
|
85
|
+
_dstConfig.gas
|
|
86
|
+
);
|
|
87
|
+
return
|
|
88
|
+
_applyPremium(
|
|
89
|
+
fee,
|
|
90
|
+
_dstConfig.multiplierBps,
|
|
91
|
+
_params.defaultMultiplierBps,
|
|
92
|
+
_dstConfig.floorMarginUSD,
|
|
93
|
+
nativePriceUSD
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ========================= Internal =========================
|
|
98
|
+
function _getCallDataSize(uint256 _quorum) internal pure returns (uint256) {
|
|
99
|
+
uint256 totalSignatureBytes = _quorum * SIGNATURE_RAW_BYTES;
|
|
100
|
+
if (totalSignatureBytes % 32 != 0) {
|
|
101
|
+
totalSignatureBytes = totalSignatureBytes - (totalSignatureBytes % 32) + 32;
|
|
102
|
+
}
|
|
103
|
+
// getFee should charge on execute(updateHash)
|
|
104
|
+
// totalSignatureBytesPadded also has 64 overhead for bytes
|
|
105
|
+
return uint256(EXECUTE_FIXED_BYTES) + UPDATE_HASH_BYTES + totalSignatureBytes + 64;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function _applyPremium(
|
|
109
|
+
uint256 _fee,
|
|
110
|
+
uint16 _bps,
|
|
111
|
+
uint16 _defaultBps,
|
|
112
|
+
uint128 _marginUSD,
|
|
113
|
+
uint128 _nativePriceUSD
|
|
114
|
+
) internal view returns (uint256) {
|
|
115
|
+
uint16 multiplierBps = _bps == 0 ? _defaultBps : _bps;
|
|
116
|
+
|
|
117
|
+
uint256 feeWithMultiplier = (_fee * multiplierBps) / 10000;
|
|
118
|
+
if (_nativePriceUSD == 0 || _marginUSD == 0) {
|
|
119
|
+
return feeWithMultiplier;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
uint256 feeWithFloorMargin = _fee + (_marginUSD * nativeDecimalsRate) / _nativePriceUSD;
|
|
123
|
+
|
|
124
|
+
return feeWithFloorMargin > feeWithMultiplier ? feeWithFloorMargin : feeWithMultiplier;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function _decodeDVNOptions(bytes calldata _options) internal pure returns (uint256) {
|
|
128
|
+
uint256 cursor;
|
|
129
|
+
while (cursor < _options.length) {
|
|
130
|
+
(uint8 optionType, , uint256 newCursor) = _options.nextDVNOption(cursor);
|
|
131
|
+
cursor = newCursor;
|
|
132
|
+
revert UnsupportedOptionType(optionType);
|
|
133
|
+
}
|
|
134
|
+
if (cursor != _options.length) revert DVNOptions.InvalidDVNOptions(cursor);
|
|
135
|
+
|
|
136
|
+
return 0; // todo: precrime fee model
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// send funds here to pay for price feed directly
|
|
140
|
+
receive() external payable {}
|
|
141
|
+
}
|