@web3dotorg/evm-slc-core-contracts 0.3.14 → 0.3.16

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.
@@ -41,6 +41,7 @@ contract ExecutorsRegistry is IExecutorsRegistry, ERC165, UUPSUpgradeable, AValu
41
41
  bool isApproved;
42
42
  bool isActive;
43
43
  bool isStandby;
44
+ string externalLink;
44
45
  }
45
46
 
46
47
  struct WithdrawalAnnouncement {
@@ -320,6 +321,19 @@ contract ExecutorsRegistry is IExecutorsRegistry, ERC165, UUPSUpgradeable, AValu
320
321
  emit RewardsClaimed(msg.sender, rewards_);
321
322
  }
322
323
 
324
+ /**
325
+ * @notice Set an external link for the executor (e.g., profile or info page)
326
+ * @param externalLink_ The external link string
327
+ */
328
+ function setExternalLink(string calldata externalLink_) external {
329
+ ExecutorsRegistryStorage storage $ = _getExecutorsRegistryStorage();
330
+ ExecutorInfo storage executorInfo = $.executors[msg.sender];
331
+
332
+ if (executorInfo.stake == 0) revert ExecutorHasNoStake(msg.sender);
333
+
334
+ executorInfo.externalLink = externalLink_;
335
+ }
336
+
323
337
  /**
324
338
  * @notice Synchronize local maxActiveExecutors parameter with the one in the registry
325
339
  */
@@ -362,6 +376,15 @@ contract ExecutorsRegistry is IExecutorsRegistry, ERC165, UUPSUpgradeable, AValu
362
376
  return _getExecutorsRegistryStorage().executors[executor_];
363
377
  }
364
378
 
379
+ /**
380
+ * @notice Get the external link set by an executor
381
+ * @param executor_ Address of the executor
382
+ * @return The external link string
383
+ */
384
+ function getExternalLink(address executor_) external view returns (string memory) {
385
+ return _getExecutorsRegistryStorage().executors[executor_].externalLink;
386
+ }
387
+
365
388
  /**
366
389
  * @notice Get active executors list with pagination
367
390
  * @param offset_ Starting index
@@ -22,11 +22,13 @@ import {IGovernance} from "../interfaces/IGovernance.sol";
22
22
  import {IQParameters} from "../interfaces/IQParameters.sol";
23
23
  import {INeutralsRegistry} from "../interfaces/INeutralsRegistry.sol";
24
24
  import {IExecutorsRegistry} from "../interfaces/IExecutorsRegistry.sol";
25
+ import {GovernanceUtils} from "../libs/GovernanceUtils.sol";
25
26
  import {DAOSLC_PARAM_NAME, TREASURY_ADDRESS_PARAM_NAME} from "../utils/Globals.sol";
26
27
 
28
+ import * as Globals from "../utils/Globals.sol";
29
+
27
30
  contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
28
31
  using EnumerableSet for EnumerableSet.AddressSet;
29
-
30
32
  using SafeERC20 for IWrappedToken;
31
33
 
32
34
  // keccak256(abi.encode(uint256(keccak256("evm-slc-core.storage.Governance")) - 1)) & ~bytes32(uint256(0xff))
@@ -36,30 +38,6 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
36
38
  bytes32 private constant GOVERNOR_STORAGE_LOCATION =
37
39
  0x7c712897014dbe49c045ef1299aa2d5f9e67e48eea4403efa21f1e0f3ac0cb00;
38
40
 
39
- string private constant VOTING_DELAY_PARAM_NAME = "slc.governance.votingDelay";
40
-
41
- // solhint-disable-next-line gas-small-strings
42
- string private constant NEUTRALS_VOTING_PERIOD_PARAM_NAME =
43
- "slc.governance.neutralsVotingPeriod";
44
-
45
- // solhint-disable-next-line gas-small-strings
46
- string private constant EXECUTORS_VOTING_PERIOD_PARAM_NAME =
47
- "slc.governance.executorsVotingPeriod";
48
-
49
- // solhint-disable-next-line gas-small-strings
50
- string private constant NEUTRALS_THRESHOLD_PARAM_NAME = "slc.governance.neutralsThreshold";
51
-
52
- // solhint-disable-next-line gas-small-strings
53
- string private constant EXECUTORS_THRESHOLD_PARAM_NAME = "slc.governance.executorsThreshold";
54
-
55
- string private constant EXECUTORS_SHARE_PARAM_NAME = "slc.governance.executorsShare";
56
-
57
- string private constant NEUTRALS_SHARE_PARAM_NAME = "slc.governance.neutralsShare";
58
-
59
- string private constant TREASURY_SHARE_PARAM_NAME = "slc.governance.treasuryShare";
60
-
61
- string private constant MIN_SERVICE_FEE_PARAM_NAME = "slc.governance.minServiceFee";
62
-
63
41
  enum VotingPhase {
64
42
  None,
65
43
  Neutrals,
@@ -89,6 +67,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
89
67
  string additionalLink;
90
68
  EnumerableSet.AddressSet selectedNeutrals;
91
69
  bytes data;
70
+ uint256 createdAt;
92
71
  VotingPhase votingPhase;
93
72
  bytes32 leadingOperationHash;
94
73
  // Proposals
@@ -104,6 +83,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
104
83
  string additionalLink;
105
84
  address[] selectedNeutrals;
106
85
  bytes data;
86
+ uint256 createdAt;
107
87
  VotingPhase votingPhase;
108
88
  bytes32 leadingOperationHash;
109
89
  // Proposals
@@ -214,6 +194,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
214
194
  requestState.neutralsNumber = neutralsNumber_;
215
195
  requestState.additionalLink = additionalLink_;
216
196
  requestState.data = data_;
197
+ requestState.createdAt = block.timestamp;
217
198
  for (uint256 i = 0; i < selectedNeutrals_.length; ++i) {
218
199
  requestState.selectedNeutrals.add(selectedNeutrals_[i]);
219
200
  }
@@ -229,41 +210,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
229
210
  uint256 proposalId_,
230
211
  OperationBundle calldata operationBundle_
231
212
  ) external {
232
- GovernanceStorage storage $ = _getGovernanceStorage();
233
-
234
- ServiceRequestState storage requestState = $
235
- .slcGovernanceState[$.proposalState[proposalId_].slcCore]
236
- .requestState[$.proposalState[proposalId_].requestId];
237
-
238
- require(
239
- requestState.votingPhase == VotingPhase.Neutrals,
240
- InvalidVotingPhaseForProposeOperations(requestState.votingPhase)
241
- );
242
-
243
- require(
244
- requestState.selectedNeutrals.contains(msg.sender),
245
- AccountNotSelected(msg.sender)
246
- );
247
-
248
- SLCProposalState storage proposalState = $.proposalState[proposalId_];
249
-
250
- bytes32 operationHash_ = hashOperation(operationBundle_);
251
-
252
- bytes32 previousOperationHash_ = proposalState.neutralToOperation[msg.sender];
253
- if (previousOperationHash_ != bytes32(0)) {
254
- proposalState.operationByNeutral[previousOperationHash_].remove(msg.sender);
255
-
256
- if (proposalState.operationByNeutral[previousOperationHash_].length() == 0) {
257
- _removeOperation(proposalState, previousOperationHash_);
258
- }
259
- }
260
-
261
- proposalState.operationByNeutral[operationHash_].add(msg.sender);
262
- proposalState.neutralToOperation[msg.sender] = operationHash_;
263
-
264
- _addOperation(proposalState, operationBundle_, operationHash_);
265
-
266
- proposalState.operationBundleByHash[operationHash_] = operationBundle_;
213
+ GovernanceUtils.handleProposeOperations(proposalId_, operationBundle_);
267
214
  }
268
215
 
269
216
  function implementation() external view returns (address) {
@@ -391,6 +338,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
391
338
  additionalLink: requestState.additionalLink,
392
339
  selectedNeutrals: requestState.selectedNeutrals.values(),
393
340
  data: requestState.data,
341
+ createdAt: requestState.createdAt,
394
342
  votingPhase: requestState.votingPhase,
395
343
  leadingOperationHash: requestState.leadingOperationHash,
396
344
  requestNonce: requestState.proposalNonce,
@@ -446,7 +394,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
446
394
  * @inheritdoc IGovernor
447
395
  */
448
396
  function votingDelay() public view override returns (uint256) {
449
- return _getGovernanceStorage().parametersRegistry.getUint(VOTING_DELAY_PARAM_NAME);
397
+ return _getGovernanceStorage().parametersRegistry.getUint(Globals.VOTING_DELAY_PARAM_NAME);
450
398
  }
451
399
 
452
400
  /**
@@ -473,12 +421,16 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
473
421
 
474
422
  function getNeutralsVotingPeriod() public view returns (uint256) {
475
423
  return
476
- _getGovernanceStorage().parametersRegistry.getUint(NEUTRALS_VOTING_PERIOD_PARAM_NAME);
424
+ _getGovernanceStorage().parametersRegistry.getUint(
425
+ Globals.NEUTRALS_VOTING_PERIOD_PARAM_NAME
426
+ );
477
427
  }
478
428
 
479
429
  function getExecutorsVotingPeriod() public view returns (uint256) {
480
430
  return
481
- _getGovernanceStorage().parametersRegistry.getUint(EXECUTORS_VOTING_PERIOD_PARAM_NAME);
431
+ _getGovernanceStorage().parametersRegistry.getUint(
432
+ Globals.EXECUTORS_VOTING_PERIOD_PARAM_NAME
433
+ );
482
434
  }
483
435
 
484
436
  /**
@@ -486,7 +438,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
486
438
  */
487
439
  function getNeutralsThreshold() public view returns (uint256) {
488
440
  uint256 neutralsThreshold_ = _getGovernanceStorage().parametersRegistry.getUint(
489
- NEUTRALS_THRESHOLD_PARAM_NAME
441
+ Globals.NEUTRALS_THRESHOLD_PARAM_NAME
490
442
  );
491
443
 
492
444
  if (neutralsThreshold_ > 100) {
@@ -501,7 +453,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
501
453
  */
502
454
  function getExecutorsThreshold() public view returns (uint256) {
503
455
  uint256 executorsThreshold_ = _getGovernanceStorage().parametersRegistry.getUint(
504
- EXECUTORS_THRESHOLD_PARAM_NAME
456
+ Globals.EXECUTORS_THRESHOLD_PARAM_NAME
505
457
  );
506
458
 
507
459
  if (executorsThreshold_ > 100) {
@@ -525,50 +477,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
525
477
  uint256 proposalId_,
526
478
  address[] memory selectedNeutrals_
527
479
  ) internal {
528
- GovernanceStorage storage $ = _getGovernanceStorage();
529
-
530
- ServiceRequestState storage requestState = $
531
- .slcGovernanceState[$.proposalState[proposalId_].slcCore]
532
- .requestState[$.proposalState[proposalId_].requestId];
533
-
534
- uint256 serviceFee_ = requestState.serviceFee;
535
- if (serviceFee_ == 0 && msg.value == 0) return;
536
-
537
- if (msg.value > 0) {
538
- require(serviceFee_ == msg.value, InvalidServiceFee(serviceFee_, msg.value));
539
- $.token.deposit{value: msg.value}();
540
- } else {
541
- $.token.safeTransferFrom(requestState.requester, address(this), serviceFee_);
542
- }
543
-
544
- uint256 executorsShare_ = _getExecutorsShare();
545
- uint256 neutralsShare_ = _getNeutralsShare();
546
- uint256 treasuryShare_ = _getTreasuryShare();
547
-
548
- // Validate shares total 100%
549
- uint256 totalShares_ = executorsShare_ + neutralsShare_ + treasuryShare_;
550
- require(totalShares_ == 100, InvalidSharesTotal(totalShares_));
551
-
552
- // Calculate amounts
553
- uint256 executorsAmount_ = (serviceFee_ * executorsShare_) / 100;
554
- uint256 neutralsAmount_ = (serviceFee_ * neutralsShare_) / 100;
555
- uint256 treasuryAmount_ = (serviceFee_ * treasuryShare_) / 100;
556
-
557
- // Distribute to executors
558
- if (executorsAmount_ > 0) {
559
- $.executorsRegistry.distributeRewards(executorsAmount_);
560
- }
561
-
562
- // Distribute to neutrals
563
- if (neutralsAmount_ > 0) {
564
- $.neutralsRegistry.distributeRewards(selectedNeutrals_, neutralsAmount_);
565
- }
566
-
567
- // Send to treasury
568
- if (treasuryAmount_ > 0) {
569
- address treasury_ = _getTreasuryAddress();
570
- $.token.safeTransfer(treasury_, treasuryAmount_);
571
- }
480
+ GovernanceUtils.distributeServiceFeeRewards(proposalId_, selectedNeutrals_);
572
481
  }
573
482
 
574
483
  function _executeOperations(
@@ -578,34 +487,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
578
487
  bytes[] memory /*calldatas*/,
579
488
  bytes32 /*descriptionHash*/
580
489
  ) internal override {
581
- GovernanceStorage storage $ = _getGovernanceStorage();
582
-
583
- ServiceRequestState storage requestState = $
584
- .slcGovernanceState[$.proposalState[proposalId].slcCore]
585
- .requestState[$.proposalState[proposalId].requestId];
586
-
587
- bytes32 leadingOperationHash_ = requestState.leadingOperationHash;
588
- if (leadingOperationHash_ == bytes32(0)) {
589
- revert Unreachable();
590
- }
591
-
592
- OperationBundle storage leadingOperationBundle_ = $
593
- .proposalState[proposalId]
594
- .operationBundleByHash[leadingOperationHash_];
595
-
596
- ISLCCore slcCore_ = ISLCCore(targets[0]);
597
-
598
- uint256 operationsLength_ = leadingOperationBundle_.operations.length;
599
- for (uint256 i = 0; i < operationsLength_; ++i) {
600
- slcCore_.arbitraryExecute(
601
- leadingOperationBundle_.operations[i].operation_,
602
- leadingOperationBundle_.operations[i].to_,
603
- leadingOperationBundle_.operations[i].data_,
604
- leadingOperationBundle_.operations[i].value_
605
- );
606
- }
607
-
608
- requestState.votingPhase = VotingPhase.Executed;
490
+ GovernanceUtils.executeOperations(proposalId, targets);
609
491
  }
610
492
 
611
493
  /**
@@ -710,27 +592,12 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
710
592
  }
711
593
 
712
594
  function _getDAOSLC() internal view returns (address) {
713
- return _getGovernanceStorage().parametersRegistry.getAddr(DAOSLC_PARAM_NAME);
714
- }
715
-
716
- function _getExecutorsShare() internal view returns (uint256) {
717
- return _getGovernanceStorage().parametersRegistry.getUint(EXECUTORS_SHARE_PARAM_NAME);
718
- }
719
-
720
- function _getNeutralsShare() internal view returns (uint256) {
721
- return _getGovernanceStorage().parametersRegistry.getUint(NEUTRALS_SHARE_PARAM_NAME);
722
- }
723
-
724
- function _getTreasuryShare() internal view returns (uint256) {
725
- return _getGovernanceStorage().parametersRegistry.getUint(TREASURY_SHARE_PARAM_NAME);
726
- }
727
-
728
- function _getTreasuryAddress() internal view returns (address) {
729
- return _getGovernanceStorage().parametersRegistry.getAddr(TREASURY_ADDRESS_PARAM_NAME);
595
+ return _getGovernanceStorage().parametersRegistry.getAddr(Globals.DAOSLC_PARAM_NAME);
730
596
  }
731
597
 
732
598
  function _getMinServiceFee() internal view returns (uint256) {
733
- return _getGovernanceStorage().parametersRegistry.getUint(MIN_SERVICE_FEE_PARAM_NAME);
599
+ return
600
+ _getGovernanceStorage().parametersRegistry.getUint(Globals.MIN_SERVICE_FEE_PARAM_NAME);
734
601
  }
735
602
 
736
603
  function _makeProposal(
@@ -786,93 +653,7 @@ contract Governance is IGovernance, GovernorUpgradeable, UUPSUpgradeable {
786
653
  }
787
654
 
788
655
  function _fillProposalState(uint256 proposalId_) private {
789
- GovernanceStorage storage $ = _getGovernanceStorage();
790
-
791
- ServiceRequestState storage requestState = $
792
- .slcGovernanceState[$.proposalState[proposalId_].slcCore]
793
- .requestState[$.proposalState[proposalId_].requestId];
794
-
795
- uint256 neutralsLength_ = requestState.selectedNeutrals.length();
796
-
797
- $.proposalState[proposalId_].neutralsThreshold = Math.mulDiv(
798
- neutralsLength_,
799
- getNeutralsThreshold(),
800
- 100,
801
- Math.Rounding.Ceil
802
- );
803
-
804
- uint256 executorsCount_ = $.executorsRegistry.getExecutorsCount();
805
- $.proposalState[proposalId_].executorsThreshold = Math.mulDiv(
806
- executorsCount_,
807
- getExecutorsThreshold(),
808
- 100,
809
- Math.Rounding.Ceil
810
- );
811
-
812
- requestState.proposalIds.push(proposalId_);
813
-
814
- requestState.votingPhase = VotingPhase.Neutrals;
815
- }
816
-
817
- function _addOperation(
818
- SLCProposalState storage proposalState,
819
- OperationBundle memory operationBundle_,
820
- bytes32 operationHash_
821
- ) private returns (bool) {
822
- if (!_containsOperation(proposalState, operationHash_)) {
823
- proposalState.operationBundles.push(operationBundle_);
824
- // The value is stored at length-1, but we add 1 to all indexes
825
- // and use 0 as a sentinel value
826
- proposalState.operationBundlePosition[operationHash_] = proposalState
827
- .operationBundles
828
- .length;
829
- return true;
830
- } else {
831
- return false;
832
- }
833
- }
834
-
835
- function _removeOperation(
836
- SLCProposalState storage proposalState,
837
- bytes32 operationHash_
838
- ) private returns (bool) {
839
- // We cache the value's position to prevent multiple reads from the same storage slot
840
- uint256 position_ = proposalState.operationBundlePosition[operationHash_];
841
-
842
- if (position_ != 0) {
843
- // Equivalent to contains(set, value)
844
- // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
845
- // the array, and then remove the last element (sometimes called as 'swap and pop').
846
-
847
- uint256 valueIndex_ = position_ - 1;
848
- uint256 lastIndex_ = proposalState.operationBundles.length - 1;
849
-
850
- if (valueIndex_ != lastIndex_) {
851
- OperationBundle storage lastValue = proposalState.operationBundles[lastIndex_];
852
-
853
- // Move the lastValue to the index where the value to delete is
854
- proposalState.operationBundles[valueIndex_] = lastValue;
855
- // Update the tracked position of the lastValue (that was just moved)
856
- proposalState.operationBundlePosition[hashOperation(lastValue)] = position_;
857
- }
858
-
859
- // Delete the slot where the moved value was stored
860
- proposalState.operationBundles.pop();
861
-
862
- // Delete the tracked position for the deleted slot
863
- delete proposalState.operationBundlePosition[operationHash_];
864
-
865
- return true;
866
- } else {
867
- return false;
868
- }
869
- }
870
-
871
- function _containsOperation(
872
- SLCProposalState storage proposalState,
873
- bytes32 operationHash_
874
- ) private view returns (bool) {
875
- return proposalState.operationBundlePosition[operationHash_] != 0;
656
+ GovernanceUtils.fillProposalState(proposalId_);
876
657
  }
877
658
 
878
659
  function _findLeadingOperation(uint256 proposalId_) private view returns (bytes32) {
@@ -287,7 +287,7 @@ contract NeutralsRegistry is INeutralsRegistry, ERC165, UUPSUpgradeable {
287
287
 
288
288
  uint256 amount_ = announcement.amount;
289
289
 
290
- delete $.delegations[neutral_][msg.sender].withdrawal;
290
+ delete $.delegations[msg.sender][neutral_].withdrawal;
291
291
 
292
292
  if ($.delegations[msg.sender][neutral_].delegatedAmount == 0) {
293
293
  $.neutralsPerDelegator[msg.sender].remove(neutral_);
@@ -0,0 +1,304 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
5
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6
+ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
7
+ import {GovernorUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";
8
+
9
+ import {ISLCCore} from "../interfaces/ISLCCore.sol";
10
+ import {Governance} from "../governance/Governance.sol";
11
+ import {IGovernance} from "../interfaces/IGovernance.sol";
12
+ import {IWrappedToken} from "../interfaces/IWrappedToken.sol";
13
+ import * as Globals from "../utils/Globals.sol";
14
+
15
+ library GovernanceUtils {
16
+ using EnumerableSet for EnumerableSet.AddressSet;
17
+ using SafeERC20 for IWrappedToken;
18
+
19
+ bytes32 private constant GOVERNANCE_STORAGE_LOCATION =
20
+ 0xb93e2841340255f69de4f9300770e3cc1894dc081e8bf6d34a78e9ac897c7900;
21
+
22
+ bytes32 private constant GOVERNOR_STORAGE_LOCATION =
23
+ 0x7c712897014dbe49c045ef1299aa2d5f9e67e48eea4403efa21f1e0f3ac0cb00;
24
+
25
+ function handleProposeOperations(
26
+ uint256 proposalId_,
27
+ IGovernance.OperationBundle calldata operationBundle_
28
+ ) external {
29
+ Governance.GovernanceStorage storage $ = _getGovernanceStorage();
30
+
31
+ Governance.ServiceRequestState storage requestState = $
32
+ .slcGovernanceState[$.proposalState[proposalId_].slcCore]
33
+ .requestState[$.proposalState[proposalId_].requestId];
34
+
35
+ require(
36
+ requestState.votingPhase == Governance.VotingPhase.Neutrals,
37
+ Governance.InvalidVotingPhaseForProposeOperations(requestState.votingPhase)
38
+ );
39
+
40
+ require(
41
+ requestState.selectedNeutrals.contains(msg.sender),
42
+ Governance.AccountNotSelected(msg.sender)
43
+ );
44
+
45
+ Governance.SLCProposalState storage proposalState = $.proposalState[proposalId_];
46
+
47
+ bytes32 operationHash_ = hashOperation(operationBundle_);
48
+
49
+ bytes32 previousOperationHash_ = proposalState.neutralToOperation[msg.sender];
50
+ if (previousOperationHash_ != bytes32(0)) {
51
+ proposalState.operationByNeutral[previousOperationHash_].remove(msg.sender);
52
+
53
+ if (proposalState.operationByNeutral[previousOperationHash_].length() == 0) {
54
+ _removeOperation(proposalState, previousOperationHash_);
55
+ }
56
+ }
57
+
58
+ proposalState.operationByNeutral[operationHash_].add(msg.sender);
59
+ proposalState.neutralToOperation[msg.sender] = operationHash_;
60
+
61
+ _addOperation(proposalState, operationBundle_, operationHash_);
62
+
63
+ proposalState.operationBundleByHash[operationHash_] = operationBundle_;
64
+ }
65
+
66
+ function fillProposalState(uint256 proposalId_) external {
67
+ Governance.GovernanceStorage storage $ = _getGovernanceStorage();
68
+
69
+ Governance.ServiceRequestState storage requestState = $
70
+ .slcGovernanceState[$.proposalState[proposalId_].slcCore]
71
+ .requestState[$.proposalState[proposalId_].requestId];
72
+
73
+ uint256 neutralsLength_ = requestState.selectedNeutrals.length();
74
+
75
+ $.proposalState[proposalId_].neutralsThreshold = Math.mulDiv(
76
+ neutralsLength_,
77
+ getNeutralsThreshold(),
78
+ 100,
79
+ Math.Rounding.Ceil
80
+ );
81
+
82
+ uint256 executorsCount_ = $.executorsRegistry.getExecutorsCount();
83
+ $.proposalState[proposalId_].executorsThreshold = Math.mulDiv(
84
+ executorsCount_,
85
+ getExecutorsThreshold(),
86
+ 100,
87
+ Math.Rounding.Ceil
88
+ );
89
+
90
+ requestState.proposalIds.push(proposalId_);
91
+
92
+ requestState.votingPhase = Governance.VotingPhase.Neutrals;
93
+ }
94
+
95
+ function distributeServiceFeeRewards(
96
+ uint256 proposalId_,
97
+ address[] memory selectedNeutrals_
98
+ ) external {
99
+ Governance.GovernanceStorage storage $ = _getGovernanceStorage();
100
+
101
+ Governance.ServiceRequestState storage requestState = $
102
+ .slcGovernanceState[$.proposalState[proposalId_].slcCore]
103
+ .requestState[$.proposalState[proposalId_].requestId];
104
+
105
+ uint256 serviceFee_ = requestState.serviceFee;
106
+ if (serviceFee_ == 0 && msg.value == 0) return;
107
+
108
+ if (msg.value > 0) {
109
+ require(
110
+ serviceFee_ == msg.value,
111
+ Governance.InvalidServiceFee(serviceFee_, msg.value)
112
+ );
113
+ $.token.deposit{value: msg.value}();
114
+ } else {
115
+ $.token.safeTransferFrom(requestState.requester, address(this), serviceFee_);
116
+ }
117
+
118
+ uint256 executorsShare_ = getExecutorsShare();
119
+ uint256 neutralsShare_ = getNeutralsShare();
120
+ uint256 treasuryShare_ = getTreasuryShare();
121
+
122
+ // Validate shares total 100%
123
+ uint256 totalShares_ = executorsShare_ + neutralsShare_ + treasuryShare_;
124
+ require(totalShares_ == 100, Governance.InvalidSharesTotal(totalShares_));
125
+
126
+ // Calculate amounts
127
+ uint256 executorsAmount_ = (serviceFee_ * executorsShare_) / 100;
128
+ uint256 neutralsAmount_ = (serviceFee_ * neutralsShare_) / 100;
129
+ uint256 treasuryAmount_ = (serviceFee_ * treasuryShare_) / 100;
130
+
131
+ // Distribute to executors
132
+ if (executorsAmount_ > 0) {
133
+ $.executorsRegistry.distributeRewards(executorsAmount_);
134
+ }
135
+
136
+ // Distribute to neutrals
137
+ if (neutralsAmount_ > 0) {
138
+ $.neutralsRegistry.distributeRewards(selectedNeutrals_, neutralsAmount_);
139
+ }
140
+
141
+ // Send to treasury
142
+ if (treasuryAmount_ > 0) {
143
+ address treasury_ = getTreasuryAddress();
144
+ $.token.safeTransfer(treasury_, treasuryAmount_);
145
+ }
146
+ }
147
+
148
+ function executeOperations(uint256 proposalId, address[] memory targets) external {
149
+ Governance.GovernanceStorage storage $ = _getGovernanceStorage();
150
+
151
+ Governance.ServiceRequestState storage requestState = $
152
+ .slcGovernanceState[$.proposalState[proposalId].slcCore]
153
+ .requestState[$.proposalState[proposalId].requestId];
154
+
155
+ bytes32 leadingOperationHash_ = requestState.leadingOperationHash;
156
+ if (leadingOperationHash_ == bytes32(0)) {
157
+ revert Governance.Unreachable();
158
+ }
159
+
160
+ IGovernance.OperationBundle storage leadingOperationBundle_ = $
161
+ .proposalState[proposalId]
162
+ .operationBundleByHash[leadingOperationHash_];
163
+
164
+ ISLCCore slcCore_ = ISLCCore(targets[0]);
165
+
166
+ uint256 operationsLength_ = leadingOperationBundle_.operations.length;
167
+ for (uint256 i = 0; i < operationsLength_; ++i) {
168
+ slcCore_.arbitraryExecute(
169
+ leadingOperationBundle_.operations[i].operation_,
170
+ leadingOperationBundle_.operations[i].to_,
171
+ leadingOperationBundle_.operations[i].data_,
172
+ leadingOperationBundle_.operations[i].value_
173
+ );
174
+ }
175
+
176
+ requestState.votingPhase = Governance.VotingPhase.Executed;
177
+ }
178
+
179
+ function hashOperation(
180
+ IGovernance.OperationBundle memory operation_
181
+ ) public pure returns (bytes32) {
182
+ return keccak256(abi.encode(operation_));
183
+ }
184
+
185
+ function getExecutorsShare() public view returns (uint256) {
186
+ return
187
+ _getGovernanceStorage().parametersRegistry.getUint(Globals.EXECUTORS_SHARE_PARAM_NAME);
188
+ }
189
+
190
+ function getNeutralsShare() public view returns (uint256) {
191
+ return
192
+ _getGovernanceStorage().parametersRegistry.getUint(Globals.NEUTRALS_SHARE_PARAM_NAME);
193
+ }
194
+
195
+ function getTreasuryShare() public view returns (uint256) {
196
+ return
197
+ _getGovernanceStorage().parametersRegistry.getUint(Globals.TREASURY_SHARE_PARAM_NAME);
198
+ }
199
+
200
+ function getTreasuryAddress() public view returns (address) {
201
+ return
202
+ _getGovernanceStorage().parametersRegistry.getAddr(
203
+ Globals.TREASURY_ADDRESS_PARAM_NAME
204
+ );
205
+ }
206
+
207
+ function getNeutralsThreshold() public view returns (uint256) {
208
+ uint256 neutralsThreshold_ = _getGovernanceStorage().parametersRegistry.getUint(
209
+ Globals.NEUTRALS_THRESHOLD_PARAM_NAME
210
+ );
211
+
212
+ if (neutralsThreshold_ > 100) {
213
+ revert Governance.NeutralsThresholdTooHigh(neutralsThreshold_);
214
+ }
215
+
216
+ return neutralsThreshold_;
217
+ }
218
+
219
+ function getExecutorsThreshold() public view returns (uint256) {
220
+ uint256 executorsThreshold_ = _getGovernanceStorage().parametersRegistry.getUint(
221
+ Globals.EXECUTORS_THRESHOLD_PARAM_NAME
222
+ );
223
+
224
+ if (executorsThreshold_ > 100) {
225
+ revert Governance.ExecutorsThresholdTooHigh(executorsThreshold_);
226
+ }
227
+
228
+ return executorsThreshold_;
229
+ }
230
+
231
+ function _addOperation(
232
+ Governance.SLCProposalState storage proposalState,
233
+ IGovernance.OperationBundle memory operationBundle_,
234
+ bytes32 operationHash_
235
+ ) private returns (bool) {
236
+ if (!_containsOperation(proposalState, operationHash_)) {
237
+ proposalState.operationBundles.push(operationBundle_);
238
+ // The value is stored at length-1, but we add 1 to all indexes
239
+ // and use 0 as a sentinel value
240
+ proposalState.operationBundlePosition[operationHash_] = proposalState
241
+ .operationBundles
242
+ .length;
243
+ return true;
244
+ } else {
245
+ return false;
246
+ }
247
+ }
248
+
249
+ function _containsOperation(
250
+ Governance.SLCProposalState storage proposalState,
251
+ bytes32 operationHash_
252
+ ) private view returns (bool) {
253
+ return proposalState.operationBundlePosition[operationHash_] != 0;
254
+ }
255
+
256
+ function _removeOperation(
257
+ Governance.SLCProposalState storage proposalState,
258
+ bytes32 operationHash_
259
+ ) private returns (bool) {
260
+ // We cache the value's position to prevent multiple reads from the same storage slot
261
+ uint256 position_ = proposalState.operationBundlePosition[operationHash_];
262
+
263
+ if (position_ != 0) {
264
+ // Equivalent to contains(set, value)
265
+ // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
266
+ // the array, and then remove the last element (sometimes called as 'swap and pop').
267
+
268
+ uint256 valueIndex_ = position_ - 1;
269
+ uint256 lastIndex_ = proposalState.operationBundles.length - 1;
270
+
271
+ if (valueIndex_ != lastIndex_) {
272
+ IGovernance.OperationBundle storage lastValue = proposalState.operationBundles[
273
+ lastIndex_
274
+ ];
275
+
276
+ // Move the lastValue to the index where the value to delete is
277
+ proposalState.operationBundles[valueIndex_] = lastValue;
278
+ // Update the tracked position of the lastValue (that was just moved)
279
+ proposalState.operationBundlePosition[hashOperation(lastValue)] = position_;
280
+ }
281
+
282
+ // Delete the slot where the moved value was stored
283
+ proposalState.operationBundles.pop();
284
+
285
+ // Delete the tracked position for the deleted slot
286
+ delete proposalState.operationBundlePosition[operationHash_];
287
+
288
+ return true;
289
+ } else {
290
+ return false;
291
+ }
292
+ }
293
+
294
+ function _getGovernanceStorage()
295
+ private
296
+ pure
297
+ returns (Governance.GovernanceStorage storage $)
298
+ {
299
+ /* solhint-disable-next-line no-inline-assembly */
300
+ assembly ("memory-safe") {
301
+ $.slot := GOVERNANCE_STORAGE_LOCATION
302
+ }
303
+ }
304
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web3dotorg/evm-slc-core-contracts",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "author": "<TBD>",
5
5
  "repository": {
6
6
  "type": "git",
package/utils/Globals.sol CHANGED
@@ -4,4 +4,28 @@ pragma solidity ^0.8.28;
4
4
  string constant TREASURY_ADDRESS_PARAM_NAME = "slc.general.treasuryAddress";
5
5
  string constant DAOSLC_PARAM_NAME = "slc.general.daoSLC";
6
6
 
7
+ // Governance
8
+
9
+ string constant VOTING_DELAY_PARAM_NAME = "slc.governance.votingDelay";
10
+
11
+ // solhint-disable-next-line gas-small-strings
12
+ string constant NEUTRALS_VOTING_PERIOD_PARAM_NAME = "slc.governance.neutralsVotingPeriod";
13
+
14
+ // solhint-disable-next-line gas-small-strings
15
+ string constant EXECUTORS_VOTING_PERIOD_PARAM_NAME = "slc.governance.executorsVotingPeriod";
16
+
17
+ // solhint-disable-next-line gas-small-strings
18
+ string constant NEUTRALS_THRESHOLD_PARAM_NAME = "slc.governance.neutralsThreshold";
19
+
20
+ // solhint-disable-next-line gas-small-strings
21
+ string constant EXECUTORS_THRESHOLD_PARAM_NAME = "slc.governance.executorsThreshold";
22
+
23
+ string constant EXECUTORS_SHARE_PARAM_NAME = "slc.governance.executorsShare";
24
+
25
+ string constant NEUTRALS_SHARE_PARAM_NAME = "slc.governance.neutralsShare";
26
+
27
+ string constant TREASURY_SHARE_PARAM_NAME = "slc.governance.treasuryShare";
28
+
29
+ string constant MIN_SERVICE_FEE_PARAM_NAME = "slc.governance.minServiceFee";
30
+
7
31
  uint256 constant MAX_BASIS_POINTS = 100_00;