@web3dotorg/evm-slc-core-contracts 0.3.15 → 0.3.17

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.
@@ -0,0 +1,192 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
5
+ import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
6
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
7
+ import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
8
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
9
+
10
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
11
+
12
+ import {AMultiOwnable} from "@solarity/solidity-lib/access/AMultiOwnable.sol";
13
+
14
+ import {IGovernance} from "../interfaces/IGovernance.sol";
15
+
16
+ /**
17
+ * @title QCoreTreasury
18
+ * @notice Minimal multi-owner upgradeable treasury for managing native currency, ERC20, and ERC721 assets.
19
+ * @dev Owners can pull/send assets and execute arbitrary operations (call/delegatecall). Delegatecall is powerful
20
+ * and should be used with extreme care as it executes in this contract's context.
21
+ */
22
+ contract QCoreTreasury is ERC165, UUPSUpgradeable, AMultiOwnable {
23
+ using SafeERC20 for IERC20;
24
+
25
+ address private constant ETHEREUM_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
26
+
27
+ event NFTSent(address indexed token, address indexed to, uint256 tokenId);
28
+ event TokensSent(address indexed token, address indexed to, uint256 amount);
29
+ event NFTDeposited(address indexed token, address indexed from, uint256 tokenId);
30
+ event TokensDeposited(address indexed token, address indexed from, uint256 amount);
31
+ event ArbitraryExecute(
32
+ IGovernance.OperationType operation,
33
+ address to,
34
+ bytes data,
35
+ uint256 value
36
+ );
37
+
38
+ error FailedToExecuteArbitraryCall(
39
+ IGovernance.OperationType operation,
40
+ address to,
41
+ bytes data,
42
+ uint256 value
43
+ );
44
+
45
+ /**
46
+ * @notice Disables initializers on implementation.
47
+ */
48
+ constructor() {
49
+ _disableInitializers();
50
+ }
51
+
52
+ /**
53
+ * @notice Receive native currency and emit a deposit event.
54
+ */
55
+ receive() external payable {
56
+ depositNative();
57
+ }
58
+
59
+ /**
60
+ * @notice Initialize the contract with initial owners.
61
+ * @param initialOwners_ Array of initial owner addresses.
62
+ */
63
+ function __QCoreTreasury_init(address[] calldata initialOwners_) external initializer {
64
+ __AMultiOwnable_init(initialOwners_);
65
+ }
66
+
67
+ /**
68
+ * @notice Deposit native currency to the treasury.
69
+ */
70
+ function depositNative() public payable virtual {
71
+ emit TokensDeposited(ETHEREUM_ADDRESS, msg.sender, msg.value);
72
+ }
73
+
74
+ /**
75
+ * @notice Deposit ERC20 tokens to the treasury.
76
+ * @param tokenAddress_ ERC20 token address.
77
+ * @param amount_ Token amount to deposit.
78
+ */
79
+ function depositERC20(address tokenAddress_, uint256 amount_) public virtual {
80
+ IERC20(tokenAddress_).safeTransferFrom(msg.sender, address(this), amount_);
81
+
82
+ emit TokensDeposited(tokenAddress_, msg.sender, amount_);
83
+ }
84
+
85
+ /**
86
+ * @notice Deposit ERC721 token to the treasury.
87
+ * @param tokenAddress_ ERC721 token address.
88
+ * @param tokenId_ Token ID to deposit.
89
+ * @dev Uses transferFrom on purpose to accept tokens without ERC721Receiver.
90
+ */
91
+ function depositNFT(address tokenAddress_, uint256 tokenId_) public {
92
+ IERC721(tokenAddress_).transferFrom(msg.sender, address(this), tokenId_);
93
+
94
+ emit NFTDeposited(tokenAddress_, msg.sender, tokenId_);
95
+ }
96
+
97
+ /**
98
+ * @notice Send native currency from the treasury.
99
+ * @param to_ Recipient address.
100
+ * @param amount_ Amount in wei.
101
+ */
102
+ function sendNative(address to_, uint256 amount_) public onlyOwner {
103
+ Address.sendValue(payable(to_), amount_);
104
+
105
+ emit TokensSent(ETHEREUM_ADDRESS, to_, amount_);
106
+ }
107
+
108
+ /**
109
+ * @notice Send ERC20 tokens from the treasury.
110
+ * @param tokenAddress_ ERC20 token address.
111
+ * @param to_ Recipient address.
112
+ * @param amount_ Token amount to send.
113
+ */
114
+ function sendERC20(address tokenAddress_, address to_, uint256 amount_) public onlyOwner {
115
+ IERC20(tokenAddress_).safeTransfer(to_, amount_);
116
+
117
+ emit TokensSent(tokenAddress_, to_, amount_);
118
+ }
119
+
120
+ /**
121
+ * @notice Send ERC721 token from the treasury.
122
+ * @param tokenAddress_ ERC721 token address.
123
+ * @param to_ Recipient address.
124
+ * @param tokenId_ Token ID to send.
125
+ * @dev Uses safeTransferFrom to prevent sending NFTs to non-receivers.
126
+ */
127
+ function sendNFT(address tokenAddress_, address to_, uint256 tokenId_) public onlyOwner {
128
+ IERC721(tokenAddress_).safeTransferFrom(address(this), to_, tokenId_);
129
+
130
+ emit NFTSent(tokenAddress_, to_, tokenId_);
131
+ }
132
+
133
+ /* solhint-disable-next-line gas-calldata-parameters */
134
+ /**
135
+ * @notice Execute arbitrary operation (call or delegatecall).
136
+ * @param operation_ Operation type.
137
+ * @param to_ Target address.
138
+ * @param data_ Calldata to execute.
139
+ * @param value_ ETH value to send with call (ignored for delegatecall).
140
+ * @return success True if the low-level operation succeeded.
141
+ */
142
+ function arbitraryExecute(
143
+ IGovernance.OperationType operation_,
144
+ address to_,
145
+ bytes memory data_,
146
+ uint256 value_
147
+ ) external virtual onlyOwner returns (bool success) {
148
+ if (data_.length == 0 && value_ == 0) return true;
149
+
150
+ if (operation_ == IGovernance.OperationType.DelegateCall) {
151
+ /* solhint-disable-next-line no-inline-assembly */
152
+ assembly ("memory-safe") {
153
+ success := delegatecall(gas(), to_, add(data_, 0x20), mload(data_), 0, 0)
154
+ }
155
+ } else {
156
+ /* solhint-disable-next-line no-inline-assembly */
157
+ assembly ("memory-safe") {
158
+ success := call(gas(), to_, value_, add(data_, 0x20), mload(data_), 0, 0)
159
+ }
160
+ }
161
+
162
+ require(success, FailedToExecuteArbitraryCall(operation_, to_, data_, value_));
163
+
164
+ emit ArbitraryExecute(operation_, to_, data_, value_);
165
+ }
166
+
167
+ /**
168
+ * @notice Get native currency balance held by the treasury.
169
+ */
170
+ function getNativeBalance() public view returns (uint256) {
171
+ return address(this).balance;
172
+ }
173
+
174
+ /**
175
+ * @notice Get ERC20 token balance held by the treasury.
176
+ * @param tokenAddress_ ERC20 token address.
177
+ */
178
+ function getERC20Balance(address tokenAddress_) public view returns (uint256) {
179
+ return IERC20(tokenAddress_).balanceOf(address(this));
180
+ }
181
+
182
+ /**
183
+ * @notice Get ERC721 token balance (number of tokens) held by the treasury.
184
+ * @param tokenAddress_ ERC721 token address.
185
+ */
186
+ function getNFTBalance(address tokenAddress_) public view returns (uint256) {
187
+ return IERC721(tokenAddress_).balanceOf(address(this));
188
+ }
189
+
190
+ // solhint-disable-next-line no-empty-blocks
191
+ function _authorizeUpgrade(address newImplementation_) internal override onlyOwner {}
192
+ }
@@ -286,6 +286,7 @@ contract ExecutorsRegistry is IExecutorsRegistry, ERC165, UUPSUpgradeable, AValu
286
286
  executorInfo.stake -= amount_;
287
287
 
288
288
  _manipulateExecutorState(executor_);
289
+ _tryActivateFromStandby();
289
290
 
290
291
  $.stakeToken.safeTransfer(slashingRecipient_, amount_);
291
292
 
@@ -484,6 +485,7 @@ contract ExecutorsRegistry is IExecutorsRegistry, ERC165, UUPSUpgradeable, AValu
484
485
 
485
486
  if (!executorInfo.isApproved) return;
486
487
  if (executorInfo.stake < _getMinimumStake()) return;
488
+ if (executorInfo.isActive) return;
487
489
 
488
490
  if ($.activeExecutors.length() < $.maxActiveExecutors) {
489
491
  _activateExecutor(executor_);
@@ -702,7 +704,7 @@ contract ExecutorsRegistry is IExecutorsRegistry, ERC165, UUPSUpgradeable, AValu
702
704
  }
703
705
 
704
706
  _addToStandby(executor_);
705
- _tryActivateFromStandby();
707
+ _tryActivateExecutor(executor_);
706
708
 
707
709
  _updateExecutorShares(executor_);
708
710
  }
@@ -1,7 +1,6 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.28;
3
3
 
4
- import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
5
4
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6
5
  /* solhint-disable-next-line no-unused-import */
7
6
  import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";
@@ -17,13 +16,11 @@ import {Paginator} from "@solarity/solidity-lib/libs/arrays/Paginator.sol";
17
16
 
18
17
  import {Errors} from "../libs/Errors.sol";
19
18
  import {IWrappedToken} from "../interfaces/IWrappedToken.sol";
20
- import {ISLCCore} from "../interfaces/ISLCCore.sol";
21
19
  import {IGovernance} from "../interfaces/IGovernance.sol";
22
20
  import {IQParameters} from "../interfaces/IQParameters.sol";
23
21
  import {INeutralsRegistry} from "../interfaces/INeutralsRegistry.sol";
24
22
  import {IExecutorsRegistry} from "../interfaces/IExecutorsRegistry.sol";
25
23
  import {GovernanceUtils} from "../libs/GovernanceUtils.sol";
26
- import {DAOSLC_PARAM_NAME, TREASURY_ADDRESS_PARAM_NAME} from "../utils/Globals.sol";
27
24
 
28
25
  import * as Globals from "../utils/Globals.sol";
29
26
 
@@ -67,6 +64,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
67
64
  string additionalLink;
68
65
  EnumerableSet.AddressSet selectedNeutrals;
69
66
  bytes data;
67
+ uint256 createdAt;
70
68
  VotingPhase votingPhase;
71
69
  bytes32 leadingOperationHash;
72
70
  // Proposals
@@ -82,6 +80,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
82
80
  string additionalLink;
83
81
  address[] selectedNeutrals;
84
82
  bytes data;
83
+ uint256 createdAt;
85
84
  VotingPhase votingPhase;
86
85
  bytes32 leadingOperationHash;
87
86
  // Proposals
@@ -192,6 +191,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
192
191
  requestState.neutralsNumber = neutralsNumber_;
193
192
  requestState.additionalLink = additionalLink_;
194
193
  requestState.data = data_;
194
+ requestState.createdAt = block.timestamp;
195
195
  for (uint256 i = 0; i < selectedNeutrals_.length; ++i) {
196
196
  requestState.selectedNeutrals.add(selectedNeutrals_[i]);
197
197
  }
@@ -335,6 +335,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
335
335
  additionalLink: requestState.additionalLink,
336
336
  selectedNeutrals: requestState.selectedNeutrals.values(),
337
337
  data: requestState.data,
338
+ createdAt: requestState.createdAt,
338
339
  votingPhase: requestState.votingPhase,
339
340
  leadingOperationHash: requestState.leadingOperationHash,
340
341
  requestNonce: requestState.proposalNonce,
@@ -4,7 +4,6 @@ pragma solidity ^0.8.28;
4
4
  import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
5
5
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6
6
  import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
7
- import {GovernorUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";
8
7
 
9
8
  import {ISLCCore} from "../interfaces/ISLCCore.sol";
10
9
  import {Governance} from "../governance/Governance.sol";
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
5
+
6
+ contract MockERC721 is ERC721 {
7
+ constructor() ERC721("Mock NFT", "MNFT") {}
8
+
9
+ function mint(address to_, uint256 tokenId_) external {
10
+ _mint(to_, tokenId_);
11
+ }
12
+ }
package/mocks/Noop.sol ADDED
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ contract Noop {
5
+ function doNothing() external {}
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web3dotorg/evm-slc-core-contracts",
3
- "version": "0.3.15",
3
+ "version": "0.3.17",
4
4
  "author": "<TBD>",
5
5
  "repository": {
6
6
  "type": "git",