@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,125 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
|
|
6
|
+
|
|
7
|
+
import { UlnBase, UlnConfig } from "./UlnBase.sol";
|
|
8
|
+
|
|
9
|
+
struct Verification {
|
|
10
|
+
bool submitted;
|
|
11
|
+
uint64 confirmations;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// @dev includes the utility functions for checking ULN states and logics
|
|
15
|
+
abstract contract ReceiveUlnBase is UlnBase {
|
|
16
|
+
using PacketV1Codec for bytes;
|
|
17
|
+
|
|
18
|
+
mapping(bytes32 headerHash => mapping(bytes32 payloadHash => mapping(address dvn => Verification)))
|
|
19
|
+
public hashLookup;
|
|
20
|
+
|
|
21
|
+
event PayloadVerified(address dvn, bytes header, uint256 confirmations, bytes32 proofHash);
|
|
22
|
+
|
|
23
|
+
error LZ_ULN_InvalidPacketHeader();
|
|
24
|
+
error LZ_ULN_InvalidPacketVersion();
|
|
25
|
+
error LZ_ULN_InvalidEid();
|
|
26
|
+
error LZ_ULN_Verifying();
|
|
27
|
+
|
|
28
|
+
// ============================ External ===================================
|
|
29
|
+
function verifiable(
|
|
30
|
+
UlnConfig memory _config,
|
|
31
|
+
bytes32 _headerHash,
|
|
32
|
+
bytes32 _payloadHash
|
|
33
|
+
) external view returns (bool) {
|
|
34
|
+
return _checkVerifiable(_config, _headerHash, _payloadHash);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function assertHeader(bytes calldata _packetHeader, uint32 _localEid) external pure {
|
|
38
|
+
_assertHeader(_packetHeader, _localEid);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================ Internal ===================================
|
|
42
|
+
/// @dev per DVN signing function
|
|
43
|
+
function _verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) internal {
|
|
44
|
+
hashLookup[keccak256(_packetHeader)][_payloadHash][msg.sender] = Verification(true, _confirmations);
|
|
45
|
+
emit PayloadVerified(msg.sender, _packetHeader, _confirmations, _payloadHash);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _verified(
|
|
49
|
+
address _dvn,
|
|
50
|
+
bytes32 _headerHash,
|
|
51
|
+
bytes32 _payloadHash,
|
|
52
|
+
uint64 _requiredConfirmation
|
|
53
|
+
) internal view returns (bool verified) {
|
|
54
|
+
Verification memory verification = hashLookup[_headerHash][_payloadHash][_dvn];
|
|
55
|
+
// return true if the dvn has signed enough confirmations
|
|
56
|
+
verified = verification.submitted && verification.confirmations >= _requiredConfirmation;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _verifyAndReclaimStorage(UlnConfig memory _config, bytes32 _headerHash, bytes32 _payloadHash) internal {
|
|
60
|
+
if (!_checkVerifiable(_config, _headerHash, _payloadHash)) {
|
|
61
|
+
revert LZ_ULN_Verifying();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// iterate the required DVNs
|
|
65
|
+
if (_config.requiredDVNCount > 0) {
|
|
66
|
+
for (uint8 i = 0; i < _config.requiredDVNCount; ++i) {
|
|
67
|
+
delete hashLookup[_headerHash][_payloadHash][_config.requiredDVNs[i]];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// iterate the optional DVNs
|
|
72
|
+
if (_config.optionalDVNCount > 0) {
|
|
73
|
+
for (uint8 i = 0; i < _config.optionalDVNCount; ++i) {
|
|
74
|
+
delete hashLookup[_headerHash][_payloadHash][_config.optionalDVNs[i]];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function _assertHeader(bytes calldata _packetHeader, uint32 _localEid) internal pure {
|
|
80
|
+
// assert packet header is of right size 81
|
|
81
|
+
if (_packetHeader.length != 81) revert LZ_ULN_InvalidPacketHeader();
|
|
82
|
+
// assert packet header version is the same as ULN
|
|
83
|
+
if (_packetHeader.version() != PacketV1Codec.PACKET_VERSION) revert LZ_ULN_InvalidPacketVersion();
|
|
84
|
+
// assert the packet is for this endpoint
|
|
85
|
+
if (_packetHeader.dstEid() != _localEid) revert LZ_ULN_InvalidEid();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// @dev for verifiable view function
|
|
89
|
+
/// @dev checks if this verification is ready to be committed to the endpoint
|
|
90
|
+
function _checkVerifiable(
|
|
91
|
+
UlnConfig memory _config,
|
|
92
|
+
bytes32 _headerHash,
|
|
93
|
+
bytes32 _payloadHash
|
|
94
|
+
) internal view returns (bool) {
|
|
95
|
+
// iterate the required DVNs
|
|
96
|
+
if (_config.requiredDVNCount > 0) {
|
|
97
|
+
for (uint8 i = 0; i < _config.requiredDVNCount; ++i) {
|
|
98
|
+
if (!_verified(_config.requiredDVNs[i], _headerHash, _payloadHash, _config.confirmations)) {
|
|
99
|
+
// return if any of the required DVNs haven't signed
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (_config.optionalDVNCount == 0) {
|
|
104
|
+
// returns early if all required DVNs have signed and there are no optional DVNs
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// then it must require optional validations
|
|
110
|
+
uint8 threshold = _config.optionalDVNThreshold;
|
|
111
|
+
for (uint8 i = 0; i < _config.optionalDVNCount; ++i) {
|
|
112
|
+
if (_verified(_config.optionalDVNs[i], _headerHash, _payloadHash, _config.confirmations)) {
|
|
113
|
+
// increment the optional count if the optional DVN has signed
|
|
114
|
+
threshold--;
|
|
115
|
+
if (threshold == 0) {
|
|
116
|
+
// early return if the optional threshold has hit
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// return false as a catch-all
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { Packet } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ISendLib.sol";
|
|
6
|
+
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
|
|
7
|
+
|
|
8
|
+
import { ILayerZeroDVN } from "./interfaces/ILayerZeroDVN.sol";
|
|
9
|
+
import { DVNOptions } from "./libs/DVNOptions.sol";
|
|
10
|
+
import { UlnOptions } from "./libs/UlnOptions.sol";
|
|
11
|
+
import { WorkerOptions } from "../SendLibBase.sol";
|
|
12
|
+
import { UlnConfig, UlnBase } from "./UlnBase.sol";
|
|
13
|
+
|
|
14
|
+
/// @dev includes the utility functions for checking ULN states and logics
|
|
15
|
+
abstract contract SendUlnBase is UlnBase {
|
|
16
|
+
event DVNFeePaid(address[] requiredDVNs, address[] optionalDVNs, uint256[] fees);
|
|
17
|
+
|
|
18
|
+
function _splitUlnOptions(bytes calldata _options) internal pure returns (bytes memory, WorkerOptions[] memory) {
|
|
19
|
+
(bytes memory executorOpts, bytes memory dvnOpts) = UlnOptions.decode(_options);
|
|
20
|
+
|
|
21
|
+
if (dvnOpts.length == 0) {
|
|
22
|
+
return (executorOpts, new WorkerOptions[](0));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
WorkerOptions[] memory workerOpts = new WorkerOptions[](1);
|
|
26
|
+
workerOpts[0] = WorkerOptions(DVNOptions.WORKER_ID, dvnOpts);
|
|
27
|
+
return (executorOpts, workerOpts);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// ---------- pay and assign jobs ----------
|
|
31
|
+
|
|
32
|
+
function _payDVNs(
|
|
33
|
+
mapping(address => uint256) storage _fees,
|
|
34
|
+
Packet memory _packet,
|
|
35
|
+
WorkerOptions[] memory _options
|
|
36
|
+
) internal returns (uint256 totalFee, bytes memory encodedPacket) {
|
|
37
|
+
bytes memory packetHeader = PacketV1Codec.encodePacketHeader(_packet);
|
|
38
|
+
bytes memory payload = PacketV1Codec.encodePayload(_packet);
|
|
39
|
+
bytes32 payloadHash = keccak256(payload);
|
|
40
|
+
uint32 dstEid = _packet.dstEid;
|
|
41
|
+
address sender = _packet.sender;
|
|
42
|
+
UlnConfig memory config = getUlnConfig(sender, dstEid);
|
|
43
|
+
|
|
44
|
+
// if options is not empty, it must be dvn options
|
|
45
|
+
bytes memory dvnOptions = _options.length == 0 ? bytes("") : _options[0].options;
|
|
46
|
+
uint256[] memory dvnFees;
|
|
47
|
+
(totalFee, dvnFees) = _assignJobs(
|
|
48
|
+
_fees,
|
|
49
|
+
config,
|
|
50
|
+
ILayerZeroDVN.AssignJobParam(dstEid, packetHeader, payloadHash, config.confirmations, sender),
|
|
51
|
+
dvnOptions
|
|
52
|
+
);
|
|
53
|
+
encodedPacket = abi.encodePacked(packetHeader, payload);
|
|
54
|
+
|
|
55
|
+
emit DVNFeePaid(config.requiredDVNs, config.optionalDVNs, dvnFees);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function _assignJobs(
|
|
59
|
+
mapping(address => uint256) storage _fees,
|
|
60
|
+
UlnConfig memory _ulnConfig,
|
|
61
|
+
ILayerZeroDVN.AssignJobParam memory _param,
|
|
62
|
+
bytes memory dvnOptions
|
|
63
|
+
) internal returns (uint256 totalFee, uint256[] memory dvnFees) {
|
|
64
|
+
(bytes[] memory optionsArray, uint8[] memory dvnIds) = DVNOptions.groupDVNOptionsByIdx(dvnOptions);
|
|
65
|
+
|
|
66
|
+
uint8 dvnsLength = _ulnConfig.requiredDVNCount + _ulnConfig.optionalDVNCount;
|
|
67
|
+
dvnFees = new uint256[](dvnsLength);
|
|
68
|
+
for (uint8 i = 0; i < dvnsLength; ++i) {
|
|
69
|
+
address dvn = i < _ulnConfig.requiredDVNCount
|
|
70
|
+
? _ulnConfig.requiredDVNs[i]
|
|
71
|
+
: _ulnConfig.optionalDVNs[i - _ulnConfig.requiredDVNCount];
|
|
72
|
+
|
|
73
|
+
bytes memory options = "";
|
|
74
|
+
for (uint256 j = 0; j < dvnIds.length; ++j) {
|
|
75
|
+
if (dvnIds[j] == i) {
|
|
76
|
+
options = optionsArray[j];
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
dvnFees[i] = ILayerZeroDVN(dvn).assignJob(_param, options);
|
|
82
|
+
if (dvnFees[i] > 0) {
|
|
83
|
+
_fees[dvn] += dvnFees[i];
|
|
84
|
+
totalFee += dvnFees[i];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// ---------- quote ----------
|
|
90
|
+
function _quoteDVNs(
|
|
91
|
+
address _sender,
|
|
92
|
+
uint32 _dstEid,
|
|
93
|
+
WorkerOptions[] memory _options
|
|
94
|
+
) internal view returns (uint256 totalFee) {
|
|
95
|
+
UlnConfig memory config = getUlnConfig(_sender, _dstEid);
|
|
96
|
+
|
|
97
|
+
// if options is not empty, it must be dvn options
|
|
98
|
+
bytes memory dvnOptions = _options.length == 0 ? bytes("") : _options[0].options;
|
|
99
|
+
(bytes[] memory optionsArray, uint8[] memory dvnIndices) = DVNOptions.groupDVNOptionsByIdx(dvnOptions);
|
|
100
|
+
|
|
101
|
+
totalFee = _getFees(config, _dstEid, _sender, optionsArray, dvnIndices);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function _getFees(
|
|
105
|
+
UlnConfig memory _config,
|
|
106
|
+
uint32 _dstEid,
|
|
107
|
+
address _sender,
|
|
108
|
+
bytes[] memory _optionsArray,
|
|
109
|
+
uint8[] memory _dvnIds
|
|
110
|
+
) internal view returns (uint256 totalFee) {
|
|
111
|
+
// here we merge 2 list of dvns into 1 to allocate the indexed dvn options to the right dvn
|
|
112
|
+
uint8 dvnsLength = _config.requiredDVNCount + _config.optionalDVNCount;
|
|
113
|
+
for (uint8 i = 0; i < dvnsLength; ++i) {
|
|
114
|
+
address dvn = i < _config.requiredDVNCount
|
|
115
|
+
? _config.requiredDVNs[i]
|
|
116
|
+
: _config.optionalDVNs[i - _config.requiredDVNCount];
|
|
117
|
+
|
|
118
|
+
bytes memory options = "";
|
|
119
|
+
// it is a double loop here. however, if the list is short, the cost is very acceptable.
|
|
120
|
+
for (uint256 j = 0; j < _dvnIds.length; ++j) {
|
|
121
|
+
if (_dvnIds[j] == i) {
|
|
122
|
+
options = _optionsArray[j];
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
totalFee += ILayerZeroDVN(dvn).getFee(_dstEid, _config.confirmations, _sender, options);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
|
|
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 LZ_ULN_Unsorted();
|
|
37
|
+
error LZ_ULN_InvalidRequiredDVNCount();
|
|
38
|
+
error LZ_ULN_InvalidOptionalDVNCount();
|
|
39
|
+
error LZ_ULN_AtLeastOneDVN();
|
|
40
|
+
error LZ_ULN_InvalidOptionalDVNThreshold();
|
|
41
|
+
error LZ_ULN_InvalidConfirmations();
|
|
42
|
+
error LZ_ULN_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 LZ_ULN_InvalidRequiredDVNCount();
|
|
61
|
+
if (param.config.optionalDVNCount == NIL_DVN_COUNT) revert LZ_ULN_InvalidOptionalDVNCount();
|
|
62
|
+
if (param.config.confirmations == NIL_CONFIRMATIONS) revert LZ_ULN_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 LZ_ULN_UnsupportedEid(_remoteEid);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ============================ Private ===================================
|
|
145
|
+
|
|
146
|
+
function _assertAtLeastOneDVN(UlnConfig memory _config) private pure {
|
|
147
|
+
if (_config.requiredDVNCount == 0 && _config.optionalDVNThreshold == 0) revert LZ_ULN_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 LZ_ULN_InvalidRequiredDVNCount();
|
|
158
|
+
} else {
|
|
159
|
+
if (_param.requiredDVNs.length != _param.requiredDVNCount || _param.requiredDVNCount > MAX_COUNT)
|
|
160
|
+
revert LZ_ULN_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 LZ_ULN_InvalidOptionalDVNCount();
|
|
174
|
+
if (_param.optionalDVNThreshold != 0) revert LZ_ULN_InvalidOptionalDVNThreshold();
|
|
175
|
+
} else {
|
|
176
|
+
if (_param.optionalDVNs.length != _param.optionalDVNCount || _param.optionalDVNCount > MAX_COUNT)
|
|
177
|
+
revert LZ_ULN_InvalidOptionalDVNCount();
|
|
178
|
+
if (_param.optionalDVNThreshold == 0 || _param.optionalDVNThreshold > _param.optionalDVNCount)
|
|
179
|
+
revert LZ_ULN_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 LZ_ULN_Unsorted(); // to ensure no duplicates
|
|
192
|
+
lastDVN = dvn;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|