@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,176 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
6
|
+
|
|
7
|
+
import { ExecutorOptions } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol";
|
|
8
|
+
|
|
9
|
+
import { DVNOptions } from "./DVNOptions.sol";
|
|
10
|
+
|
|
11
|
+
library UlnOptions {
|
|
12
|
+
using SafeCast for uint256;
|
|
13
|
+
|
|
14
|
+
uint16 internal constant TYPE_1 = 1; // legacy options type 1
|
|
15
|
+
uint16 internal constant TYPE_2 = 2; // legacy options type 2
|
|
16
|
+
uint16 internal constant TYPE_3 = 3;
|
|
17
|
+
|
|
18
|
+
error LZ_ULN_InvalidWorkerOptions(uint256 cursor);
|
|
19
|
+
error LZ_ULN_InvalidWorkerId(uint8 workerId);
|
|
20
|
+
error LZ_ULN_InvalidLegacyType1Option();
|
|
21
|
+
error LZ_ULN_InvalidLegacyType2Option();
|
|
22
|
+
error LZ_ULN_UnsupportedOptionType(uint16 optionType);
|
|
23
|
+
|
|
24
|
+
/// @dev decode the options into executorOptions and dvnOptions
|
|
25
|
+
/// @param _options the options can be either legacy options (type 1 or 2) or type 3 options
|
|
26
|
+
/// @return executorOptions the executor options, share the same format of type 3 options
|
|
27
|
+
/// @return dvnOptions the dvn options, share the same format of type 3 options
|
|
28
|
+
function decode(
|
|
29
|
+
bytes calldata _options
|
|
30
|
+
) internal pure returns (bytes memory executorOptions, bytes memory dvnOptions) {
|
|
31
|
+
// at least 2 bytes for the option type, but can have no options
|
|
32
|
+
if (_options.length < 2) revert LZ_ULN_InvalidWorkerOptions(0);
|
|
33
|
+
|
|
34
|
+
uint16 optionsType = uint16(bytes2(_options[0:2]));
|
|
35
|
+
uint256 cursor = 2;
|
|
36
|
+
|
|
37
|
+
// type3 options: [worker_option][worker_option]...
|
|
38
|
+
// worker_option: [worker_id][option_size][option]
|
|
39
|
+
// worker_id: uint8, option_size: uint16, option: bytes
|
|
40
|
+
if (optionsType == TYPE_3) {
|
|
41
|
+
unchecked {
|
|
42
|
+
uint256 start = cursor;
|
|
43
|
+
uint8 lastWorkerId; // worker_id starts from 1, so 0 is an invalid worker_id
|
|
44
|
+
|
|
45
|
+
// heuristic: we assume that the options are mostly EXECUTOR options only
|
|
46
|
+
// checking the workerID can reduce gas usage for most cases
|
|
47
|
+
while (cursor < _options.length) {
|
|
48
|
+
uint8 workerId = uint8(bytes1(_options[cursor:cursor + 1]));
|
|
49
|
+
if (workerId == 0) revert LZ_ULN_InvalidWorkerId(0);
|
|
50
|
+
|
|
51
|
+
// workerId must equal to the lastWorkerId for the first option
|
|
52
|
+
// so it is always skipped in the first option
|
|
53
|
+
// this operation slices out options whenever the the scan finds a different workerId
|
|
54
|
+
if (lastWorkerId == 0) {
|
|
55
|
+
lastWorkerId = workerId;
|
|
56
|
+
} else if (workerId != lastWorkerId) {
|
|
57
|
+
bytes calldata op = _options[start:cursor]; // slice out the last worker's options
|
|
58
|
+
(executorOptions, dvnOptions) = _insertWorkerOptions(
|
|
59
|
+
executorOptions,
|
|
60
|
+
dvnOptions,
|
|
61
|
+
lastWorkerId,
|
|
62
|
+
op
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// reset the start cursor and lastWorkerId
|
|
66
|
+
start = cursor;
|
|
67
|
+
lastWorkerId = workerId;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
++cursor; // for workerId
|
|
71
|
+
|
|
72
|
+
uint16 size = uint16(bytes2(_options[cursor:cursor + 2]));
|
|
73
|
+
if (size == 0) revert LZ_ULN_InvalidWorkerOptions(cursor);
|
|
74
|
+
cursor += size + 2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// the options length must be the same as the cursor at the end
|
|
78
|
+
if (cursor != _options.length) revert LZ_ULN_InvalidWorkerOptions(cursor);
|
|
79
|
+
|
|
80
|
+
// if we have reached the end of the options and the options are not empty
|
|
81
|
+
// we need to process the last worker's options
|
|
82
|
+
if (_options.length > 2) {
|
|
83
|
+
bytes calldata op = _options[start:cursor];
|
|
84
|
+
(executorOptions, dvnOptions) = _insertWorkerOptions(executorOptions, dvnOptions, lastWorkerId, op);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
executorOptions = decodeLegacyOptions(optionsType, _options);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _insertWorkerOptions(
|
|
93
|
+
bytes memory _executorOptions,
|
|
94
|
+
bytes memory _dvnOptions,
|
|
95
|
+
uint8 _workerId,
|
|
96
|
+
bytes calldata _newOptions
|
|
97
|
+
) private pure returns (bytes memory, bytes memory) {
|
|
98
|
+
if (_workerId == ExecutorOptions.WORKER_ID) {
|
|
99
|
+
_executorOptions = _executorOptions.length == 0
|
|
100
|
+
? _newOptions
|
|
101
|
+
: abi.encodePacked(_executorOptions, _newOptions);
|
|
102
|
+
} else if (_workerId == DVNOptions.WORKER_ID) {
|
|
103
|
+
_dvnOptions = _dvnOptions.length == 0 ? _newOptions : abi.encodePacked(_dvnOptions, _newOptions);
|
|
104
|
+
} else {
|
|
105
|
+
revert LZ_ULN_InvalidWorkerId(_workerId);
|
|
106
|
+
}
|
|
107
|
+
return (_executorOptions, _dvnOptions);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// @dev decode the legacy options (type 1 or 2) into executorOptions
|
|
111
|
+
/// @param _optionType the legacy option type
|
|
112
|
+
/// @param _options the legacy options, which still has the option type in the first 2 bytes
|
|
113
|
+
/// @return executorOptions the executor options, share the same format of type 3 options
|
|
114
|
+
/// Data format:
|
|
115
|
+
/// legacy type 1: [extraGas]
|
|
116
|
+
/// legacy type 2: [extraGas][dstNativeAmt][dstNativeAddress]
|
|
117
|
+
/// extraGas: uint256, dstNativeAmt: uint256, dstNativeAddress: bytes
|
|
118
|
+
function decodeLegacyOptions(
|
|
119
|
+
uint16 _optionType,
|
|
120
|
+
bytes calldata _options
|
|
121
|
+
) internal pure returns (bytes memory executorOptions) {
|
|
122
|
+
if (_optionType == TYPE_1) {
|
|
123
|
+
if (_options.length != 34) revert LZ_ULN_InvalidLegacyType1Option();
|
|
124
|
+
|
|
125
|
+
// execution gas
|
|
126
|
+
uint128 executionGas = uint256(bytes32(_options[2:2 + 32])).toUint128();
|
|
127
|
+
|
|
128
|
+
// dont use the encode function in the ExecutorOptions lib for saving gas by calling abi.encodePacked once
|
|
129
|
+
// the result is a lzReceive option: [executor_id][option_size][option_type][execution_gas]
|
|
130
|
+
// option_type: uint8, execution_gas: uint128
|
|
131
|
+
// option_size = len(option_type) + len(execution_gas) = 1 + 16 = 17
|
|
132
|
+
executorOptions = abi.encodePacked(
|
|
133
|
+
ExecutorOptions.WORKER_ID,
|
|
134
|
+
uint16(17), // 16 + 1, 16 for option_length, + 1 for option_type
|
|
135
|
+
ExecutorOptions.OPTION_TYPE_LZRECEIVE,
|
|
136
|
+
executionGas
|
|
137
|
+
);
|
|
138
|
+
} else if (_optionType == TYPE_2) {
|
|
139
|
+
// receiver size <= 32
|
|
140
|
+
if (_options.length <= 66 || _options.length > 98) revert LZ_ULN_InvalidLegacyType2Option();
|
|
141
|
+
|
|
142
|
+
// execution gas
|
|
143
|
+
uint128 executionGas = uint256(bytes32(_options[2:2 + 32])).toUint128();
|
|
144
|
+
|
|
145
|
+
// nativeDrop (amount + receiver)
|
|
146
|
+
uint128 amount = uint256(bytes32(_options[34:34 + 32])).toUint128(); // offset 2 + 32
|
|
147
|
+
bytes32 receiver;
|
|
148
|
+
unchecked {
|
|
149
|
+
uint256 receiverLen = _options.length - 66; // offset 2 + 32 + 32
|
|
150
|
+
receiver = bytes32(_options[66:]);
|
|
151
|
+
receiver = receiver >> (8 * (32 - receiverLen)); // padding 0 to the left
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// dont use the encode function in the ExecutorOptions lib for saving gas by calling abi.encodePacked once
|
|
155
|
+
// the result has one lzReceive option and one nativeDrop option:
|
|
156
|
+
// [executor_id][lzReceive_option_size][option_type][execution_gas] +
|
|
157
|
+
// [executor_id][nativeDrop_option_size][option_type][nativeDrop_amount][receiver]
|
|
158
|
+
// option_type: uint8, execution_gas: uint128, nativeDrop_amount: uint128, receiver: bytes32
|
|
159
|
+
// lzReceive_option_size = len(option_type) + len(execution_gas) = 1 + 16 = 17
|
|
160
|
+
// nativeDrop_option_size = len(option_type) + len(nativeDrop_amount) + len(receiver) = 1 + 16 + 32 = 49
|
|
161
|
+
executorOptions = abi.encodePacked(
|
|
162
|
+
ExecutorOptions.WORKER_ID,
|
|
163
|
+
uint16(17), // 16 + 1, 16 for option_length, + 1 for option_type
|
|
164
|
+
ExecutorOptions.OPTION_TYPE_LZRECEIVE,
|
|
165
|
+
executionGas,
|
|
166
|
+
ExecutorOptions.WORKER_ID,
|
|
167
|
+
uint16(49), // 48 + 1, 32 + 16 for option_length, + 1 for option_type
|
|
168
|
+
ExecutorOptions.OPTION_TYPE_NATIVE_DROP,
|
|
169
|
+
amount,
|
|
170
|
+
receiver
|
|
171
|
+
);
|
|
172
|
+
} else {
|
|
173
|
+
revert LZ_ULN_UnsupportedOptionType(_optionType);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { ERC165, IERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
6
|
+
|
|
7
|
+
import { ILayerZeroEndpointV2, MessagingFee, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
|
|
8
|
+
import { IMessageLib, MessageLibType, SetConfigParam } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/IMessageLib.sol";
|
|
9
|
+
import { ISendLib, Packet } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol";
|
|
10
|
+
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
|
|
11
|
+
import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
|
|
12
|
+
import { AddressCast } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/AddressCast.sol";
|
|
13
|
+
|
|
14
|
+
import { ILayerZeroReadExecutor } from "../../interfaces/ILayerZeroReadExecutor.sol";
|
|
15
|
+
import { ILayerZeroReadDVN } from "../interfaces/ILayerZeroReadDVN.sol";
|
|
16
|
+
import { ILayerZeroTreasury } from "../../interfaces/ILayerZeroTreasury.sol";
|
|
17
|
+
|
|
18
|
+
import { UlnOptions } from "../libs/UlnOptions.sol";
|
|
19
|
+
import { DVNOptions } from "../libs/DVNOptions.sol";
|
|
20
|
+
import { SafeCall } from "../../libs/SafeCall.sol";
|
|
21
|
+
|
|
22
|
+
import { MessageLibBase } from "../../MessageLibBase.sol";
|
|
23
|
+
import { ReadLibBase, ReadLibConfig } from "./ReadLibBase.sol";
|
|
24
|
+
|
|
25
|
+
contract ReadLib1002 is ISendLib, ERC165, ReadLibBase, MessageLibBase {
|
|
26
|
+
using PacketV1Codec for bytes;
|
|
27
|
+
using SafeCall for address;
|
|
28
|
+
|
|
29
|
+
uint32 internal constant CONFIG_TYPE_READ_LID_CONFIG = 1;
|
|
30
|
+
|
|
31
|
+
uint16 internal constant TREASURY_MAX_COPY = 32;
|
|
32
|
+
|
|
33
|
+
uint256 internal immutable treasuryGasLimit;
|
|
34
|
+
|
|
35
|
+
mapping(address oapp => mapping(uint32 eid => mapping(uint64 nonce => bytes32 cmdHash))) public cmdHashLookup;
|
|
36
|
+
mapping(bytes32 headerHash => mapping(bytes32 cmdHash => mapping(address dvn => bytes32 payloadHash)))
|
|
37
|
+
public hashLookup;
|
|
38
|
+
|
|
39
|
+
// accumulated fees for workers and treasury
|
|
40
|
+
mapping(address worker => uint256 fee) public fees;
|
|
41
|
+
uint256 internal treasuryNativeFeeCap;
|
|
42
|
+
address internal treasury;
|
|
43
|
+
|
|
44
|
+
event PayloadVerified(address dvn, bytes header, bytes32 cmdHash, bytes32 payloadHash);
|
|
45
|
+
event ExecutorFeePaid(address executor, uint256 fee);
|
|
46
|
+
event DVNFeePaid(address[] requiredDVNs, address[] optionalDVNs, uint256[] fees);
|
|
47
|
+
event NativeFeeWithdrawn(address worker, address receiver, uint256 amount);
|
|
48
|
+
event LzTokenFeeWithdrawn(address lzToken, address receiver, uint256 amount);
|
|
49
|
+
event TreasurySet(address treasury);
|
|
50
|
+
event TreasuryNativeFeeCapSet(uint256 newTreasuryNativeFeeCap);
|
|
51
|
+
|
|
52
|
+
error LZ_RL_InvalidReceiver();
|
|
53
|
+
error LZ_RL_InvalidPacketHeader();
|
|
54
|
+
error LZ_RL_InvalidCmdHash();
|
|
55
|
+
error LZ_RL_InvalidPacketVersion();
|
|
56
|
+
error LZ_RL_InvalidEid();
|
|
57
|
+
error LZ_RL_Verifying();
|
|
58
|
+
error LZ_RL_InvalidConfigType(uint32 configType);
|
|
59
|
+
error LZ_RL_InvalidAmount(uint256 requested, uint256 available);
|
|
60
|
+
error LZ_RL_NotTreasury();
|
|
61
|
+
error LZ_RL_CannotWithdrawAltToken();
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
address _endpoint,
|
|
65
|
+
uint256 _treasuryGasLimit,
|
|
66
|
+
uint256 _treasuryGasForFeeCap
|
|
67
|
+
) MessageLibBase(_endpoint, ILayerZeroEndpointV2(_endpoint).eid()) {
|
|
68
|
+
treasuryGasLimit = _treasuryGasLimit;
|
|
69
|
+
treasuryNativeFeeCap = _treasuryGasForFeeCap;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function supportsInterface(bytes4 _interfaceId) public view override(ERC165, IERC165) returns (bool) {
|
|
73
|
+
return
|
|
74
|
+
_interfaceId == type(IMessageLib).interfaceId ||
|
|
75
|
+
_interfaceId == type(ISendLib).interfaceId ||
|
|
76
|
+
super.supportsInterface(_interfaceId);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================ OnlyOwner ===================================
|
|
80
|
+
|
|
81
|
+
function setTreasury(address _treasury) external onlyOwner {
|
|
82
|
+
treasury = _treasury;
|
|
83
|
+
emit TreasurySet(_treasury);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// @dev the new value can not be greater than the old value, i.e. down only
|
|
87
|
+
function setTreasuryNativeFeeCap(uint256 _newTreasuryNativeFeeCap) external onlyOwner {
|
|
88
|
+
// assert the new value is no greater than the old value
|
|
89
|
+
if (_newTreasuryNativeFeeCap > treasuryNativeFeeCap)
|
|
90
|
+
revert LZ_RL_InvalidAmount(_newTreasuryNativeFeeCap, treasuryNativeFeeCap);
|
|
91
|
+
treasuryNativeFeeCap = _newTreasuryNativeFeeCap;
|
|
92
|
+
emit TreasuryNativeFeeCapSet(_newTreasuryNativeFeeCap);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ============================ OnlyEndpoint ===================================
|
|
96
|
+
|
|
97
|
+
function send(
|
|
98
|
+
Packet calldata _packet,
|
|
99
|
+
bytes calldata _options,
|
|
100
|
+
bool _payInLzToken
|
|
101
|
+
) external onlyEndpoint returns (MessagingFee memory, bytes memory) {
|
|
102
|
+
// the receiver must be the same as the sender
|
|
103
|
+
if (AddressCast.toBytes32(_packet.sender) != _packet.receiver) revert LZ_RL_InvalidReceiver();
|
|
104
|
+
|
|
105
|
+
// pay worker and treasury
|
|
106
|
+
(bytes memory encodedPacket, uint256 totalNativeFee) = _payWorkers(_packet, _options);
|
|
107
|
+
(uint256 treasuryNativeFee, uint256 lzTokenFee) = _payTreasury(
|
|
108
|
+
_packet.sender,
|
|
109
|
+
_packet.dstEid,
|
|
110
|
+
totalNativeFee,
|
|
111
|
+
_payInLzToken
|
|
112
|
+
);
|
|
113
|
+
totalNativeFee += treasuryNativeFee;
|
|
114
|
+
|
|
115
|
+
// store the cmdHash for verification in order to prevent reorg attack
|
|
116
|
+
cmdHashLookup[_packet.sender][_packet.dstEid][_packet.nonce] = keccak256(_packet.message);
|
|
117
|
+
|
|
118
|
+
return (MessagingFee(totalNativeFee, lzTokenFee), encodedPacket);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function setConfig(address _oapp, SetConfigParam[] calldata _params) external onlyEndpoint {
|
|
122
|
+
for (uint256 i = 0; i < _params.length; i++) {
|
|
123
|
+
SetConfigParam calldata param = _params[i];
|
|
124
|
+
_assertSupportedEid(param.eid);
|
|
125
|
+
if (param.configType == CONFIG_TYPE_READ_LID_CONFIG) {
|
|
126
|
+
_setReadLibConfig(param.eid, _oapp, abi.decode(param.config, (ReadLibConfig)));
|
|
127
|
+
} else {
|
|
128
|
+
revert LZ_RL_InvalidConfigType(param.configType);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================ External ===================================
|
|
134
|
+
/// @dev The verification will be done in the same chain where the packet is sent.
|
|
135
|
+
/// @dev dont need to check endpoint verifiable here to save gas, as it will reverts if not verifiable.
|
|
136
|
+
/// @param _packetHeader - the srcEid should be the localEid and the dstEid should be the channel id.
|
|
137
|
+
/// The original packet header in PacketSent event should be processed to flip the srcEid and dstEid.
|
|
138
|
+
function commitVerification(bytes calldata _packetHeader, bytes32 _cmdHash, bytes32 _payloadHash) external {
|
|
139
|
+
// assert packet header is of right size 81
|
|
140
|
+
if (_packetHeader.length != 81) revert LZ_RL_InvalidPacketHeader();
|
|
141
|
+
// assert packet header version is the same
|
|
142
|
+
if (_packetHeader.version() != PacketV1Codec.PACKET_VERSION) revert LZ_RL_InvalidPacketVersion();
|
|
143
|
+
// assert the packet is for this endpoint
|
|
144
|
+
if (_packetHeader.dstEid() != localEid) revert LZ_RL_InvalidEid();
|
|
145
|
+
|
|
146
|
+
// cache these values to save gas
|
|
147
|
+
address receiver = _packetHeader.receiverB20();
|
|
148
|
+
uint32 srcEid = _packetHeader.srcEid(); // channel id
|
|
149
|
+
uint64 nonce = _packetHeader.nonce();
|
|
150
|
+
|
|
151
|
+
// reorg protection. to allow reverification, the cmdHash cant be removed
|
|
152
|
+
if (cmdHashLookup[receiver][srcEid][nonce] != _cmdHash) revert LZ_RL_InvalidCmdHash();
|
|
153
|
+
|
|
154
|
+
ReadLibConfig memory config = getReadLibConfig(receiver, srcEid);
|
|
155
|
+
_verifyAndReclaimStorage(config, keccak256(_packetHeader), _cmdHash, _payloadHash);
|
|
156
|
+
|
|
157
|
+
// endpoint will revert if nonce <= lazyInboundNonce
|
|
158
|
+
Origin memory origin = Origin(srcEid, _packetHeader.sender(), nonce);
|
|
159
|
+
ILayerZeroEndpointV2(endpoint).verify(origin, receiver, _payloadHash);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// @dev DVN verifies the payload with the packet header and command hash
|
|
163
|
+
/// @param _packetHeader - the packet header is needed for event only, which can be conveniently for off-chain to track the packet state.
|
|
164
|
+
function verify(bytes calldata _packetHeader, bytes32 _cmdHash, bytes32 _payloadHash) external {
|
|
165
|
+
hashLookup[keccak256(_packetHeader)][_cmdHash][msg.sender] = _payloadHash;
|
|
166
|
+
emit PayloadVerified(msg.sender, _packetHeader, _cmdHash, _payloadHash);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function withdrawFee(address _to, uint256 _amount) external {
|
|
170
|
+
uint256 fee = fees[msg.sender];
|
|
171
|
+
if (_amount > fee) revert LZ_RL_InvalidAmount(_amount, fee);
|
|
172
|
+
unchecked {
|
|
173
|
+
fees[msg.sender] = fee - _amount;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// transfers native if nativeToken == address(0x0)
|
|
177
|
+
address nativeToken = ILayerZeroEndpointV2(endpoint).nativeToken();
|
|
178
|
+
Transfer.nativeOrToken(nativeToken, _to, _amount);
|
|
179
|
+
emit NativeFeeWithdrawn(msg.sender, _to, _amount);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ============================ Treasury ===================================
|
|
183
|
+
|
|
184
|
+
/// @dev _lzToken is a user-supplied value because lzToken might change in the endpoint before all lzToken can be taken out
|
|
185
|
+
function withdrawLzTokenFee(address _lzToken, address _to, uint256 _amount) external {
|
|
186
|
+
if (msg.sender != treasury) revert LZ_RL_NotTreasury();
|
|
187
|
+
|
|
188
|
+
// lz token cannot be the same as the native token
|
|
189
|
+
if (ILayerZeroEndpointV2(endpoint).nativeToken() == _lzToken) revert LZ_RL_CannotWithdrawAltToken();
|
|
190
|
+
|
|
191
|
+
Transfer.token(_lzToken, _to, _amount);
|
|
192
|
+
|
|
193
|
+
emit LzTokenFeeWithdrawn(_lzToken, _to, _amount);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ============================ View ===================================
|
|
197
|
+
|
|
198
|
+
function quote(
|
|
199
|
+
Packet calldata _packet,
|
|
200
|
+
bytes calldata _options,
|
|
201
|
+
bool _payInLzToken
|
|
202
|
+
) external view returns (MessagingFee memory) {
|
|
203
|
+
// split workers options
|
|
204
|
+
(bytes memory executorOptions, bytes memory dvnOptions) = UlnOptions.decode(_options);
|
|
205
|
+
|
|
206
|
+
address sender = _packet.sender;
|
|
207
|
+
uint32 dstEid = _packet.dstEid;
|
|
208
|
+
|
|
209
|
+
// quote the executor and dvns
|
|
210
|
+
ReadLibConfig memory config = getReadLibConfig(sender, dstEid);
|
|
211
|
+
uint256 nativeFee = _quoteDVNs(
|
|
212
|
+
config,
|
|
213
|
+
sender,
|
|
214
|
+
PacketV1Codec.encodePacketHeader(_packet),
|
|
215
|
+
_packet.message,
|
|
216
|
+
dvnOptions
|
|
217
|
+
);
|
|
218
|
+
nativeFee += ILayerZeroReadExecutor(config.executor).getFee(sender, executorOptions);
|
|
219
|
+
|
|
220
|
+
// quote treasury
|
|
221
|
+
(uint256 treasuryNativeFee, uint256 lzTokenFee) = _quoteTreasury(sender, dstEid, nativeFee, _payInLzToken);
|
|
222
|
+
nativeFee += treasuryNativeFee;
|
|
223
|
+
|
|
224
|
+
return MessagingFee(nativeFee, lzTokenFee);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function verifiable(
|
|
228
|
+
ReadLibConfig calldata _config,
|
|
229
|
+
bytes32 _headerHash,
|
|
230
|
+
bytes32 _cmdHash,
|
|
231
|
+
bytes32 _payloadHash
|
|
232
|
+
) external view returns (bool) {
|
|
233
|
+
return _checkVerifiable(_config, _headerHash, _cmdHash, _payloadHash);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function getConfig(uint32 _eid, address _oapp, uint32 _configType) external view returns (bytes memory) {
|
|
237
|
+
if (_configType == CONFIG_TYPE_READ_LID_CONFIG) {
|
|
238
|
+
return abi.encode(getReadLibConfig(_oapp, _eid));
|
|
239
|
+
} else {
|
|
240
|
+
revert LZ_RL_InvalidConfigType(_configType);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getTreasuryAndNativeFeeCap() external view returns (address, uint256) {
|
|
245
|
+
return (treasury, treasuryNativeFeeCap);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function isSupportedEid(uint32 _eid) external view returns (bool) {
|
|
249
|
+
return _isSupportedEid(_eid);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function messageLibType() external pure returns (MessageLibType) {
|
|
253
|
+
return MessageLibType.SendAndReceive;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function version() external pure returns (uint64 major, uint8 minor, uint8 endpointVersion) {
|
|
257
|
+
return (10, 0, 2);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ============================ Internal ===================================
|
|
261
|
+
|
|
262
|
+
/// 1/ handle executor
|
|
263
|
+
/// 2/ handle other workers
|
|
264
|
+
function _payWorkers(
|
|
265
|
+
Packet calldata _packet,
|
|
266
|
+
bytes calldata _options
|
|
267
|
+
) internal returns (bytes memory encodedPacket, uint256 totalNativeFee) {
|
|
268
|
+
// split workers options
|
|
269
|
+
(bytes memory executorOptions, bytes memory dvnOptions) = UlnOptions.decode(_options);
|
|
270
|
+
|
|
271
|
+
// handle executor
|
|
272
|
+
ReadLibConfig memory config = getReadLibConfig(_packet.sender, _packet.dstEid);
|
|
273
|
+
totalNativeFee = _payExecutor(config.executor, _packet.sender, executorOptions);
|
|
274
|
+
|
|
275
|
+
// handle dvns
|
|
276
|
+
(uint256 dvnFee, bytes memory packetBytes) = _payDVNs(config, _packet, dvnOptions);
|
|
277
|
+
totalNativeFee += dvnFee;
|
|
278
|
+
|
|
279
|
+
encodedPacket = packetBytes;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function _payDVNs(
|
|
283
|
+
ReadLibConfig memory _config,
|
|
284
|
+
Packet calldata _packet,
|
|
285
|
+
bytes memory _options
|
|
286
|
+
) internal returns (uint256 totalFee, bytes memory encodedPacket) {
|
|
287
|
+
bytes memory packetHeader = PacketV1Codec.encodePacketHeader(_packet);
|
|
288
|
+
bytes memory payload = PacketV1Codec.encodePayload(_packet);
|
|
289
|
+
|
|
290
|
+
uint256[] memory dvnFees;
|
|
291
|
+
(totalFee, dvnFees) = _assignDVNJobs(_config, _packet.sender, packetHeader, _packet.message, _options);
|
|
292
|
+
|
|
293
|
+
encodedPacket = abi.encodePacked(packetHeader, payload);
|
|
294
|
+
emit DVNFeePaid(_config.requiredDVNs, _config.optionalDVNs, dvnFees);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function _assignDVNJobs(
|
|
298
|
+
ReadLibConfig memory _config,
|
|
299
|
+
address _sender,
|
|
300
|
+
bytes memory _packetHeader,
|
|
301
|
+
bytes calldata _cmd,
|
|
302
|
+
bytes memory _options
|
|
303
|
+
) internal returns (uint256 totalFee, uint256[] memory dvnFees) {
|
|
304
|
+
(bytes[] memory optionsArray, uint8[] memory dvnIds) = DVNOptions.groupDVNOptionsByIdx(_options);
|
|
305
|
+
|
|
306
|
+
uint8 dvnsLength = _config.requiredDVNCount + _config.optionalDVNCount;
|
|
307
|
+
dvnFees = new uint256[](dvnsLength);
|
|
308
|
+
for (uint8 i = 0; i < dvnsLength; ++i) {
|
|
309
|
+
address dvn = i < _config.requiredDVNCount
|
|
310
|
+
? _config.requiredDVNs[i]
|
|
311
|
+
: _config.optionalDVNs[i - _config.requiredDVNCount];
|
|
312
|
+
|
|
313
|
+
bytes memory options = "";
|
|
314
|
+
for (uint256 j = 0; j < dvnIds.length; ++j) {
|
|
315
|
+
if (dvnIds[j] == i) {
|
|
316
|
+
options = optionsArray[j];
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
dvnFees[i] = ILayerZeroReadDVN(dvn).assignJob(_sender, _packetHeader, _cmd, options);
|
|
322
|
+
if (dvnFees[i] > 0) {
|
|
323
|
+
fees[dvn] += dvnFees[i];
|
|
324
|
+
totalFee += dvnFees[i];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function _quoteDVNs(
|
|
330
|
+
ReadLibConfig memory _config,
|
|
331
|
+
address _sender,
|
|
332
|
+
bytes memory _packetHeader,
|
|
333
|
+
bytes calldata _cmd,
|
|
334
|
+
bytes memory _options
|
|
335
|
+
) internal view returns (uint256 totalFee) {
|
|
336
|
+
(bytes[] memory optionsArray, uint8[] memory dvnIndices) = DVNOptions.groupDVNOptionsByIdx(_options);
|
|
337
|
+
|
|
338
|
+
// here we merge 2 list of dvns into 1 to allocate the indexed dvn options to the right dvn
|
|
339
|
+
uint8 dvnsLength = _config.requiredDVNCount + _config.optionalDVNCount;
|
|
340
|
+
for (uint8 i = 0; i < dvnsLength; ++i) {
|
|
341
|
+
address dvn = i < _config.requiredDVNCount
|
|
342
|
+
? _config.requiredDVNs[i]
|
|
343
|
+
: _config.optionalDVNs[i - _config.requiredDVNCount];
|
|
344
|
+
|
|
345
|
+
bytes memory options = "";
|
|
346
|
+
// it is a double loop here. however, if the list is short, the cost is very acceptable.
|
|
347
|
+
for (uint256 j = 0; j < dvnIndices.length; ++j) {
|
|
348
|
+
if (dvnIndices[j] == i) {
|
|
349
|
+
options = optionsArray[j];
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
totalFee += ILayerZeroReadDVN(dvn).getFee(_sender, _packetHeader, _cmd, options);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function _payTreasury(
|
|
358
|
+
address _sender,
|
|
359
|
+
uint32 _dstEid,
|
|
360
|
+
uint256 _totalNativeFee,
|
|
361
|
+
bool _payInLzToken
|
|
362
|
+
) internal returns (uint256 treasuryNativeFee, uint256 lzTokenFee) {
|
|
363
|
+
if (treasury != address(0x0)) {
|
|
364
|
+
bytes memory callData = abi.encodeCall(
|
|
365
|
+
ILayerZeroTreasury.payFee,
|
|
366
|
+
(_sender, _dstEid, _totalNativeFee, _payInLzToken)
|
|
367
|
+
);
|
|
368
|
+
(bool success, bytes memory result) = treasury.safeCall(treasuryGasLimit, 0, TREASURY_MAX_COPY, callData);
|
|
369
|
+
|
|
370
|
+
(treasuryNativeFee, lzTokenFee) = _parseTreasuryResult(_totalNativeFee, _payInLzToken, success, result);
|
|
371
|
+
// fee should be in lzTokenFee if payInLzToken, otherwise in native
|
|
372
|
+
if (treasuryNativeFee > 0) {
|
|
373
|
+
fees[treasury] += treasuryNativeFee;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/// @dev this interface should be DoS-free if the user is paying with native. properties
|
|
379
|
+
/// 1/ treasury can return an overly high lzToken fee
|
|
380
|
+
/// 2/ if treasury returns an overly high native fee, it will be capped by maxNativeFee,
|
|
381
|
+
/// which can be reasoned with the configurations
|
|
382
|
+
/// 3/ the owner can not configure the treasury in a way that force this function to revert
|
|
383
|
+
function _quoteTreasury(
|
|
384
|
+
address _sender,
|
|
385
|
+
uint32 _dstEid,
|
|
386
|
+
uint256 _totalNativeFee,
|
|
387
|
+
bool _payInLzToken
|
|
388
|
+
) internal view returns (uint256 nativeFee, uint256 lzTokenFee) {
|
|
389
|
+
// treasury must be set, and it has to be a contract
|
|
390
|
+
if (treasury != address(0x0)) {
|
|
391
|
+
bytes memory callData = abi.encodeCall(
|
|
392
|
+
ILayerZeroTreasury.getFee,
|
|
393
|
+
(_sender, _dstEid, _totalNativeFee, _payInLzToken)
|
|
394
|
+
);
|
|
395
|
+
(bool success, bytes memory result) = treasury.safeStaticCall(
|
|
396
|
+
treasuryGasLimit,
|
|
397
|
+
TREASURY_MAX_COPY,
|
|
398
|
+
callData
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
return _parseTreasuryResult(_totalNativeFee, _payInLzToken, success, result);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function _parseTreasuryResult(
|
|
406
|
+
uint256 _totalNativeFee,
|
|
407
|
+
bool _payInLzToken,
|
|
408
|
+
bool _success,
|
|
409
|
+
bytes memory _result
|
|
410
|
+
) internal view returns (uint256 nativeFee, uint256 lzTokenFee) {
|
|
411
|
+
// failure, charges nothing
|
|
412
|
+
if (!_success || _result.length < TREASURY_MAX_COPY) return (0, 0);
|
|
413
|
+
|
|
414
|
+
// parse the result
|
|
415
|
+
uint256 treasureFeeQuote = abi.decode(_result, (uint256));
|
|
416
|
+
if (_payInLzToken) {
|
|
417
|
+
lzTokenFee = treasureFeeQuote;
|
|
418
|
+
} else {
|
|
419
|
+
// pay in native
|
|
420
|
+
// we must prevent high-treasuryFee Dos attack
|
|
421
|
+
// nativeFee = min(treasureFeeQuote, maxNativeFee)
|
|
422
|
+
// opportunistically raise the maxNativeFee to be the same as _totalNativeFee
|
|
423
|
+
// can't use the _totalNativeFee alone because the oapp can use custom workers to force the fee to 0.
|
|
424
|
+
// maxNativeFee = max (_totalNativeFee, treasuryNativeFeeCap)
|
|
425
|
+
uint256 maxNativeFee = _totalNativeFee > treasuryNativeFeeCap ? _totalNativeFee : treasuryNativeFeeCap;
|
|
426
|
+
|
|
427
|
+
// min (treasureFeeQuote, nativeFeeCap)
|
|
428
|
+
nativeFee = treasureFeeQuote > maxNativeFee ? maxNativeFee : treasureFeeQuote;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function _verifyAndReclaimStorage(
|
|
433
|
+
ReadLibConfig memory _config,
|
|
434
|
+
bytes32 _headerHash,
|
|
435
|
+
bytes32 _cmdHash,
|
|
436
|
+
bytes32 _payloadHash
|
|
437
|
+
) internal {
|
|
438
|
+
if (!_checkVerifiable(_config, _headerHash, _cmdHash, _payloadHash)) {
|
|
439
|
+
revert LZ_RL_Verifying();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// iterate the required DVNs
|
|
443
|
+
if (_config.requiredDVNCount > 0) {
|
|
444
|
+
for (uint8 i = 0; i < _config.requiredDVNCount; ++i) {
|
|
445
|
+
delete hashLookup[_headerHash][_cmdHash][_config.requiredDVNs[i]];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// iterate the optional DVNs
|
|
450
|
+
if (_config.optionalDVNCount > 0) {
|
|
451
|
+
for (uint8 i = 0; i < _config.optionalDVNCount; ++i) {
|
|
452
|
+
delete hashLookup[_headerHash][_cmdHash][_config.optionalDVNs[i]];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/// @dev for verifiable view function
|
|
458
|
+
/// @dev checks if this verification is ready to be committed to the endpoint
|
|
459
|
+
function _checkVerifiable(
|
|
460
|
+
ReadLibConfig memory _config,
|
|
461
|
+
bytes32 _headerHash,
|
|
462
|
+
bytes32 _cmdHash,
|
|
463
|
+
bytes32 _payloadHash
|
|
464
|
+
) internal view returns (bool) {
|
|
465
|
+
// iterate the required DVNs
|
|
466
|
+
if (_config.requiredDVNCount > 0) {
|
|
467
|
+
for (uint8 i = 0; i < _config.requiredDVNCount; ++i) {
|
|
468
|
+
if (!_verified(_config.requiredDVNs[i], _headerHash, _cmdHash, _payloadHash)) {
|
|
469
|
+
// return if any of the required DVNs haven't signed
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (_config.optionalDVNCount == 0) {
|
|
474
|
+
// returns early if all required DVNs have signed and there are no optional DVNs
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// then it must require optional validations
|
|
480
|
+
uint8 threshold = _config.optionalDVNThreshold;
|
|
481
|
+
for (uint8 i = 0; i < _config.optionalDVNCount; ++i) {
|
|
482
|
+
if (_verified(_config.optionalDVNs[i], _headerHash, _cmdHash, _payloadHash)) {
|
|
483
|
+
// increment the optional count if the optional DVN has signed
|
|
484
|
+
threshold--;
|
|
485
|
+
if (threshold == 0) {
|
|
486
|
+
// early return if the optional threshold has hit
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// return false as a catch-all
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function _verified(
|
|
497
|
+
address _dvn,
|
|
498
|
+
bytes32 _headerHash,
|
|
499
|
+
bytes32 _cmdHash,
|
|
500
|
+
bytes32 _expectedPayloadHash
|
|
501
|
+
) internal view returns (bool verified) {
|
|
502
|
+
verified = hashLookup[_headerHash][_cmdHash][_dvn] == _expectedPayloadHash;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function _payExecutor(
|
|
506
|
+
address _executor,
|
|
507
|
+
address _sender,
|
|
508
|
+
bytes memory _executorOptions
|
|
509
|
+
) internal returns (uint256 executorFee) {
|
|
510
|
+
executorFee = ILayerZeroReadExecutor(_executor).assignJob(_sender, _executorOptions);
|
|
511
|
+
if (executorFee > 0) {
|
|
512
|
+
fees[_executor] += executorFee;
|
|
513
|
+
}
|
|
514
|
+
emit ExecutorFeePaid(_executor, executorFee);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
receive() external payable {}
|
|
518
|
+
}
|