|
| 1 | +// SPDX-License-Identifier: MIT |
| 2 | +pragma solidity 0.8.25; |
| 3 | + |
| 4 | +import { |
| 5 | + OwnableUpgradeable |
| 6 | +} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; |
| 7 | +import { |
| 8 | + ReentrancyGuardUpgradeable |
| 9 | +} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; |
| 10 | +import {Flyover} from "./libraries/Flyover.sol"; |
| 11 | + |
| 12 | +/// @title OwnableDaoContributorUpgradeable |
| 13 | +/// @notice This contract is used to handle the contributions to the DAO |
| 14 | +/// @author Rootstock Labs |
| 15 | +/// @dev Any contract that inherits from this contract will be able to collect DAO |
| 16 | +/// contributions according to the logic the child contract defines |
| 17 | +abstract contract OwnableDaoContributorUpgradeable is |
| 18 | + ReentrancyGuardUpgradeable, |
| 19 | + OwnableUpgradeable { |
| 20 | + |
| 21 | + // @custom:storage-location erc7201:rsk.dao.contributor |
| 22 | + struct DaoContributorStorage { |
| 23 | + uint256 feePercentage; |
| 24 | + uint256 currentContribution; |
| 25 | + address payable feeCollector; |
| 26 | + } |
| 27 | + |
| 28 | + // keccak256(abi.encode(uint256(keccak256(bytes("rsk.dao.contributor"))) - 1)) & ~bytes32(uint256(0xff)); |
| 29 | + bytes32 private constant _CONTRIBUTOR_STORAGE_LOCATION = |
| 30 | + 0xb7e513d124139aa68259a99d4c2c344f3ba61e36716330d77f7fa887d0048e00; |
| 31 | + |
| 32 | + /// @notice This event is emitted when a contribution is made to the DAO |
| 33 | + /// @param contributor the address of the contributor |
| 34 | + /// @param amount the amount of the contribution |
| 35 | + event DaoContribution(address indexed contributor, uint256 indexed amount); |
| 36 | + |
| 37 | + /// @notice This event is emitted when the DAO fees are claimed. The claim is always all the |
| 38 | + /// contributions made to the DAO by this contract so far |
| 39 | + /// @param claimer the address of the claimer |
| 40 | + /// @param receiver the address of the receiver |
| 41 | + /// @param amount the amount of the fees claimed |
| 42 | + event DaoFeesClaimed(address indexed claimer, address indexed receiver, uint256 indexed amount); |
| 43 | + |
| 44 | + /// @notice This event is emitted when the contributions are configured |
| 45 | + /// @param feeCollector the address of the fee collector |
| 46 | + /// @param feePercentage the percentage of the contributions that goes to the DAO |
| 47 | + event ContributionsConfigured(address indexed feeCollector, uint256 indexed feePercentage); |
| 48 | + |
| 49 | + /// @notice This error is emitted when there are no fees to claim |
| 50 | + error NoFees(); |
| 51 | + |
| 52 | + /// @notice This error is emitted when the fee collector is not set |
| 53 | + error FeeCollectorUnset(); |
| 54 | + |
| 55 | + /// @notice This function is used to claim the contributions to the DAO. |
| 56 | + /// It sends to the fee collector all the accumulated contributions so far |
| 57 | + /// and resets the accumulated contributions to zero |
| 58 | + /// @dev The function is only callable by the owner of the contract |
| 59 | + function claimContribution() external onlyOwner nonReentrant { |
| 60 | + DaoContributorStorage storage $ = _getContributorStorage(); |
| 61 | + uint256 amount = $.currentContribution; |
| 62 | + $.currentContribution = 0; |
| 63 | + address feeCollector = $.feeCollector; |
| 64 | + if (amount == 0) revert NoFees(); |
| 65 | + if (feeCollector == address(0)) revert FeeCollectorUnset(); |
| 66 | + if (amount > address(this).balance) revert Flyover.NoBalance(amount, address(this).balance); |
| 67 | + emit DaoFeesClaimed(msg.sender, feeCollector, amount); |
| 68 | + (bool sent, bytes memory reason) = feeCollector.call{value: amount}(""); |
| 69 | + if (!sent) revert Flyover.PaymentFailed(feeCollector, amount, reason); |
| 70 | + } |
| 71 | + |
| 72 | + /// @notice This function is used to configure the contributions to the DAO |
| 73 | + /// @param feeCollector the address of the fee collector |
| 74 | + /// @param feePercentage the percentage of the contributions that goes to the DAO |
| 75 | + /// @dev The function is only callable by the owner of the contract |
| 76 | + function configureContributions( |
| 77 | + address payable feeCollector, |
| 78 | + uint256 feePercentage |
| 79 | + ) external onlyOwner { |
| 80 | + DaoContributorStorage storage $ = _getContributorStorage(); |
| 81 | + $.feeCollector = feeCollector; |
| 82 | + $.feePercentage = feePercentage; |
| 83 | + emit ContributionsConfigured(feeCollector, feePercentage); |
| 84 | + } |
| 85 | + |
| 86 | + /// @notice This function is used to get the fee percentage |
| 87 | + /// that the child contracts use to calculate the contributions |
| 88 | + /// @return feePercentage the fee percentage |
| 89 | + function getFeePercentage() external view returns (uint256) { |
| 90 | + return _getContributorStorage().feePercentage; |
| 91 | + } |
| 92 | + |
| 93 | + /// @notice This function is used to get the current contribution |
| 94 | + /// @return currentContribution the current contribution |
| 95 | + function getCurrentContribution() external view returns (uint256) { |
| 96 | + return _getContributorStorage().currentContribution; |
| 97 | + } |
| 98 | + |
| 99 | + /// @notice This function is used to get the fee collector |
| 100 | + /// @return feeCollector the fee collector address |
| 101 | + function getFeeCollector() external view returns (address) { |
| 102 | + return _getContributorStorage().feeCollector; |
| 103 | + } |
| 104 | + |
| 105 | + /// @notice This function is used to initialize the contract |
| 106 | + /// @param owner the owner of the contract |
| 107 | + /// @param feePercentage the percentage that the child contracts use to calculate the contributions |
| 108 | + /// @param feeCollector the address of the fee collector that will receive the contributions |
| 109 | + // solhint-disable-next-line func-name-mixedcase |
| 110 | + function __OwnableDaoContributor_init( |
| 111 | + address owner, |
| 112 | + uint256 feePercentage, |
| 113 | + address payable feeCollector |
| 114 | + ) internal onlyInitializing { |
| 115 | + __ReentrancyGuard_init_unchained(); |
| 116 | + __Ownable_init_unchained(owner); |
| 117 | + DaoContributorStorage storage $ = _getContributorStorage(); |
| 118 | + $.feePercentage = feePercentage; |
| 119 | + $.feeCollector = feeCollector; |
| 120 | + } |
| 121 | + |
| 122 | + /// @notice This function is used to add a contribution to the DAO |
| 123 | + /// @dev The function is only callable by the child contract, it can be |
| 124 | + /// included wherever they consider the protocol should collect fees for the DAO |
| 125 | + /// @param contributor the address of the contributor |
| 126 | + /// @param amount the amount of the contribution |
| 127 | + function _addDaoContribution(address contributor, uint256 amount) internal { |
| 128 | + if (amount < 1) return; |
| 129 | + DaoContributorStorage storage $ = _getContributorStorage(); |
| 130 | + $.currentContribution += amount; |
| 131 | + emit DaoContribution(contributor, amount); |
| 132 | + } |
| 133 | + |
| 134 | + /// @dev The function is used to get the storage of the contract, avoid using the regular |
| 135 | + /// storage to prevent conflicts with state variables of the child contract |
| 136 | + /// @return $ the contributor storage |
| 137 | + function _getContributorStorage() private pure returns (DaoContributorStorage storage $) { |
| 138 | + assembly { |
| 139 | + $.slot := _CONTRIBUTOR_STORAGE_LOCATION |
| 140 | + } |
| 141 | + } |
| 142 | +} |
0 commit comments