@layerzerolabs/lz-evm-oapp-v2 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/contracts/oapp/OApp.sol/OApp.json +332 -0
- package/artifacts/contracts/oapp/OAppCore.sol/OAppCore.json +195 -0
- package/artifacts/contracts/oapp/OAppReceiver.sol/OAppReceiver.json +316 -0
- package/artifacts/contracts/oapp/OAppSender.sol/OAppSender.json +211 -0
- package/artifacts/contracts/oapp/examples/OmniCounter.sol/MsgCodec.json +10 -0
- package/artifacts/contracts/oapp/examples/OmniCounter.sol/OmniCounter.json +874 -0
- package/artifacts/contracts/oapp/examples/OmniCounterPreCrime.sol/OmniCounterPreCrime.json +373 -0
- package/artifacts/contracts/oapp/interfaces/IOAppComposer.sol/IOAppComposer.json +44 -0
- package/artifacts/contracts/oapp/interfaces/IOAppCore.sol/IOAppCore.json +143 -0
- package/artifacts/contracts/oapp/interfaces/IOAppMsgInspector.sol/IOAppMsgInspector.json +51 -0
- package/artifacts/contracts/oapp/interfaces/IOAppOptionsType3.sol/IOAppOptionsType3.json +111 -0
- package/artifacts/contracts/oapp/libs/OAppOptionsType3.sol/OAppOptionsType3.json +187 -0
- package/artifacts/contracts/oapp/libs/OptionsBuilder.sol/OptionsBuilder.json +38 -0
- package/artifacts/contracts/oft/OFT.sol/OFT.json +1466 -0
- package/artifacts/contracts/oft/OFTAdapter.sol/OFTAdapter.json +1204 -0
- package/artifacts/contracts/oft/OFTCore.sol/OFTCore.json +1170 -0
- package/artifacts/contracts/oft/interfaces/IOFT.sol/IOFT.json +472 -0
- package/artifacts/contracts/oft/libs/OFTComposeMsgCodec.sol/OFTComposeMsgCodec.json +10 -0
- package/artifacts/contracts/oft/libs/OFTMsgCodec.sol/OFTMsgCodec.json +10 -0
- package/artifacts/contracts/precrime/OAppPreCrimeSimulator.sol/OAppPreCrimeSimulator.json +277 -0
- package/artifacts/contracts/precrime/PreCrime.sol/PreCrime.json +352 -0
- package/artifacts/contracts/precrime/extensions/PreCrimeE1.sol/PreCrimeE1.json +352 -0
- package/artifacts/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol/IOAppPreCrimeSimulator.json +175 -0
- package/artifacts/contracts/precrime/interfaces/IPreCrime.sol/IPreCrime.json +188 -0
- package/artifacts/contracts/precrime/libs/Packet.sol/PacketDecoder.json +10 -0
- package/contracts/oapp/OApp.sol +39 -0
- package/contracts/oapp/OAppCore.sol +68 -0
- package/contracts/oapp/OAppReceiver.sol +101 -0
- package/contracts/oapp/OAppSender.sol +124 -0
- package/contracts/oapp/examples/OmniCounter.sol +283 -0
- package/contracts/oapp/examples/OmniCounterPreCrime.sol +102 -0
- package/contracts/oapp/interfaces/IOAppComposer.sol +12 -0
- package/contracts/oapp/interfaces/IOAppCore.sol +51 -0
- package/contracts/oapp/interfaces/IOAppMsgInspector.sol +22 -0
- package/contracts/oapp/interfaces/IOAppOptionsType3.sol +43 -0
- package/contracts/oapp/libs/OAppOptionsType3.sol +82 -0
- package/contracts/oapp/libs/OptionsBuilder.sol +200 -0
- package/contracts/oft/OFT.sol +116 -0
- package/contracts/oft/OFTAdapter.sol +131 -0
- package/contracts/oft/OFTCore.sol +456 -0
- package/contracts/oft/OFTPrecrime.sol +99 -0
- package/contracts/oft/interfaces/IOFT.sol +168 -0
- package/contracts/oft/libs/OFTComposeMsgCodec.sol +91 -0
- package/contracts/oft/libs/OFTMsgCodec.sol +83 -0
- package/contracts/precrime/OAppPreCrimeSimulator.sol +125 -0
- package/contracts/precrime/PreCrime.sol +208 -0
- package/contracts/precrime/extensions/PreCrimeE1.sol +30 -0
- package/contracts/precrime/interfaces/IOAppPreCrimeSimulator.sol +55 -0
- package/contracts/precrime/interfaces/IPreCrime.sol +40 -0
- package/contracts/precrime/libs/Packet.sol +61 -0
- package/package.json +35 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { PreCrime, PreCrimePeer } from "../../precrime/PreCrime.sol";
|
|
6
|
+
import { InboundPacket } from "../../precrime/libs/Packet.sol";
|
|
7
|
+
import { OmniCounter } from "./OmniCounter.sol";
|
|
8
|
+
|
|
9
|
+
contract OmniCounterPreCrime is PreCrime {
|
|
10
|
+
struct ChainCount {
|
|
11
|
+
uint32 remoteEid;
|
|
12
|
+
uint256 inboundCount;
|
|
13
|
+
uint256 outboundCount;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor(address _endpoint, address _counter, address _owner) PreCrime(_endpoint, _counter, _owner) {}
|
|
17
|
+
|
|
18
|
+
function buildSimulationResult() external view override returns (bytes memory) {
|
|
19
|
+
address payable payableSimulator = payable(simulator);
|
|
20
|
+
OmniCounter counter = OmniCounter(payableSimulator);
|
|
21
|
+
ChainCount[] memory chainCounts = new ChainCount[](preCrimePeers.length);
|
|
22
|
+
for (uint256 i = 0; i < preCrimePeers.length; i++) {
|
|
23
|
+
uint32 remoteEid = preCrimePeers[i].eid;
|
|
24
|
+
chainCounts[i] = ChainCount(remoteEid, counter.inboundCount(remoteEid), counter.outboundCount(remoteEid));
|
|
25
|
+
}
|
|
26
|
+
return abi.encode(chainCounts);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _preCrime(
|
|
30
|
+
InboundPacket[] memory /** _packets */,
|
|
31
|
+
uint32[] memory _eids,
|
|
32
|
+
bytes[] memory _simulations
|
|
33
|
+
) internal view override {
|
|
34
|
+
uint32 localEid = _getLocalEid();
|
|
35
|
+
ChainCount[] memory localChainCounts;
|
|
36
|
+
|
|
37
|
+
// find local chain counts
|
|
38
|
+
for (uint256 i = 0; i < _eids.length; i++) {
|
|
39
|
+
if (_eids[i] == localEid) {
|
|
40
|
+
localChainCounts = abi.decode(_simulations[i], (ChainCount[]));
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// local against remote
|
|
46
|
+
for (uint256 i = 0; i < _eids.length; i++) {
|
|
47
|
+
uint32 remoteEid = _eids[i];
|
|
48
|
+
ChainCount[] memory remoteChainCounts = abi.decode(_simulations[i], (ChainCount[]));
|
|
49
|
+
(uint256 _inboundCount, ) = _findChainCounts(localChainCounts, remoteEid);
|
|
50
|
+
(, uint256 _outboundCount) = _findChainCounts(remoteChainCounts, localEid);
|
|
51
|
+
if (_inboundCount > _outboundCount) {
|
|
52
|
+
revert CrimeFound("inboundCount > outboundCount");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _findChainCounts(
|
|
58
|
+
ChainCount[] memory _chainCounts,
|
|
59
|
+
uint32 _remoteEid
|
|
60
|
+
) internal pure returns (uint256, uint256) {
|
|
61
|
+
for (uint256 i = 0; i < _chainCounts.length; i++) {
|
|
62
|
+
if (_chainCounts[i].remoteEid == _remoteEid) {
|
|
63
|
+
return (_chainCounts[i].inboundCount, _chainCounts[i].outboundCount);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return (0, 0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _getPreCrimePeers(
|
|
70
|
+
InboundPacket[] memory _packets
|
|
71
|
+
) internal view override returns (PreCrimePeer[] memory peers) {
|
|
72
|
+
PreCrimePeer[] memory allPeers = preCrimePeers;
|
|
73
|
+
PreCrimePeer[] memory peersTmp = new PreCrimePeer[](_packets.length);
|
|
74
|
+
|
|
75
|
+
int256 cursor = -1;
|
|
76
|
+
for (uint256 i = 0; i < _packets.length; i++) {
|
|
77
|
+
uint32 srcEid = _packets[i].origin.srcEid;
|
|
78
|
+
|
|
79
|
+
// push src eid & peer
|
|
80
|
+
int256 index = _indexOf(allPeers, srcEid);
|
|
81
|
+
if (index >= 0 && _indexOf(peersTmp, srcEid) < 0) {
|
|
82
|
+
cursor++;
|
|
83
|
+
peersTmp[uint256(cursor)] = allPeers[uint256(index)];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// copy to return
|
|
87
|
+
if (cursor >= 0) {
|
|
88
|
+
uint256 len = uint256(cursor) + 1;
|
|
89
|
+
peers = new PreCrimePeer[](len);
|
|
90
|
+
for (uint256 i = 0; i < len; i++) {
|
|
91
|
+
peers[i] = peersTmp[i];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function _indexOf(PreCrimePeer[] memory _peers, uint32 _eid) internal pure returns (int256) {
|
|
97
|
+
for (uint256 i = 0; i < _peers.length; i++) {
|
|
98
|
+
if (_peers[i].eid == _eid) return int256(i);
|
|
99
|
+
}
|
|
100
|
+
return -1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title IOAppComposer
|
|
9
|
+
* @dev This interface defines the OApp Composer, allowing developers to inherit only the OApp package without the protocol.
|
|
10
|
+
*/
|
|
11
|
+
// solhint-disable-next-line no-empty-blocks
|
|
12
|
+
interface IOAppComposer is ILayerZeroComposer {}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title IOAppCore
|
|
9
|
+
*/
|
|
10
|
+
interface IOAppCore {
|
|
11
|
+
// Custom error messages
|
|
12
|
+
error OnlyPeer(uint32 eid, bytes32 sender);
|
|
13
|
+
error NoPeer(uint32 eid);
|
|
14
|
+
error InvalidEndpointCall();
|
|
15
|
+
|
|
16
|
+
// Event emitted when a peer (OApp) is set for a corresponding endpoint
|
|
17
|
+
event PeerSet(uint32 eid, bytes32 peer);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @notice Retrieves the OApp version information.
|
|
21
|
+
* @return senderVersion The version of the OAppSender.sol contract.
|
|
22
|
+
* @return receiverVersion The version of the OAppReceiver.sol contract.
|
|
23
|
+
*/
|
|
24
|
+
function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @notice Retrieves the LayerZero endpoint associated with the OApp.
|
|
28
|
+
* @return iEndpoint The LayerZero endpoint as an interface.
|
|
29
|
+
*/
|
|
30
|
+
function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
|
|
34
|
+
* @param _eid The endpoint ID.
|
|
35
|
+
* @return peer The peer address (OApp instance) associated with the corresponding endpoint.
|
|
36
|
+
*/
|
|
37
|
+
function peers(uint32 _eid) external view returns (bytes32 peer);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
|
|
41
|
+
* @param _eid The endpoint ID.
|
|
42
|
+
* @param _peer The address of the peer to be associated with the corresponding endpoint.
|
|
43
|
+
*/
|
|
44
|
+
function setPeer(uint32 _eid, bytes32 _peer) external;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @notice Sets the delegate address for the OApp Core.
|
|
48
|
+
* @param _delegate The address of the delegate to be set.
|
|
49
|
+
*/
|
|
50
|
+
function setDelegate(address _delegate) external;
|
|
51
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @title IOAppMsgInspector
|
|
7
|
+
* @dev Interface for the OApp Message Inspector, allowing examination of message and options contents.
|
|
8
|
+
*/
|
|
9
|
+
interface IOAppMsgInspector {
|
|
10
|
+
// Custom error message for inspection failure
|
|
11
|
+
error InspectionFailed(bytes message, bytes options);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @notice Allows the inspector to examine LayerZero message contents and optionally throw a revert if invalid.
|
|
15
|
+
* @param _message The message payload to be inspected.
|
|
16
|
+
* @param _options Additional options or parameters for inspection.
|
|
17
|
+
* @return valid A boolean indicating whether the inspection passed (true) or failed (false).
|
|
18
|
+
*
|
|
19
|
+
* @dev Optionally done as a revert, OR use the boolean provided to handle the failure.
|
|
20
|
+
*/
|
|
21
|
+
function inspect(bytes calldata _message, bytes calldata _options) external view returns (bool valid);
|
|
22
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @dev Struct representing enforced option parameters.
|
|
7
|
+
*/
|
|
8
|
+
struct EnforcedOptionParam {
|
|
9
|
+
uint32 eid; // Endpoint ID
|
|
10
|
+
uint16 msgType; // Message Type
|
|
11
|
+
bytes options; // Additional options
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @title IOAppOptionsType3
|
|
16
|
+
* @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.
|
|
17
|
+
*/
|
|
18
|
+
interface IOAppOptionsType3 {
|
|
19
|
+
// Custom error message for invalid options
|
|
20
|
+
error InvalidOptions(bytes options);
|
|
21
|
+
|
|
22
|
+
// Event emitted when enforced options are set
|
|
23
|
+
event EnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice Sets enforced options for specific endpoint and message type combinations.
|
|
27
|
+
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
|
|
28
|
+
*/
|
|
29
|
+
function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @notice Combines options for a given endpoint and message type.
|
|
33
|
+
* @param _eid The endpoint ID.
|
|
34
|
+
* @param _msgType The OApp message type.
|
|
35
|
+
* @param _extraOptions Additional options passed by the caller.
|
|
36
|
+
* @return options The combination of caller specified options AND enforced options.
|
|
37
|
+
*/
|
|
38
|
+
function combineOptions(
|
|
39
|
+
uint32 _eid,
|
|
40
|
+
uint16 _msgType,
|
|
41
|
+
bytes calldata _extraOptions
|
|
42
|
+
) external view returns (bytes memory options);
|
|
43
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
|
6
|
+
import { IOAppOptionsType3, EnforcedOptionParam } from "../interfaces/IOAppOptionsType3.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title OAppOptionsType3
|
|
10
|
+
* @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.
|
|
11
|
+
*/
|
|
12
|
+
abstract contract OAppOptionsType3 is IOAppOptionsType3, Ownable {
|
|
13
|
+
uint16 internal constant OPTION_TYPE_3 = 3;
|
|
14
|
+
|
|
15
|
+
// @dev The "msgType" should be defined in the child contract.
|
|
16
|
+
mapping(uint32 eid => mapping(uint16 msgType => bytes enforcedOption)) public enforcedOptions;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @dev Sets the enforced options for specific endpoint and message type combinations.
|
|
20
|
+
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
|
|
21
|
+
*
|
|
22
|
+
* @dev Only the owner/admin of the OApp can call this function.
|
|
23
|
+
* @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
|
|
24
|
+
* @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
|
|
25
|
+
* eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
|
|
26
|
+
* if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
|
|
27
|
+
*/
|
|
28
|
+
function setEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) public virtual onlyOwner {
|
|
29
|
+
for (uint256 i = 0; i < _enforcedOptions.length; i++) {
|
|
30
|
+
// @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.
|
|
31
|
+
_assertOptionsType3(_enforcedOptions[i].options);
|
|
32
|
+
enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
emit EnforcedOptionSet(_enforcedOptions);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @notice Combines options for a given endpoint and message type.
|
|
40
|
+
* @param _eid The endpoint ID.
|
|
41
|
+
* @param _msgType The OAPP message type.
|
|
42
|
+
* @param _extraOptions Additional options passed by the caller.
|
|
43
|
+
* @return options The combination of caller specified options AND enforced options.
|
|
44
|
+
*
|
|
45
|
+
* @dev If there is an enforced lzReceive option:
|
|
46
|
+
* - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}
|
|
47
|
+
* - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.
|
|
48
|
+
* @dev This presence of duplicated options is handled off-chain in the verifier/executor.
|
|
49
|
+
*/
|
|
50
|
+
function combineOptions(
|
|
51
|
+
uint32 _eid,
|
|
52
|
+
uint16 _msgType,
|
|
53
|
+
bytes calldata _extraOptions
|
|
54
|
+
) public view virtual returns (bytes memory) {
|
|
55
|
+
bytes memory enforced = enforcedOptions[_eid][_msgType];
|
|
56
|
+
|
|
57
|
+
// No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.
|
|
58
|
+
if (enforced.length == 0) return _extraOptions;
|
|
59
|
+
|
|
60
|
+
// No caller options, return enforced
|
|
61
|
+
if (_extraOptions.length == 0) return enforced;
|
|
62
|
+
|
|
63
|
+
// @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.
|
|
64
|
+
if (_extraOptions.length >= 2) {
|
|
65
|
+
_assertOptionsType3(_extraOptions);
|
|
66
|
+
// @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.
|
|
67
|
+
return bytes.concat(enforced, _extraOptions[2:]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// No valid set of options was found.
|
|
71
|
+
revert InvalidOptions(_extraOptions);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @dev Internal function to assert that options are of type 3.
|
|
76
|
+
* @param _options The options to be checked.
|
|
77
|
+
*/
|
|
78
|
+
function _assertOptionsType3(bytes calldata _options) internal pure virtual {
|
|
79
|
+
uint16 optionsType = uint16(bytes2(_options[0:2]));
|
|
80
|
+
if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol";
|
|
6
|
+
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
|
7
|
+
|
|
8
|
+
import { ExecutorOptions } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol";
|
|
9
|
+
import { DVNOptions } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @title OptionsBuilder
|
|
13
|
+
* @dev Library for building and encoding various message options.
|
|
14
|
+
*/
|
|
15
|
+
library OptionsBuilder {
|
|
16
|
+
using SafeCast for uint256;
|
|
17
|
+
using BytesLib for bytes;
|
|
18
|
+
|
|
19
|
+
// Constants for options types
|
|
20
|
+
uint16 internal constant TYPE_1 = 1; // legacy options type 1
|
|
21
|
+
uint16 internal constant TYPE_2 = 2; // legacy options type 2
|
|
22
|
+
uint16 internal constant TYPE_3 = 3;
|
|
23
|
+
|
|
24
|
+
// Custom error message
|
|
25
|
+
error InvalidSize(uint256 max, uint256 actual);
|
|
26
|
+
error InvalidOptionType(uint16 optionType);
|
|
27
|
+
|
|
28
|
+
// Modifier to ensure only options of type 3 are used
|
|
29
|
+
modifier onlyType3(bytes memory _options) {
|
|
30
|
+
if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));
|
|
31
|
+
_;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @dev Creates a new options container with type 3.
|
|
36
|
+
* @return options The newly created options container.
|
|
37
|
+
*/
|
|
38
|
+
function newOptions() internal pure returns (bytes memory) {
|
|
39
|
+
return abi.encodePacked(TYPE_3);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @dev Adds an executor LZ receive option to the existing options.
|
|
44
|
+
* @param _options The existing options container.
|
|
45
|
+
* @param _gas The gasLimit used on the lzReceive() function in the OApp.
|
|
46
|
+
* @param _value The msg.value passed to the lzReceive() function in the OApp.
|
|
47
|
+
* @return options The updated options container.
|
|
48
|
+
*
|
|
49
|
+
* @dev When multiples of this option are added, they are summed by the executor
|
|
50
|
+
* eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,
|
|
51
|
+
* that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.
|
|
52
|
+
*/
|
|
53
|
+
function addExecutorLzReceiveOption(
|
|
54
|
+
bytes memory _options,
|
|
55
|
+
uint128 _gas,
|
|
56
|
+
uint128 _value
|
|
57
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
58
|
+
bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);
|
|
59
|
+
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @dev Adds an executor native drop option to the existing options.
|
|
64
|
+
* @param _options The existing options container.
|
|
65
|
+
* @param _amount The amount for the native value that is airdropped to the 'receiver'.
|
|
66
|
+
* @param _receiver The receiver address for the native drop option.
|
|
67
|
+
* @return options The updated options container.
|
|
68
|
+
*
|
|
69
|
+
* @dev When multiples of this option are added, they are summed by the executor on the remote chain.
|
|
70
|
+
*/
|
|
71
|
+
function addExecutorNativeDropOption(
|
|
72
|
+
bytes memory _options,
|
|
73
|
+
uint128 _amount,
|
|
74
|
+
bytes32 _receiver
|
|
75
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
76
|
+
bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);
|
|
77
|
+
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @dev Adds an executor LZ compose option to the existing options.
|
|
82
|
+
* @param _options The existing options container.
|
|
83
|
+
* @param _index The index for the lzCompose() function call.
|
|
84
|
+
* @param _gas The gasLimit for the lzCompose() function call.
|
|
85
|
+
* @param _value The msg.value for the lzCompose() function call.
|
|
86
|
+
* @return options The updated options container.
|
|
87
|
+
*
|
|
88
|
+
* @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.
|
|
89
|
+
* @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.
|
|
90
|
+
* ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2
|
|
91
|
+
*/
|
|
92
|
+
function addExecutorLzComposeOption(
|
|
93
|
+
bytes memory _options,
|
|
94
|
+
uint16 _index,
|
|
95
|
+
uint128 _gas,
|
|
96
|
+
uint128 _value
|
|
97
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
98
|
+
bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);
|
|
99
|
+
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @dev Adds an executor ordered execution option to the existing options.
|
|
104
|
+
* @param _options The existing options container.
|
|
105
|
+
* @return options The updated options container.
|
|
106
|
+
*/
|
|
107
|
+
function addExecutorOrderedExecutionOption(
|
|
108
|
+
bytes memory _options
|
|
109
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
110
|
+
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(""));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @dev Adds a DVN pre-crime option to the existing options.
|
|
115
|
+
* @param _options The existing options container.
|
|
116
|
+
* @param _dvnIdx The DVN index for the pre-crime option.
|
|
117
|
+
* @return options The updated options container.
|
|
118
|
+
*/
|
|
119
|
+
function addDVNPreCrimeOption(
|
|
120
|
+
bytes memory _options,
|
|
121
|
+
uint8 _dvnIdx
|
|
122
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
123
|
+
return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(""));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @dev Adds an executor option to the existing options.
|
|
128
|
+
* @param _options The existing options container.
|
|
129
|
+
* @param _optionType The type of the executor option.
|
|
130
|
+
* @param _option The encoded data for the executor option.
|
|
131
|
+
* @return options The updated options container.
|
|
132
|
+
*/
|
|
133
|
+
function addExecutorOption(
|
|
134
|
+
bytes memory _options,
|
|
135
|
+
uint8 _optionType,
|
|
136
|
+
bytes memory _option
|
|
137
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
138
|
+
return
|
|
139
|
+
abi.encodePacked(
|
|
140
|
+
_options,
|
|
141
|
+
ExecutorOptions.WORKER_ID,
|
|
142
|
+
_option.length.toUint16() + 1, // +1 for optionType
|
|
143
|
+
_optionType,
|
|
144
|
+
_option
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @dev Adds a DVN option to the existing options.
|
|
150
|
+
* @param _options The existing options container.
|
|
151
|
+
* @param _dvnIdx The DVN index for the DVN option.
|
|
152
|
+
* @param _optionType The type of the DVN option.
|
|
153
|
+
* @param _option The encoded data for the DVN option.
|
|
154
|
+
* @return options The updated options container.
|
|
155
|
+
*/
|
|
156
|
+
function addDVNOption(
|
|
157
|
+
bytes memory _options,
|
|
158
|
+
uint8 _dvnIdx,
|
|
159
|
+
uint8 _optionType,
|
|
160
|
+
bytes memory _option
|
|
161
|
+
) internal pure onlyType3(_options) returns (bytes memory) {
|
|
162
|
+
return
|
|
163
|
+
abi.encodePacked(
|
|
164
|
+
_options,
|
|
165
|
+
DVNOptions.WORKER_ID,
|
|
166
|
+
_option.length.toUint16() + 2, // +2 for optionType and dvnIdx
|
|
167
|
+
_dvnIdx,
|
|
168
|
+
_optionType,
|
|
169
|
+
_option
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @dev Encodes legacy options of type 1.
|
|
175
|
+
* @param _executionGas The gasLimit value passed to lzReceive().
|
|
176
|
+
* @return legacyOptions The encoded legacy options.
|
|
177
|
+
*/
|
|
178
|
+
function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {
|
|
179
|
+
if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);
|
|
180
|
+
return abi.encodePacked(TYPE_1, _executionGas);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @dev Encodes legacy options of type 2.
|
|
185
|
+
* @param _executionGas The gasLimit value passed to lzReceive().
|
|
186
|
+
* @param _nativeForDst The amount of native air dropped to the receiver.
|
|
187
|
+
* @param _receiver The _nativeForDst receiver address.
|
|
188
|
+
* @return legacyOptions The encoded legacy options of type 2.
|
|
189
|
+
*/
|
|
190
|
+
function encodeLegacyOptionsType2(
|
|
191
|
+
uint256 _executionGas,
|
|
192
|
+
uint256 _nativeForDst,
|
|
193
|
+
bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.
|
|
194
|
+
) internal pure returns (bytes memory) {
|
|
195
|
+
if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);
|
|
196
|
+
if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);
|
|
197
|
+
if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);
|
|
198
|
+
return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
6
|
+
import { OFTCore } from "./OFTCore.sol";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @title OFT Contract
|
|
10
|
+
* @dev OFT is an ERC-20 token that extends the functionality of the OFTCore contract.
|
|
11
|
+
*/
|
|
12
|
+
contract OFT is OFTCore, ERC20 {
|
|
13
|
+
/**
|
|
14
|
+
* @dev Constructor for the OFT contract.
|
|
15
|
+
* @param _name The name of the OFT.
|
|
16
|
+
* @param _symbol The symbol of the OFT.
|
|
17
|
+
* @param _lzEndpoint The LayerZero endpoint address.
|
|
18
|
+
* @param _owner The owner of the contract.
|
|
19
|
+
*/
|
|
20
|
+
constructor(
|
|
21
|
+
string memory _name,
|
|
22
|
+
string memory _symbol,
|
|
23
|
+
address _lzEndpoint,
|
|
24
|
+
address _owner
|
|
25
|
+
) ERC20(_name, _symbol) OFTCore(decimals(), _lzEndpoint, _owner) {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @dev Retrieves the OFT contract version.
|
|
29
|
+
* @return major The major version.
|
|
30
|
+
* @return minor The minor version.
|
|
31
|
+
*
|
|
32
|
+
* @dev major version: Indicates a cross-chain compatible msg encoding with other OFTs.
|
|
33
|
+
* @dev minor version: Indicates a version within the local chains context. eg. OFTAdapter vs. OFT
|
|
34
|
+
* @dev For example, if a new feature is added to the OFT contract, the minor version will be incremented.
|
|
35
|
+
* @dev If a new feature is added to the OFT cross-chain msg encoding, the major version will be incremented.
|
|
36
|
+
* ie. localOFT version(1,1) CAN send messages to remoteOFT version(1,2)
|
|
37
|
+
*/
|
|
38
|
+
function oftVersion() external pure returns (uint64 major, uint64 minor) {
|
|
39
|
+
return (1, 1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @dev Retrieves the address of the underlying ERC20 implementation.
|
|
44
|
+
* @return The address of the OFT token.
|
|
45
|
+
*
|
|
46
|
+
* @dev In the case of OFT, address(this) and erc20 are the same contract.
|
|
47
|
+
*/
|
|
48
|
+
function token() external view returns (address) {
|
|
49
|
+
return address(this);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @dev Burns tokens from the sender's specified balance.
|
|
54
|
+
* @param _amountToSendLD The amount of tokens to send in local decimals.
|
|
55
|
+
* @param _minAmountToCreditLD The minimum amount to credit in local decimals.
|
|
56
|
+
* @param _dstEid The destination chain ID.
|
|
57
|
+
* @return amountDebitedLD The amount of tokens ACTUALLY debited in local decimals.
|
|
58
|
+
* @return amountToCreditLD The amount of tokens to credit in local decimals.
|
|
59
|
+
*/
|
|
60
|
+
function _debitSender(
|
|
61
|
+
uint256 _amountToSendLD,
|
|
62
|
+
uint256 _minAmountToCreditLD,
|
|
63
|
+
uint32 _dstEid
|
|
64
|
+
) internal virtual override returns (uint256 amountDebitedLD, uint256 amountToCreditLD) {
|
|
65
|
+
(amountDebitedLD, amountToCreditLD) = _debitView(_amountToSendLD, _minAmountToCreditLD, _dstEid);
|
|
66
|
+
|
|
67
|
+
// @dev In NON-default OFT, amountDebited could be 100, with a 10% fee, the credited amount is 90,
|
|
68
|
+
// therefore amountDebited CAN differ from amountToCredit.
|
|
69
|
+
|
|
70
|
+
// @dev Default OFT burns on src.
|
|
71
|
+
_burn(msg.sender, amountDebitedLD);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @dev Burns tokens that have been sent into this contract.
|
|
76
|
+
* @param _minAmountToReceiveLD The minimum amount to receive in local decimals.
|
|
77
|
+
* @param _dstEid The destination chain ID.
|
|
78
|
+
* @return amountDebitedLD The amount of tokens ACTUALLY debited in local decimals.
|
|
79
|
+
* @return amountToCreditLD The amount of tokens to credit in local decimals.
|
|
80
|
+
*/
|
|
81
|
+
function _debitThis(
|
|
82
|
+
uint256 _minAmountToReceiveLD,
|
|
83
|
+
uint32 _dstEid
|
|
84
|
+
) internal virtual override returns (uint256 amountDebitedLD, uint256 amountToCreditLD) {
|
|
85
|
+
// @dev This is the push method, where at any point in the transaction, the OFT receives tokens and they can be sent by the caller.
|
|
86
|
+
// @dev This SHOULD be done atomically, otherwise any caller can spend tokens that are owned by the contract.
|
|
87
|
+
// @dev In the NON-default case where fees are stored in the contract, there should be a value reserved via a global state.
|
|
88
|
+
// eg. balanceOf(address(this)) - accruedFees;
|
|
89
|
+
(amountDebitedLD, amountToCreditLD) = _debitView(balanceOf(address(this)), _minAmountToReceiveLD, _dstEid);
|
|
90
|
+
|
|
91
|
+
// @dev Default OFT burns on src.
|
|
92
|
+
_burn(address(this), amountDebitedLD);
|
|
93
|
+
|
|
94
|
+
// @dev When sending tokens direct to the OFT contract,
|
|
95
|
+
// there is NOT a default mechanism to capture the dust that MIGHT get left in the contract.
|
|
96
|
+
// If you want to refund this dust, will need to add another function to return it.
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @dev Credits tokens to the specified address.
|
|
101
|
+
* @param _to The address to credit the tokens to.
|
|
102
|
+
* @param _amountToCreditLD The amount of tokens to credit in local decimals.
|
|
103
|
+
* @dev _srcEid The source chain ID.
|
|
104
|
+
* @return amountReceivedLD The amount of tokens ACTUALLY received in local decimals.
|
|
105
|
+
*/
|
|
106
|
+
function _credit(
|
|
107
|
+
address _to,
|
|
108
|
+
uint256 _amountToCreditLD,
|
|
109
|
+
uint32 /*_srcEid*/
|
|
110
|
+
) internal virtual override returns (uint256 amountReceivedLD) {
|
|
111
|
+
// @dev Default OFT mints on dst.
|
|
112
|
+
_mint(_to, _amountToCreditLD);
|
|
113
|
+
// @dev In the case of NON-default OFT, the amountToCreditLD MIGHT not == amountReceivedLD.
|
|
114
|
+
return _amountToCreditLD;
|
|
115
|
+
}
|
|
116
|
+
}
|