@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,104 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol";
|
|
6
|
+
|
|
7
|
+
library ExecutorOptions {
|
|
8
|
+
using CalldataBytesLib for bytes;
|
|
9
|
+
|
|
10
|
+
uint8 internal constant WORKER_ID = 1;
|
|
11
|
+
|
|
12
|
+
uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;
|
|
13
|
+
uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;
|
|
14
|
+
uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;
|
|
15
|
+
uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;
|
|
16
|
+
uint8 internal constant OPTION_TYPE_LZREAD = 5;
|
|
17
|
+
|
|
18
|
+
error Executor_InvalidLzReceiveOption();
|
|
19
|
+
error Executor_InvalidNativeDropOption();
|
|
20
|
+
error Executor_InvalidLzComposeOption();
|
|
21
|
+
error Executor_InvalidLzReadOption();
|
|
22
|
+
|
|
23
|
+
/// @dev decode the next executor option from the options starting from the specified cursor
|
|
24
|
+
/// @param _options [executor_id][executor_option][executor_id][executor_option]...
|
|
25
|
+
/// executor_option = [option_size][option_type][option]
|
|
26
|
+
/// option_size = len(option_type) + len(option)
|
|
27
|
+
/// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes
|
|
28
|
+
/// @param _cursor the cursor to start decoding from
|
|
29
|
+
/// @return optionType the type of the option
|
|
30
|
+
/// @return option the option of the executor
|
|
31
|
+
/// @return cursor the cursor to start decoding the next executor option
|
|
32
|
+
function nextExecutorOption(
|
|
33
|
+
bytes calldata _options,
|
|
34
|
+
uint256 _cursor
|
|
35
|
+
) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {
|
|
36
|
+
unchecked {
|
|
37
|
+
// skip worker id
|
|
38
|
+
cursor = _cursor + 1;
|
|
39
|
+
|
|
40
|
+
// read option size
|
|
41
|
+
uint16 size = _options.toU16(cursor);
|
|
42
|
+
cursor += 2;
|
|
43
|
+
|
|
44
|
+
// read option type
|
|
45
|
+
optionType = _options.toU8(cursor);
|
|
46
|
+
|
|
47
|
+
// startCursor and endCursor are used to slice the option from _options
|
|
48
|
+
uint256 startCursor = cursor + 1; // skip option type
|
|
49
|
+
uint256 endCursor = cursor + size;
|
|
50
|
+
option = _options[startCursor:endCursor];
|
|
51
|
+
cursor += size;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {
|
|
56
|
+
if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();
|
|
57
|
+
gas = _option.toU128(0);
|
|
58
|
+
value = _option.length == 32 ? _option.toU128(16) : 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {
|
|
62
|
+
if (_option.length != 48) revert Executor_InvalidNativeDropOption();
|
|
63
|
+
amount = _option.toU128(0);
|
|
64
|
+
receiver = _option.toB32(16);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function decodeLzComposeOption(
|
|
68
|
+
bytes calldata _option
|
|
69
|
+
) internal pure returns (uint16 index, uint128 gas, uint128 value) {
|
|
70
|
+
if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();
|
|
71
|
+
index = _option.toU16(0);
|
|
72
|
+
gas = _option.toU128(2);
|
|
73
|
+
value = _option.length == 34 ? _option.toU128(18) : 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function decodeLzReadOption(
|
|
77
|
+
bytes calldata _option
|
|
78
|
+
) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {
|
|
79
|
+
if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();
|
|
80
|
+
gas = _option.toU128(0);
|
|
81
|
+
calldataSize = _option.toU32(16);
|
|
82
|
+
value = _option.length == 36 ? _option.toU128(20) : 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
|
|
86
|
+
return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {
|
|
90
|
+
return abi.encodePacked(_amount, _receiver);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
|
|
94
|
+
return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function encodeLzReadOption(
|
|
98
|
+
uint128 _gas,
|
|
99
|
+
uint32 _calldataSize,
|
|
100
|
+
uint128 _value
|
|
101
|
+
) internal pure returns (bytes memory) {
|
|
102
|
+
return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
/// @dev copied from https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol.
|
|
6
|
+
library SafeCall {
|
|
7
|
+
/// @notice calls a contract with a specified gas limit and value and captures the return data
|
|
8
|
+
/// @param _target The address to call
|
|
9
|
+
/// @param _gas The amount of gas to forward to the remote contract
|
|
10
|
+
/// @param _value The value in wei to send to the remote contract
|
|
11
|
+
/// to memory.
|
|
12
|
+
/// @param _maxCopy The maximum number of bytes of returndata to copy
|
|
13
|
+
/// to memory.
|
|
14
|
+
/// @param _calldata The data to send to the remote contract
|
|
15
|
+
/// @return success and returndata, as `.call()`. Returndata is capped to
|
|
16
|
+
/// `_maxCopy` bytes.
|
|
17
|
+
function safeCall(
|
|
18
|
+
address _target,
|
|
19
|
+
uint256 _gas,
|
|
20
|
+
uint256 _value,
|
|
21
|
+
uint16 _maxCopy,
|
|
22
|
+
bytes memory _calldata
|
|
23
|
+
) internal returns (bool, bytes memory) {
|
|
24
|
+
// check that target has code
|
|
25
|
+
uint size;
|
|
26
|
+
assembly {
|
|
27
|
+
size := extcodesize(_target)
|
|
28
|
+
}
|
|
29
|
+
if (size == 0) {
|
|
30
|
+
return (false, new bytes(0));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// set up for assembly call
|
|
34
|
+
uint256 _toCopy;
|
|
35
|
+
bool _success;
|
|
36
|
+
bytes memory _returnData = new bytes(_maxCopy);
|
|
37
|
+
// dispatch message to recipient
|
|
38
|
+
// by assembly calling "handle" function
|
|
39
|
+
// we call via assembly to avoid memcopying a very large returndata
|
|
40
|
+
// returned by a malicious contract
|
|
41
|
+
assembly {
|
|
42
|
+
_success := call(
|
|
43
|
+
_gas, // gas
|
|
44
|
+
_target, // recipient
|
|
45
|
+
_value, // ether value
|
|
46
|
+
add(_calldata, 0x20), // inloc
|
|
47
|
+
mload(_calldata), // inlen
|
|
48
|
+
0, // outloc
|
|
49
|
+
0 // outlen
|
|
50
|
+
)
|
|
51
|
+
// limit our copy to 100 bytes
|
|
52
|
+
_toCopy := returndatasize()
|
|
53
|
+
if gt(_toCopy, _maxCopy) {
|
|
54
|
+
_toCopy := _maxCopy
|
|
55
|
+
}
|
|
56
|
+
// Store the length of the copied bytes
|
|
57
|
+
mstore(_returnData, _toCopy)
|
|
58
|
+
// copy the bytes from returndata[0:_toCopy]
|
|
59
|
+
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
|
|
60
|
+
}
|
|
61
|
+
return (_success, _returnData);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// @notice Use when you _really_ really _really_ don't trust the called
|
|
65
|
+
/// contract. This prevents the called contract from causing reversion of
|
|
66
|
+
/// the caller in as many ways as we can.
|
|
67
|
+
/// @dev The main difference between this and a solidity low-level call is
|
|
68
|
+
/// that we limit the number of bytes that the callee can cause to be
|
|
69
|
+
/// copied to caller memory. This prevents stupid things like malicious
|
|
70
|
+
/// contracts returning 10,000,000 bytes causing a local OOG when copying
|
|
71
|
+
/// to memory.
|
|
72
|
+
/// @param _target The address to call
|
|
73
|
+
/// @param _gas The amount of gas to forward to the remote contract
|
|
74
|
+
/// @param _maxCopy The maximum number of bytes of returndata to copy
|
|
75
|
+
/// to memory.
|
|
76
|
+
/// @param _calldata The data to send to the remote contract
|
|
77
|
+
/// @return success and returndata, as `.call()`. Returndata is capped to
|
|
78
|
+
/// `_maxCopy` bytes.
|
|
79
|
+
function safeStaticCall(
|
|
80
|
+
address _target,
|
|
81
|
+
uint256 _gas,
|
|
82
|
+
uint16 _maxCopy,
|
|
83
|
+
bytes memory _calldata
|
|
84
|
+
) internal view returns (bool, bytes memory) {
|
|
85
|
+
// check that target has code
|
|
86
|
+
uint size;
|
|
87
|
+
assembly {
|
|
88
|
+
size := extcodesize(_target)
|
|
89
|
+
}
|
|
90
|
+
if (size == 0) {
|
|
91
|
+
return (false, new bytes(0));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// set up for assembly call
|
|
95
|
+
uint256 _toCopy;
|
|
96
|
+
bool _success;
|
|
97
|
+
bytes memory _returnData = new bytes(_maxCopy);
|
|
98
|
+
// dispatch message to recipient
|
|
99
|
+
// by assembly calling "handle" function
|
|
100
|
+
// we call via assembly to avoid memcopying a very large returndata
|
|
101
|
+
// returned by a malicious contract
|
|
102
|
+
assembly {
|
|
103
|
+
_success := staticcall(
|
|
104
|
+
_gas, // gas
|
|
105
|
+
_target, // recipient
|
|
106
|
+
add(_calldata, 0x20), // inloc
|
|
107
|
+
mload(_calldata), // inlen
|
|
108
|
+
0, // outloc
|
|
109
|
+
0 // outlen
|
|
110
|
+
)
|
|
111
|
+
// limit our copy to 256 bytes
|
|
112
|
+
_toCopy := returndatasize()
|
|
113
|
+
if gt(_toCopy, _maxCopy) {
|
|
114
|
+
_toCopy := _maxCopy
|
|
115
|
+
}
|
|
116
|
+
// Store the length of the copied bytes
|
|
117
|
+
mstore(_returnData, _toCopy)
|
|
118
|
+
// copy the bytes from returndata[0:_toCopy]
|
|
119
|
+
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
|
|
120
|
+
}
|
|
121
|
+
return (_success, _returnData);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
|
|
6
|
+
|
|
7
|
+
import { MultiCall } from "./MultiCall.sol";
|
|
8
|
+
|
|
9
|
+
/// @title EssenceDVNWrapper is a contract, used to execute multiple worker calls in a single transaction
|
|
10
|
+
/// The calls should be as follows:
|
|
11
|
+
/// 1. N calls to dvn.execute() to verify the payload on ULN
|
|
12
|
+
/// 2. 1 call to uln.commitVerification() to commit the verification into the endpoint
|
|
13
|
+
/// 3. 1 call to executor.execute() to execute lzReceive() on the endpoint
|
|
14
|
+
///
|
|
15
|
+
/// Node: The DVN should assign EssenceDVNWrapper as an admin to the DVN contract.
|
|
16
|
+
///
|
|
17
|
+
contract EssenceDVNWrapper is AccessControl, MultiCall {
|
|
18
|
+
bytes32 internal constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
|
|
19
|
+
|
|
20
|
+
error DVNWrapper_InvalidRole(bytes32 role);
|
|
21
|
+
error DVNWrapper_InvalidAdminCount();
|
|
22
|
+
error DVNWrapper_RoleRenouncingDisabled();
|
|
23
|
+
|
|
24
|
+
uint256 public adminCount;
|
|
25
|
+
|
|
26
|
+
modifier onlyAdmin(bytes32 _role) {
|
|
27
|
+
if (_role == ADMIN_ROLE) {
|
|
28
|
+
_checkRole(_role); // admin required
|
|
29
|
+
} else {
|
|
30
|
+
revert DVNWrapper_InvalidRole(_role);
|
|
31
|
+
}
|
|
32
|
+
_;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
constructor(address[] memory admins) {
|
|
36
|
+
if (admins.length == 0) {
|
|
37
|
+
revert DVNWrapper_InvalidAdminCount();
|
|
38
|
+
}
|
|
39
|
+
for (uint i = 0; i < admins.length; i++) {
|
|
40
|
+
_setupRole(ADMIN_ROLE, admins[i]);
|
|
41
|
+
}
|
|
42
|
+
adminCount = admins.length;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function multiCall(
|
|
46
|
+
Call[] calldata _calls
|
|
47
|
+
) public payable override onlyAdmin(ADMIN_ROLE) returns (bool[] memory successes, bytes[] memory results) {
|
|
48
|
+
(successes, results) = super.multiCall(_calls);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ========================= Override Functions =========================
|
|
52
|
+
|
|
53
|
+
function grantRole(bytes32 _role, address _account) public override onlyAdmin(_role) {
|
|
54
|
+
_grantRole(_role, _account);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function revokeRole(bytes32 _role, address _account) public override onlyAdmin(_role) {
|
|
58
|
+
_revokeRole(_role, _account);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renounceRole(bytes32 /*role*/, address /*account*/) public pure override {
|
|
62
|
+
revert DVNWrapper_RoleRenouncingDisabled();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _grantRole(bytes32 _role, address _account) internal override {
|
|
66
|
+
if (_role == ADMIN_ROLE) ++adminCount;
|
|
67
|
+
super._grantRole(_role, _account);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _revokeRole(bytes32 _role, address _account) internal override {
|
|
71
|
+
if (_role == ADMIN_ROLE) --adminCount;
|
|
72
|
+
if (adminCount == 0) {
|
|
73
|
+
revert DVNWrapper_InvalidAdminCount(); // not allowed to remove all admins
|
|
74
|
+
}
|
|
75
|
+
super._revokeRole(_role, _account);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
|
|
6
|
+
|
|
7
|
+
contract MultiCall {
|
|
8
|
+
struct Call {
|
|
9
|
+
address target;
|
|
10
|
+
bytes data;
|
|
11
|
+
uint256 gasLimit;
|
|
12
|
+
uint256 value;
|
|
13
|
+
bool revertOnError;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
error NotEnoughGas(uint256 index, uint256 requested, uint256 available);
|
|
17
|
+
error CallReverted(uint256 index, bytes result);
|
|
18
|
+
|
|
19
|
+
function multiCall(
|
|
20
|
+
Call[] calldata _calls
|
|
21
|
+
) public payable virtual returns (bool[] memory successes, bytes[] memory results) {
|
|
22
|
+
successes = new bool[](_calls.length);
|
|
23
|
+
results = new bytes[](_calls.length);
|
|
24
|
+
|
|
25
|
+
for (uint256 i = 0; i < _calls.length; i++) {
|
|
26
|
+
Call calldata call = _calls[i];
|
|
27
|
+
|
|
28
|
+
if (gasleft() < call.gasLimit) revert NotEnoughGas(i, call.gasLimit, gasleft());
|
|
29
|
+
|
|
30
|
+
(successes[i], results[i]) = call.target.call{
|
|
31
|
+
value: call.value,
|
|
32
|
+
gas: call.gasLimit == 0 ? gasleft() : call.gasLimit
|
|
33
|
+
}(call.data);
|
|
34
|
+
|
|
35
|
+
if (!successes[i] && _calls[i].revertOnError) revert CallReverted(i, results[i]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (address(this).balance > 0) {
|
|
39
|
+
Transfer.native(msg.sender, address(this).balance);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.22;
|
|
3
|
+
|
|
4
|
+
import { ExecuteParam } from "../uln/dvn/DVN.sol";
|
|
5
|
+
|
|
6
|
+
contract DVNMock {
|
|
7
|
+
event Executed(uint32 vid, address target, bytes callData, uint256 expiration, bytes signatures);
|
|
8
|
+
|
|
9
|
+
uint32 public immutable vid;
|
|
10
|
+
|
|
11
|
+
constructor(uint32 _vid) {
|
|
12
|
+
vid = _vid;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function execute(ExecuteParam[] calldata _params) external {
|
|
16
|
+
for (uint256 i = 0; i < _params.length; i++) {
|
|
17
|
+
emit Executed(
|
|
18
|
+
_params[i].vid,
|
|
19
|
+
_params[i].target,
|
|
20
|
+
_params[i].callData,
|
|
21
|
+
_params[i].expiration,
|
|
22
|
+
_params[i].signatures
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function verify(bytes calldata _packetHeader, bytes32 _payloadHash, uint64 _confirmations) external {}
|
|
28
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
|
|
6
|
+
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";
|
|
7
|
+
import { IExecutor } from "../../contracts/interfaces/IExecutor.sol";
|
|
8
|
+
|
|
9
|
+
contract ExecutorMock {
|
|
10
|
+
using PacketV1Codec for bytes;
|
|
11
|
+
|
|
12
|
+
event NativeDropMeta(
|
|
13
|
+
uint32 srcEid,
|
|
14
|
+
bytes32 sender,
|
|
15
|
+
uint64 nonce,
|
|
16
|
+
uint32 dstEid,
|
|
17
|
+
address oapp,
|
|
18
|
+
uint256 nativeDropGasLimit
|
|
19
|
+
);
|
|
20
|
+
event NativeDropped(address receiver, uint256 amount);
|
|
21
|
+
event Executed301(bytes packet, uint256 gasLimit);
|
|
22
|
+
event Executed302(
|
|
23
|
+
uint32 srcEid,
|
|
24
|
+
bytes32 sender,
|
|
25
|
+
uint64 nonce,
|
|
26
|
+
address receiver,
|
|
27
|
+
bytes32 guid,
|
|
28
|
+
bytes message,
|
|
29
|
+
bytes extraData,
|
|
30
|
+
uint256 gasLimit
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
uint32 public immutable dstEid;
|
|
34
|
+
|
|
35
|
+
constructor(uint32 _dstEid) {
|
|
36
|
+
dstEid = _dstEid;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function nativeDrop(
|
|
40
|
+
Origin calldata _origin,
|
|
41
|
+
uint32 _dstEid,
|
|
42
|
+
address _oapp,
|
|
43
|
+
IExecutor.NativeDropParams[] calldata _nativeDropParams,
|
|
44
|
+
uint256 _nativeDropGasLimit
|
|
45
|
+
) external payable {
|
|
46
|
+
_nativeDrop(_origin, _dstEid, _oapp, _nativeDropParams, _nativeDropGasLimit);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function nativeDropAndExecute301(
|
|
50
|
+
Origin calldata _origin,
|
|
51
|
+
IExecutor.NativeDropParams[] calldata _nativeDropParams,
|
|
52
|
+
uint256 _nativeDropGasLimit,
|
|
53
|
+
bytes calldata _packet,
|
|
54
|
+
uint256 _gasLimit
|
|
55
|
+
) external payable {
|
|
56
|
+
_nativeDrop(_origin, _packet.dstEid(), _packet.receiverB20(), _nativeDropParams, _nativeDropGasLimit);
|
|
57
|
+
emit Executed301(_packet, _gasLimit);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function execute301(bytes calldata _packet, uint256 _gasLimit) external {
|
|
61
|
+
emit Executed301(_packet, _gasLimit);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function nativeDropAndExecute302(
|
|
65
|
+
IExecutor.NativeDropParams[] calldata _nativeDropParams,
|
|
66
|
+
uint256 _nativeDropGasLimit,
|
|
67
|
+
IExecutor.ExecutionParams calldata _executionParams
|
|
68
|
+
) external payable {
|
|
69
|
+
_nativeDrop(_executionParams.origin, dstEid, _executionParams.receiver, _nativeDropParams, _nativeDropGasLimit);
|
|
70
|
+
|
|
71
|
+
emit Executed302(
|
|
72
|
+
_executionParams.origin.srcEid,
|
|
73
|
+
_executionParams.origin.sender,
|
|
74
|
+
_executionParams.origin.nonce,
|
|
75
|
+
_executionParams.receiver,
|
|
76
|
+
_executionParams.guid,
|
|
77
|
+
_executionParams.message,
|
|
78
|
+
_executionParams.extraData,
|
|
79
|
+
_executionParams.gasLimit
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function _nativeDrop(
|
|
84
|
+
Origin calldata _origin,
|
|
85
|
+
uint32 _dstEid,
|
|
86
|
+
address _oapp,
|
|
87
|
+
IExecutor.NativeDropParams[] calldata _nativeDropParams,
|
|
88
|
+
uint256 _nativeDropGasLimit
|
|
89
|
+
) internal {
|
|
90
|
+
for (uint256 i = 0; i < _nativeDropParams.length; i++) {
|
|
91
|
+
emit NativeDropped(_nativeDropParams[i].receiver, _nativeDropParams[i].amount);
|
|
92
|
+
}
|
|
93
|
+
emit NativeDropMeta(_origin.srcEid, _origin.sender, _origin.nonce, _dstEid, _oapp, _nativeDropGasLimit);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
6
|
+
import { Proxied } from "hardhat-deploy/solc_0.8/proxy/Proxied.sol";
|
|
7
|
+
|
|
8
|
+
import { IReceiveUlnE2 } from "./interfaces/IReceiveUlnE2.sol";
|
|
9
|
+
import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
|
|
10
|
+
import { Transfer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/Transfer.sol";
|
|
11
|
+
|
|
12
|
+
import { ExecutionState, EndpointV2ViewUpgradeable } from "@layerzerolabs/lz-evm-protocol-v2/contracts/EndpointV2ViewUpgradeable.sol";
|
|
13
|
+
|
|
14
|
+
import { VerificationState } from "./uln302/ReceiveUln302View.sol";
|
|
15
|
+
|
|
16
|
+
struct LzReceiveParam {
|
|
17
|
+
Origin origin;
|
|
18
|
+
address receiver;
|
|
19
|
+
bytes32 guid;
|
|
20
|
+
bytes message;
|
|
21
|
+
bytes extraData;
|
|
22
|
+
uint256 gas;
|
|
23
|
+
uint256 value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
struct NativeDropParam {
|
|
27
|
+
address _receiver;
|
|
28
|
+
uint256 _amount;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface IReceiveUlnView {
|
|
32
|
+
function verifiable(bytes calldata _packetHeader, bytes32 _payloadHash) external view returns (VerificationState);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
contract LzExecutor is OwnableUpgradeable, EndpointV2ViewUpgradeable, Proxied {
|
|
36
|
+
error LzExecutor_Executed();
|
|
37
|
+
error LzExecutor_Verifying();
|
|
38
|
+
error LzExecutor_ReceiveLibViewNotSet();
|
|
39
|
+
|
|
40
|
+
event NativeWithdrawn(address _to, uint256 _amount);
|
|
41
|
+
event ReceiveLibViewSet(address _receiveLib, address _receiveLibView);
|
|
42
|
+
|
|
43
|
+
address public receiveUln302;
|
|
44
|
+
uint32 public localEid;
|
|
45
|
+
|
|
46
|
+
mapping(address receiveLib => address receiveLibView) public receiveLibToView;
|
|
47
|
+
|
|
48
|
+
function initialize(
|
|
49
|
+
address _receiveUln302,
|
|
50
|
+
address _receiveUln302View,
|
|
51
|
+
address _endpoint
|
|
52
|
+
) external proxied initializer {
|
|
53
|
+
__Ownable_init();
|
|
54
|
+
__EndpointV2View_init(_endpoint);
|
|
55
|
+
|
|
56
|
+
receiveUln302 = _receiveUln302;
|
|
57
|
+
localEid = endpoint.eid();
|
|
58
|
+
receiveLibToView[_receiveUln302] = _receiveUln302View;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================ OnlyOwner ===================================
|
|
62
|
+
|
|
63
|
+
function withdrawNative(address _to, uint256 _amount) external onlyOwner {
|
|
64
|
+
Transfer.native(_to, _amount);
|
|
65
|
+
emit NativeWithdrawn(_to, _amount);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function setReceiveLibView(address _receiveLib, address _receiveLibView) external onlyOwner {
|
|
69
|
+
receiveLibToView[_receiveLib] = _receiveLibView;
|
|
70
|
+
emit ReceiveLibViewSet(_receiveLib, _receiveLibView);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ============================ External ===================================
|
|
74
|
+
|
|
75
|
+
/// @notice process for commit and execute
|
|
76
|
+
/// 1. check if executable, revert if executed, execute if executable
|
|
77
|
+
/// 2. check if verifiable, revert if verifying, commit if verifiable
|
|
78
|
+
/// 3. native drop
|
|
79
|
+
/// 4. try execute, will revert if not executable
|
|
80
|
+
function commitAndExecute(
|
|
81
|
+
address _receiveLib,
|
|
82
|
+
LzReceiveParam calldata _lzReceiveParam,
|
|
83
|
+
NativeDropParam[] calldata _nativeDropParams
|
|
84
|
+
) external payable {
|
|
85
|
+
/// 1. check if executable, revert if executed
|
|
86
|
+
ExecutionState executionState = executable(_lzReceiveParam.origin, _lzReceiveParam.receiver);
|
|
87
|
+
if (executionState == ExecutionState.Executed) revert LzExecutor_Executed();
|
|
88
|
+
|
|
89
|
+
/// 2. if not executable, check if verifiable, revert if verifying, commit if verifiable
|
|
90
|
+
if (executionState != ExecutionState.Executable) {
|
|
91
|
+
address receiveLib = receiveUln302 == address(0x0) ? _receiveLib : address(receiveUln302);
|
|
92
|
+
bytes memory packetHeader = abi.encodePacked(
|
|
93
|
+
uint8(1), // packet version 1
|
|
94
|
+
_lzReceiveParam.origin.nonce,
|
|
95
|
+
_lzReceiveParam.origin.srcEid,
|
|
96
|
+
_lzReceiveParam.origin.sender,
|
|
97
|
+
localEid,
|
|
98
|
+
bytes32(uint256(uint160(_lzReceiveParam.receiver)))
|
|
99
|
+
);
|
|
100
|
+
bytes32 payloadHash = keccak256(abi.encodePacked(_lzReceiveParam.guid, _lzReceiveParam.message));
|
|
101
|
+
|
|
102
|
+
address receiveLibView = receiveLibToView[receiveLib];
|
|
103
|
+
if (receiveLibView == address(0x0)) revert LzExecutor_ReceiveLibViewNotSet();
|
|
104
|
+
|
|
105
|
+
VerificationState verificationState = IReceiveUlnView(receiveLibView).verifiable(packetHeader, payloadHash);
|
|
106
|
+
if (verificationState == VerificationState.Verifiable) {
|
|
107
|
+
// verification required
|
|
108
|
+
IReceiveUlnE2(receiveLib).commitVerification(packetHeader, payloadHash);
|
|
109
|
+
} else if (verificationState == VerificationState.Verifying) {
|
|
110
|
+
revert LzExecutor_Verifying();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// 3. native drop
|
|
115
|
+
for (uint256 i = 0; i < _nativeDropParams.length; i++) {
|
|
116
|
+
NativeDropParam calldata param = _nativeDropParams[i];
|
|
117
|
+
Transfer.native(param._receiver, param._amount);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// 4. try execute, will revert if not executable
|
|
121
|
+
endpoint.lzReceive{ gas: _lzReceiveParam.gas, value: _lzReceiveParam.value }(
|
|
122
|
+
_lzReceiveParam.origin,
|
|
123
|
+
_lzReceiveParam.receiver,
|
|
124
|
+
_lzReceiveParam.guid,
|
|
125
|
+
_lzReceiveParam.message,
|
|
126
|
+
_lzReceiveParam.extraData
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|