From 4e4c5e6124d1073cc07f217b1f4eb7563d5aa69e Mon Sep 17 00:00:00 2001 From: Alberto Granzotto Date: Tue, 14 Nov 2023 15:34:52 +0100 Subject: [PATCH 1/4] WIP --- contracts/GovernanceToken/GovernanceToken.sol | 100 ++--------- .../GovernanceToken/GovernanceTokenBase.sol | 33 +--- contracts/InternalMarket/InternalMarket.sol | 72 ++------ .../InternalMarket/InternalMarketBase.sol | 57 ++----- .../RedemptionController.sol | 25 +-- .../RedemptionControllerBase.sol | 7 +- .../ResolutionManager/ResolutionManager.sol | 80 +-------- .../ResolutionManagerBase.sol | 98 ++++------- .../ShareholderRegistry.sol | 36 +--- .../ShareholderRegistryBase.sol | 17 +- contracts/extensions/DAORegistry.sol | 161 ++++++++++++++++++ contracts/extensions/DAORegistryProxy.sol | 108 ++++++++++++ contracts/extensions/DAORoles.sol | 11 -- contracts/extensions/HasRole.sol | 41 ----- contracts/extensions/Roles.sol | 15 -- 15 files changed, 388 insertions(+), 473 deletions(-) create mode 100644 contracts/extensions/DAORegistry.sol create mode 100644 contracts/extensions/DAORegistryProxy.sol delete mode 100644 contracts/extensions/DAORoles.sol delete mode 100644 contracts/extensions/HasRole.sol delete mode 100644 contracts/extensions/Roles.sol diff --git a/contracts/GovernanceToken/GovernanceToken.sol b/contracts/GovernanceToken/GovernanceToken.sol index 33a69ff..b6fe00e 100644 --- a/contracts/GovernanceToken/GovernanceToken.sol +++ b/contracts/GovernanceToken/GovernanceToken.sol @@ -4,9 +4,6 @@ pragma solidity 0.8.16; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import "./GovernanceTokenSnapshot.sol"; -import { Roles } from "../extensions/Roles.sol"; -import "../extensions/DAORoles.sol"; -import "../extensions/HasRole.sol"; /** * @title GovernanceToken @@ -16,7 +13,7 @@ import "../extensions/HasRole.sol"; * functionality for voting, minting, burning, wrapping/unwrapping, and settling tokens. Only authorized * roles (as defined in DAORoles contract) can call certain functions such as mint, burn, wrap, unwrap, and others. */ -contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { +contract GovernanceToken is Initializable, GovernanceTokenSnapshot { IShareholderRegistry internal _shareholderRegistry; event DepositStarted( @@ -37,21 +34,17 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { /** * @notice Initializes a new GovernanceToken instance with the provided roles, name, and symbol. * @dev Sets the roles, ERC20 name and symbol using provided args and calls the internal `_initialize` function. - * @param roles DAORoles instance that controls roles within the token contract. + * @param daoRegistry DAORegistry instance that controls roles within the token contract. * @param name string for the ERC20 token name of GovernanceToken. * @param symbol string for the ERC20 token symbol of GovernanceToken. */ function initialize( - DAORoles roles, + DAORegistry daoRegistry, string memory name, string memory symbol ) public initializer { - require( - address(roles) != address(0), - "GovernanceToken: 0x0 not allowed" - ); + _setDAORegistry(daoRegistry); _initialize(name, symbol); - _setRoles(roles); } /// @custom:oz-upgrades-unsafe-allow constructor @@ -71,39 +64,12 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { public virtual override - onlyRole(Roles.RESOLUTION_ROLE) + onlyResolutionManager returns (uint256) { return _snapshot(); } - /** - * @notice Set address of the Voting logic for the contract. - * @dev Can be called only by the operator role. - * @param voting IVoting instance that controls the voting logic. - */ - function setVoting( - IVoting voting - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(voting)) - { - _setVoting(voting); - } - - /** - * @notice Set the shareholder registry contract address. - * @dev Can be called only by the operator role. - * @param shareholderRegistry IShareholderRegistry instance that maintains the shareholder data. - */ - function setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) external virtual onlyRole(Roles.OPERATOR_ROLE) { - _shareholderRegistry = shareholderRegistry; - } - /** * @notice Set the settlement period for token deposits. * @dev Can be called only by the operator role. @@ -111,42 +77,10 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { */ function setSettlementPeriod( uint256 settlementPeriod_ - ) external virtual onlyRole(Roles.OPERATOR_ROLE) { + ) external virtual onlyResolutionManager { settlementPeriod = settlementPeriod_; } - /** - * @notice Set the external token reference for wrapping into the GovernanceToken. - * @dev Can be called only by the operator role. - * @param tokenExternalAddress Address of the external token to wrap into GovernanceToken. - */ - function setTokenExternal( - address tokenExternalAddress - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(tokenExternalAddress) - { - _setTokenExternal(tokenExternalAddress); - } - - /** - * @notice Set redemption controller. - * @dev Can be called only by the operator role. - * @param redemption IRedemptionController instance that controls token redemption. - */ - function setRedemptionController( - IRedemptionController redemption - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(redemption)) - { - _setRedemptionController(redemption); - } - /** * @notice Mint new governance tokens for the given address. * @dev Can be called only by the resolution role. @@ -156,7 +90,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { function mint( address to, uint256 amount - ) public virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual onlyResolutionManager { _mint(to, amount); if ( @@ -165,7 +99,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { to ) ) { - _redemptionController.afterMint(to, amount); + getRedemptionController().afterMint(to, amount); } } @@ -178,7 +112,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { function burn( address from, uint256 amount - ) public virtual onlyRole(Roles.MARKET_ROLE) { + ) public virtual onlyInternalMarket { _burn(from, amount); } @@ -191,7 +125,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { function wrap( address from, uint256 amount - ) public virtual onlyRole(Roles.MARKET_ROLE) { + ) public virtual onlyInternalMarket { _wrap(from, amount); } @@ -206,7 +140,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { address from, address to, uint256 amount - ) public virtual onlyRole(Roles.MARKET_ROLE) { + ) public virtual onlyInternalMarket { _unwrap(from, to, amount); } @@ -228,7 +162,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { function mintVesting( address to, uint256 amount - ) public virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual onlyResolutionManager { _mintVesting(to, amount); } @@ -241,7 +175,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { function setVesting( address to, uint256 amount - ) public virtual onlyRole(Roles.OPERATOR_ROLE) { + ) public virtual onlyResolutionManager { _setVesting(to, amount); } @@ -259,7 +193,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { public virtual override(ERC20Upgradeable, IERC20Upgradeable) - onlyRole(Roles.MARKET_ROLE) + onlyInternalMarket returns (bool) { return super.transfer(to, amount); @@ -281,7 +215,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { public virtual override(ERC20Upgradeable, IERC20Upgradeable) - onlyRole(Roles.MARKET_ROLE) + onlyInternalMarket returns (bool) { return super.transferFrom(from, to, amount); @@ -303,7 +237,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { uint256 amount ) internal virtual override { super._afterTokenTransfer(from, to, amount); - _voting.afterTokenTransfer(from, to, amount); + getVoting().afterTokenTransfer(from, to, amount); // Invariants require( @@ -346,7 +280,7 @@ contract GovernanceToken is Initializable, HasRole, GovernanceTokenSnapshot { */ function _wrap(address from, uint amount) internal virtual { require( - tokenExternal.transferFrom(from, address(this), amount), + getNeokingdomToken().transferFrom(from, address(this), amount), "GovernanceToken: transfer failed" ); require(amount > 0, "GovernanceToken: attempt to wrap 0 tokens"); diff --git a/contracts/GovernanceToken/GovernanceTokenBase.sol b/contracts/GovernanceToken/GovernanceTokenBase.sol index fe33fcc..ad6db68 100644 --- a/contracts/GovernanceToken/GovernanceTokenBase.sol +++ b/contracts/GovernanceToken/GovernanceTokenBase.sol @@ -5,16 +5,16 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import "../RedemptionController/IRedemptionController.sol"; import "../Voting/IVoting.sol"; import "../InternalMarket/InternalMarket.sol"; -import "../extensions/DAORoles.sol"; +import "../extensions/DAORegistryProxy.sol"; import "./IGovernanceToken.sol"; -abstract contract GovernanceTokenBase is ERC20Upgradeable, IGovernanceToken { +abstract contract GovernanceTokenBase is + ERC20Upgradeable, + IGovernanceToken, + DAORegistryProxy +{ event VestingSet(address to, uint256 amount); - IVoting internal _voting; - IRedemptionController internal _redemptionController; - INeokingdomToken public tokenExternal; - function _initialize( string memory name, string memory symbol @@ -26,21 +26,6 @@ abstract contract GovernanceTokenBase is ERC20Upgradeable, IGovernanceToken { // In theory they should be burned or added to a pool mapping(address => uint256) internal _vestingBalance; - // mapping(address => uint256) internal _unlockedBalance; - function _setVoting(IVoting voting) internal { - _voting = voting; - } - - function _setRedemptionController( - IRedemptionController redemptionController - ) internal virtual { - _redemptionController = redemptionController; - } - - function _setTokenExternal(address tokenExternalAddress) internal { - tokenExternal = INeokingdomToken(tokenExternalAddress); - } - function _beforeTokenTransfer( address from, address to, @@ -57,7 +42,7 @@ abstract contract GovernanceTokenBase is ERC20Upgradeable, IGovernanceToken { } function _mint(address to, uint256 amount) internal virtual override { - tokenExternal.mint(address(this), amount); + getNeokingdomToken().mint(address(this), amount); super._mint(to, amount); } @@ -69,14 +54,14 @@ abstract contract GovernanceTokenBase is ERC20Upgradeable, IGovernanceToken { function _unwrap(address from, address to, uint amount) internal virtual { require( - tokenExternal.transfer(to, amount), + getNeokingdomToken().transfer(to, amount), "GovernanceToken: transfer failed" ); super._burn(from, amount); } function _burn(address from, uint amount) internal virtual override { - tokenExternal.burn(amount); + getNeokingdomToken().burn(amount); super._burn(from, amount); } diff --git a/contracts/InternalMarket/InternalMarket.sol b/contracts/InternalMarket/InternalMarket.sol index 8ecff95..975c8f2 100644 --- a/contracts/InternalMarket/InternalMarket.sol +++ b/contracts/InternalMarket/InternalMarket.sol @@ -6,9 +6,6 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "../ShareholderRegistry/IShareholderRegistry.sol"; import "./InternalMarketBase.sol"; -import { Roles } from "../extensions/Roles.sol"; -import "../extensions/DAORoles.sol"; -import "../extensions/HasRole.sol"; import "./IDIAOracleV2.sol"; /** @@ -16,25 +13,16 @@ import "./IDIAOracleV2.sol"; * @dev A smart contract that handles trading of governance tokens between users, * allowing them to make an offer, match existing offers, deposit, withdraw, and redeem locked tokens. */ -contract InternalMarket is Initializable, HasRole, InternalMarketBase { +contract InternalMarket is Initializable, InternalMarketBase { IDIAOracleV2 internal _diaPriceOracle; /** * @dev Initializes the contract with the given roles and internal token. - * @param roles DAORoles instance containing custom access control roles. - * @param governanceToken Reference to governance token. + * @param daoRegistry DAORegistry instance containing custom access control roles. */ - function initialize( - DAORoles roles, - IGovernanceToken governanceToken - ) public initializer { - require( - address(roles) != address(0) && - address(governanceToken) != address(0), - "InternalMarket: 0x0 not allowed" - ); - _initialize(governanceToken, 7 days); - _setRoles(roles); + function initialize(DAORegistry daoRegistry) public initializer { + _setDAORegistry(daoRegistry); + _initialize(7 days); } /// @custom:oz-upgrades-unsafe-allow constructor @@ -51,8 +39,8 @@ contract InternalMarket is Initializable, HasRole, InternalMarketBase { */ function makeOffer(uint256 amount) public virtual { require( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.CONTRIBUTOR_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().CONTRIBUTOR_STATUS(), msg.sender ), "InternalMarket: only contributors can make offers" @@ -101,30 +89,6 @@ contract InternalMarket is Initializable, HasRole, InternalMarketBase { _redeem(_msgSender(), amount); } - /** - * @dev Set internal token reference. - * @param token The address of the internal governance token. - */ - function setTokenInternal( - IGovernanceToken token - ) public onlyRole(Roles.RESOLUTION_ROLE) zeroCheck(address(token)) { - _setTokenInternal(token); - } - - /** - * @dev Set shareholder registry reference. - * @param shareholderRegistry The address of the shareholder registry contract. - */ - function setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) - public - onlyRole(Roles.RESOLUTION_ROLE) - zeroCheck(address(shareholderRegistry)) - { - _setShareholderRegistry(shareholderRegistry); - } - /** * @dev Set the exchange pair and Oracle reference. * @param token The address of the ERC20 token to set as the exchange pair. @@ -135,7 +99,7 @@ contract InternalMarket is Initializable, HasRole, InternalMarketBase { IDIAOracleV2 oracle ) public - onlyRole(Roles.RESOLUTION_ROLE) + onlyResolutionManager zeroCheck(address(token)) zeroCheck(address(oracle)) { @@ -148,31 +112,15 @@ contract InternalMarket is Initializable, HasRole, InternalMarketBase { */ function setReserve( address reserve_ - ) public onlyRole(Roles.RESOLUTION_ROLE) zeroCheck(address(reserve_)) { + ) public onlyResolutionManager zeroCheck(address(reserve_)) { _setReserve(reserve_); } - /** - * @dev Set redemption controller address. - * @param redemptionController_ The address of the redemption controller. - */ - function setRedemptionController( - IRedemptionController redemptionController_ - ) - public - onlyRole(Roles.RESOLUTION_ROLE) - zeroCheck(address(redemptionController_)) - { - _setRedemptionController(redemptionController_); - } - /** * @dev Set offer duration. * @param duration The duration of the offer in seconds. */ - function setOfferDuration( - uint duration - ) public onlyRole(Roles.RESOLUTION_ROLE) { + function setOfferDuration(uint duration) public onlyResolutionManager { _setOfferDuration(duration); } } diff --git a/contracts/InternalMarket/InternalMarketBase.sol b/contracts/InternalMarket/InternalMarketBase.sol index c50d73d..7faaa2d 100644 --- a/contracts/InternalMarket/InternalMarketBase.sol +++ b/contracts/InternalMarket/InternalMarketBase.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; +import "../extensions/DAORegistryProxy.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../ShareholderRegistry/IShareholderRegistry.sol"; import "../RedemptionController/IRedemptionController.sol"; @@ -8,7 +9,7 @@ import "./IDIAOracleV2.sol"; import "../NeokingdomToken/INeokingdomToken.sol"; import "../GovernanceToken/IGovernanceToken.sol"; -contract InternalMarketBase { +contract InternalMarketBase is DAORegistryProxy { event OfferCreated( uint128 id, address from, @@ -30,14 +31,10 @@ contract InternalMarketBase { mapping(uint128 => Offer) offer; } - IGovernanceToken public tokenInternal; - // Cannot use IERC20 here because it lacks `decimals` ERC20 public exchangeToken; - IRedemptionController public redemptionController; IDIAOracleV2 public priceOracle; - IShareholderRegistry internal _shareholderRegistry; address public reserve; uint256 public offerDuration; @@ -46,11 +43,7 @@ contract InternalMarketBase { mapping(address => uint256) internal _vaultContributors; - function _initialize( - IGovernanceToken _governanceToken, - uint256 _offerDuration - ) internal virtual { - tokenInternal = _governanceToken; + function _initialize(uint256 _offerDuration) internal virtual { offerDuration = _offerDuration; } @@ -62,16 +55,6 @@ contract InternalMarketBase { return offers.end++; } - function _setTokenInternal(IGovernanceToken token) internal virtual { - tokenInternal = token; - } - - function _setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) internal virtual { - _shareholderRegistry = shareholderRegistry; - } - function _setExchangePair( ERC20 token, IDIAOracleV2 oracle @@ -84,12 +67,6 @@ contract InternalMarketBase { reserve = reserve_; } - function _setRedemptionController( - IRedemptionController redemptionController_ - ) internal virtual { - redemptionController = redemptionController_; - } - function _setOfferDuration(uint duration) internal virtual { offerDuration = duration; } @@ -103,10 +80,10 @@ contract InternalMarketBase { emit OfferCreated(id, from, amount, expiredAt); require( - tokenInternal.transferFrom(from, address(this), amount), + getGovernanceToken().transferFrom(from, address(this), amount), "InternalMarketBase: transfer failed" ); - redemptionController.afterOffer(from, amount); + getRedemptionController().afterOffer(from, amount); } function _beforeWithdraw(address from, uint256 amount) internal virtual { @@ -179,7 +156,7 @@ contract InternalMarketBase { ) internal virtual { _beforeMatchOffer(from, to, amount); require( - tokenInternal.transfer(to, amount), + getGovernanceToken().transfer(to, amount), "InternalMarketBase: transfer failed" ); require( @@ -194,15 +171,15 @@ contract InternalMarketBase { uint256 amount ) internal virtual { if ( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.CONTRIBUTOR_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().CONTRIBUTOR_STATUS(), from ) ) { _beforeWithdraw(from, amount); - tokenInternal.unwrap(address(this), to, amount); + getGovernanceToken().unwrap(address(this), to, amount); } else { - tokenInternal.unwrap(from, to, amount); + getGovernanceToken().unwrap(from, to, amount); } emit Withdrawn(from, to, amount); @@ -210,21 +187,21 @@ contract InternalMarketBase { function _burn(address from, uint256 amount) internal virtual { assert( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.CONTRIBUTOR_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().CONTRIBUTOR_STATUS(), from ) ); _beforeWithdraw(from, amount); - tokenInternal.burn(address(this), amount); + getGovernanceToken().burn(address(this), amount); } function _deposit(address to, uint256 amount) internal virtual { - tokenInternal.wrap(to, amount); + getGovernanceToken().wrap(to, amount); } function _finalizeDeposit(address to) internal virtual { - tokenInternal.settleTokens(to); + getGovernanceToken().settleTokens(to); } function _redeem(address from, uint256 amount) internal virtual { @@ -233,7 +210,7 @@ contract InternalMarketBase { uint256 difference = amount - withdrawableBalance; // governanceToken is an address set by the operators of the DAO, hence trustworthy // slither-disable-start reentrancy-no-eth - tokenInternal.burn(from, difference); + getGovernanceToken().burn(from, difference); _burn(from, withdrawableBalance); // slither-disable-end reentrancy-no-eth } else { @@ -246,7 +223,7 @@ contract InternalMarketBase { exchangeToken.transferFrom(reserve, from, _convertToUSDC(amount)), "InternalMarketBase: transfer failed" ); - redemptionController.afterRedeem(from, amount); + getRedemptionController().afterRedeem(from, amount); } function _convertToUSDC( diff --git a/contracts/RedemptionController/RedemptionController.sol b/contracts/RedemptionController/RedemptionController.sol index 155c6cc..152d88a 100644 --- a/contracts/RedemptionController/RedemptionController.sol +++ b/contracts/RedemptionController/RedemptionController.sol @@ -3,9 +3,6 @@ pragma solidity 0.8.16; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "./RedemptionControllerBase.sol"; -import { Roles } from "../extensions/Roles.sol"; -import "../extensions/DAORoles.sol"; -import "../extensions/HasRole.sol"; /** * @title RedemptionController @@ -25,21 +22,13 @@ import "../extensions/HasRole.sol"; * redeemable. They can only be moved outside the vault (contributor or * secondary). */ -contract RedemptionController is - Initializable, - HasRole, - RedemptionControllerBase -{ +contract RedemptionController is Initializable, RedemptionControllerBase { /** * @dev Initializes the smart contract. - * @param roles The addresses of DAORoles for this contract. + * @param daoRegistry The addresses of DAORoles for this contract. */ - function initialize(DAORoles roles) public initializer { - require( - address(roles) != address(0), - "RedemptionController: 0x0 not allowed" - ); - _setRoles(roles); + function initialize(DAORegistry daoRegistry) public initializer { + _setDAORegistry(daoRegistry); _initialize(); } @@ -58,7 +47,7 @@ contract RedemptionController is function afterMint( address to, uint256 amount - ) external override onlyRole(Roles.TOKEN_MANAGER_ROLE) { + ) external override onlyGovernanceToken { _afterMint(to, amount); } @@ -71,7 +60,7 @@ contract RedemptionController is function afterOffer( address account, uint256 amount - ) external override onlyRole(Roles.TOKEN_MANAGER_ROLE) { + ) external override onlyInternalMarket { _afterOffer(account, amount); } @@ -84,7 +73,7 @@ contract RedemptionController is function afterRedeem( address account, uint256 amount - ) external override onlyRole(Roles.TOKEN_MANAGER_ROLE) { + ) external override onlyInternalMarket { _afterRedeem(account, amount); } } diff --git a/contracts/RedemptionController/RedemptionControllerBase.sol b/contracts/RedemptionController/RedemptionControllerBase.sol index 4d019b3..c68d0a2 100644 --- a/contracts/RedemptionController/RedemptionControllerBase.sol +++ b/contracts/RedemptionController/RedemptionControllerBase.sol @@ -2,10 +2,13 @@ pragma solidity 0.8.16; import "./IRedemptionController.sol"; -import "hardhat/console.sol"; +import "../extensions/DAORegistryProxy.sol"; // The contract tells how many tokens are redeemable by Contributors -abstract contract RedemptionControllerBase is IRedemptionController { +abstract contract RedemptionControllerBase is + IRedemptionController, + DAORegistryProxy +{ struct Redeemable { uint256 amount; uint256 mintTimestamp; diff --git a/contracts/ResolutionManager/ResolutionManager.sol b/contracts/ResolutionManager/ResolutionManager.sol index 0ef7e19..0453f57 100644 --- a/contracts/ResolutionManager/ResolutionManager.sol +++ b/contracts/ResolutionManager/ResolutionManager.sol @@ -1,50 +1,27 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; +import "../extensions/DAORegistryProxy.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import { Roles } from "../extensions/Roles.sol"; import "./ResolutionManagerBase.sol"; -import "../extensions/DAORoles.sol"; -import "../extensions/HasRole.sol"; /** * @title ResolutionManager * @dev This contract manages the creation, approval, rejection, updating and * execution of resolutions. It also allows shareholders to vote on resolutions. */ -contract ResolutionManager is Initializable, ResolutionManagerBase, HasRole { +contract ResolutionManager is Initializable, ResolutionManagerBase { /** * @dev Initializes the contract with the required dependencies. - * @param roles The roles extension of the DAO. - * @param shareholderRegistry The registry of shareholders of the DAO. - * @param governanceToken The governance token of the DAO. - * @param voting The voting extension of the DAO. + * @param daoRegistry The roles extension of the DAO. */ - function initialize( - DAORoles roles, - IShareholderRegistry shareholderRegistry, - IGovernanceToken governanceToken, - IVoting voting - ) public initializer { - require( - address(roles) != address(0) && - address(shareholderRegistry) != address(0) && - address(governanceToken) != address(0) && - address(voting) != address(0), - "ResolutionManager: 0x0 not allowed" - ); - _setRoles(roles); - _initialize(shareholderRegistry, governanceToken, voting); + function initialize(DAORegistry daoRegistry) public initializer { + _setDAORegistry(daoRegistry); } /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} - modifier zeroCheck(address address_) { - require(address_ != address(0), "ResolutionManager: 0x0 not allowed"); - _; - } - /** * @dev Adds a new resolution type. * @param name The name of the new resolution type. @@ -59,7 +36,7 @@ contract ResolutionManager is Initializable, ResolutionManagerBase, HasRole { uint256 noticePeriod, uint256 votingPeriod, bool canBeNegative - ) public virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual onlyResolutionManager { _addResolutionType( name, quorum, @@ -69,51 +46,6 @@ contract ResolutionManager is Initializable, ResolutionManagerBase, HasRole { ); } - /** - * @dev Sets the shareholder registry. - * @param shareholderRegistry The new shareholder registry. - */ - function setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(shareholderRegistry)) - { - _setShareholderRegistry(shareholderRegistry); - } - - /** - * @dev Sets the governance token. - * @param governanceToken The new governance token. - */ - function setGovernanceToken( - IGovernanceToken governanceToken - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(governanceToken)) - { - _setGovernanceToken(governanceToken); - } - - /** - * @dev Sets the voting extension. - * @param voting The new voting extension. - */ - function setVoting( - IVoting voting - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(voting)) - { - _setVoting(voting); - } - /** * @dev Creates a new resolution. * @param dataURI The data URI of the resolution. diff --git a/contracts/ResolutionManager/ResolutionManagerBase.sol b/contracts/ResolutionManager/ResolutionManagerBase.sol index 819e139..5449f69 100644 --- a/contracts/ResolutionManager/ResolutionManagerBase.sol +++ b/contracts/ResolutionManager/ResolutionManagerBase.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import "../ShareholderRegistry/IShareholderRegistry.sol"; -import "../GovernanceToken/IGovernanceToken.sol"; -import "../Voting/IVoting.sol"; +import "../extensions/DAORegistryProxy.sol"; -abstract contract ResolutionManagerBase { +contract ResolutionManagerBase is DAORegistryProxy { event ResolutionCreated(address indexed from, uint256 indexed resolutionId); event ResolutionUpdated(address indexed from, uint256 indexed resolutionId); @@ -71,23 +69,11 @@ abstract contract ResolutionManagerBase { uint256 internal _currentResolutionId; - IShareholderRegistry internal _shareholderRegistry; - IGovernanceToken internal _governanceToken; - IVoting internal _voting; - ResolutionType[] public resolutionTypes; mapping(uint256 => Resolution) public resolutions; - function _initialize( - IShareholderRegistry shareholderRegistry, - IGovernanceToken governanceToken, - IVoting voting - ) internal { - _shareholderRegistry = shareholderRegistry; - _governanceToken = governanceToken; - _voting = voting; - + function _initialize() internal { // TODO: check if there are any rounding errors _addResolutionType("amendment", 66, 14 days, 6 days, false); _addResolutionType("capitalChange", 66, 14 days, 6 days, false); @@ -124,27 +110,11 @@ abstract contract ResolutionManagerBase { _; } - function _setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) internal virtual { - _shareholderRegistry = shareholderRegistry; - } - - function _setGovernanceToken( - IGovernanceToken governanceToken - ) internal virtual { - _governanceToken = governanceToken; - } - - function _setVoting(IVoting voting) internal virtual { - _voting = voting; - } - function _snapshotAll() internal virtual returns (uint256) { - uint256 snapshotId = _shareholderRegistry.snapshot(); + uint256 snapshotId = getShareholderRegistry().snapshot(); require( - _governanceToken.snapshot() == snapshotId && - _voting.snapshot() == snapshotId, + getGovernanceToken().snapshot() == snapshotId && + getVoting().snapshot() == snapshotId, "ResolutionManager: snapshot ids are inconsistent" ); @@ -163,8 +133,8 @@ abstract contract ResolutionManagerBase { resolutionTypeId ]; require( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.CONTRIBUTOR_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().CONTRIBUTOR_STATUS(), msg.sender ), "Resolution: only contributor can create" @@ -197,8 +167,8 @@ abstract contract ResolutionManagerBase { ) internal virtual onlyPending(resolutionId) exists(resolutionId) { emit ResolutionApproved(msg.sender, resolutionId); require( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.MANAGING_BOARD_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().MANAGING_BOARD_STATUS(), msg.sender ), "Resolution: only managing board can approve" @@ -217,13 +187,13 @@ abstract contract ResolutionManagerBase { bytes32 originalStatus; if (addressedContributor != address(0)) { - originalDelegate = _voting.getDelegate(addressedContributor); - originalStatus = _shareholderRegistry.getStatus( + originalDelegate = getVoting().getDelegate(addressedContributor); + originalStatus = getShareholderRegistry().getStatus( addressedContributor ); // Downgrading to investor removes delegation - _shareholderRegistry.setStatus( - _shareholderRegistry.INVESTOR_STATUS(), + getShareholderRegistry().setStatus( + getShareholderRegistry().INVESTOR_STATUS(), addressedContributor ); } @@ -231,12 +201,15 @@ abstract contract ResolutionManagerBase { resolution.snapshotId = _snapshotAll(); if (addressedContributor != address(0)) { - _shareholderRegistry.setStatus( + getShareholderRegistry().setStatus( originalStatus, addressedContributor ); if (addressedContributor != originalDelegate) { - _voting.delegateFrom(addressedContributor, originalDelegate); + getVoting().delegateFrom( + addressedContributor, + originalDelegate + ); } } } @@ -247,8 +220,8 @@ abstract contract ResolutionManagerBase { emit ResolutionRejected(msg.sender, resolutionId); require( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.MANAGING_BOARD_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().MANAGING_BOARD_STATUS(), msg.sender ), "Resolution: only managing board can reject" @@ -276,8 +249,8 @@ abstract contract ResolutionManagerBase { ); require( - _shareholderRegistry.isAtLeast( - _shareholderRegistry.MANAGING_BOARD_STATUS(), + getShareholderRegistry().isAtLeast( + getShareholderRegistry().MANAGING_BOARD_STATUS(), msg.sender ), "Resolution: only managing board can update" @@ -342,7 +315,7 @@ abstract contract ResolutionManagerBase { ); require( - _voting.canVoteAt(msg.sender, resolution.snapshotId), + getVoting().canVoteAt(msg.sender, resolution.snapshotId), "Resolution: account cannot vote" ); @@ -359,11 +332,11 @@ abstract contract ResolutionManagerBase { "Resolution: not votable" ); - uint256 votingPower = _voting.getVotingPowerAt( + uint256 votingPower = getVoting().getVotingPowerAt( msg.sender, resolution.snapshotId ); - address delegate = _voting.getDelegateAt( + address delegate = getVoting().getDelegateAt( msg.sender, resolution.snapshotId ); @@ -371,11 +344,11 @@ abstract contract ResolutionManagerBase { // If sender has a delegate, load voting power from GovernanceToken if (delegate != msg.sender) { votingPower = - _governanceToken.balanceOfAt( + getGovernanceToken().balanceOfAt( msg.sender, resolution.snapshotId ) + - _shareholderRegistry.balanceOfAt( + getShareholderRegistry().balanceOfAt( msg.sender, resolution.snapshotId ); @@ -466,7 +439,7 @@ abstract contract ResolutionManagerBase { Resolution storage resolution = resolutions[resolutionId]; require( voter != resolution.addressedContributor && - _voting.canVoteAt(voter, resolution.snapshotId), + getVoting().canVoteAt(voter, resolution.snapshotId), "Resolution: account cannot vote" ); @@ -474,15 +447,18 @@ abstract contract ResolutionManagerBase { hasVoted = resolution.hasVoted[voter]; if ( - _voting.getDelegateAt(voter, resolution.snapshotId) != voter && + getVoting().getDelegateAt(voter, resolution.snapshotId) != voter && hasVoted ) { votingPower = - _governanceToken.balanceOfAt(voter, resolution.snapshotId) + - _shareholderRegistry.balanceOfAt(voter, resolution.snapshotId); + getGovernanceToken().balanceOfAt(voter, resolution.snapshotId) + + getShareholderRegistry().balanceOfAt( + voter, + resolution.snapshotId + ); } else { votingPower = - _voting.getVotingPowerAt(voter, resolution.snapshotId) - + getVoting().getVotingPowerAt(voter, resolution.snapshotId) - resolution.lostVotingPower[voter]; } } @@ -494,7 +470,7 @@ abstract contract ResolutionManagerBase { ResolutionType storage resolutionType = resolutionTypes[ resolution.resolutionTypeId ]; - uint256 totalVotingPower = _voting.getTotalVotingPowerAt( + uint256 totalVotingPower = getVoting().getTotalVotingPowerAt( resolution.snapshotId ); diff --git a/contracts/ShareholderRegistry/ShareholderRegistry.sol b/contracts/ShareholderRegistry/ShareholderRegistry.sol index 42265d2..9831853 100644 --- a/contracts/ShareholderRegistry/ShareholderRegistry.sol +++ b/contracts/ShareholderRegistry/ShareholderRegistry.sol @@ -3,37 +3,25 @@ pragma solidity 0.8.16; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "./ShareholderRegistrySnapshot.sol"; -import { Roles } from "../extensions/Roles.sol"; -import "../extensions/DAORoles.sol"; -import "../extensions/HasRole.sol"; /** * @title ShareholderRegistry * @notice ShareholderRegistry provides an implementation of a registry system for * a) holding and distributing shares; and b) set the status of the holders. */ -contract ShareholderRegistry is - Initializable, - ShareholderRegistrySnapshot, - HasRole -{ +contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { /** * @notice Initializes the ShareholderRegistry contract with the given token name, symbol, and DAO roles. - * @param roles DAORoles struct containing DAO-specific roles. + * @param daoRegistry DAORegistry struct containing DAO-specific roles. * @param name string value representing the name of the token. * @param symbol string value representing the symbol of the token. */ function initialize( - DAORoles roles, + DAORegistry daoRegistry, string memory name, string memory symbol ) public initializer { - require( - address(roles) != address(0), - "ShareholderRegistry: 0x0 not allowed" - ); - _initialize(name, symbol); - _setRoles(roles); + _initialize(daoRegistry, name, symbol); } /// @custom:oz-upgrades-unsafe-allow constructor @@ -72,22 +60,6 @@ contract ShareholderRegistry is _setStatus(status, account); } - /** - * @notice Sets the voting implementation for the registry. - * @dev Requires the sender to have the OPERATOR_ROLE. - * @param voting contract instance to set as the current voting contract. - */ - function setVoting( - IVoting voting - ) - external - virtual - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(voting)) - { - _setVoting(voting); - } - /** * @notice Mints new shares and assigns them to the specified shareholder address. * @dev Requires the sender to have the RESOLUTION_ROLE. diff --git a/contracts/ShareholderRegistry/ShareholderRegistryBase.sol b/contracts/ShareholderRegistry/ShareholderRegistryBase.sol index 9df6737..4254178 100644 --- a/contracts/ShareholderRegistry/ShareholderRegistryBase.sol +++ b/contracts/ShareholderRegistry/ShareholderRegistryBase.sol @@ -1,18 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; +import "../extensions/DAORegistryProxy.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "../Voting/IVoting.sol"; -contract ShareholderRegistryBase is ERC20Upgradeable { +contract ShareholderRegistryBase is ERC20Upgradeable, DAORegistryProxy { bytes32 public SHAREHOLDER_STATUS; bytes32 public INVESTOR_STATUS; bytes32 public CONTRIBUTOR_STATUS; bytes32 public MANAGING_BOARD_STATUS; - IVoting internal _voting; - event StatusChanged( address indexed account, bytes32 previous, @@ -22,9 +21,11 @@ contract ShareholderRegistryBase is ERC20Upgradeable { mapping(address => bytes32) internal _statuses; function _initialize( + DAORegistry daoRegistry, string memory name, string memory symbol ) internal virtual { + _setDAORegistry(daoRegistry); __ERC20_init(name, symbol); SHAREHOLDER_STATUS = keccak256("SHAREHOLDER_STATUS"); INVESTOR_STATUS = keccak256("INVESTOR_STATUS"); @@ -32,10 +33,6 @@ contract ShareholderRegistryBase is ERC20Upgradeable { MANAGING_BOARD_STATUS = keccak256("MANAGING_BOARD_STATUS"); } - function _setVoting(IVoting voting) internal virtual { - _voting = voting; - } - function _setStatus(bytes32 status, address account) internal virtual { require( !Address.isContract(account), @@ -104,7 +101,7 @@ contract ShareholderRegistryBase is ERC20Upgradeable { _isAtLeast(1, statusBefore, CONTRIBUTOR_STATUS) && !_isAtLeast(1, statusAfter, CONTRIBUTOR_STATUS) ) { - _voting.beforeRemoveContributor(account); + getVoting().beforeRemoveContributor(account); } } @@ -117,7 +114,7 @@ contract ShareholderRegistryBase is ERC20Upgradeable { !_isAtLeast(1, statusBefore, CONTRIBUTOR_STATUS) && _isAtLeast(1, statusAfter, CONTRIBUTOR_STATUS) ) { - _voting.afterAddContributor(account); + getVoting().afterAddContributor(account); } } @@ -142,6 +139,6 @@ contract ShareholderRegistryBase is ERC20Upgradeable { if (balanceOf(from) == 0) { _setStatus(0, from); } - _voting.afterTokenTransfer(from, to, amount); + getVoting().afterTokenTransfer(from, to, amount); } } diff --git a/contracts/extensions/DAORegistry.sol b/contracts/extensions/DAORegistry.sol new file mode 100644 index 0000000..cacb736 --- /dev/null +++ b/contracts/extensions/DAORegistry.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.16; + +import "@openzeppelin/contracts/access/AccessControl.sol"; +import "../NeokingdomToken/INeokingdomToken.sol"; +import "../GovernanceToken/IGovernanceToken.sol"; +import "../ShareholderRegistry/IShareholderRegistry.sol"; +import "../Voting/IVoting.sol"; +import "../RedemptionController/IRedemptionController.sol"; +import "../ResolutionManager/ResolutionManager.sol"; +import "../InternalMarket/InternalMarket.sol"; + +contract DAORegistry is AccessControl { + INeokingdomToken internal _neokingdomToken; + IGovernanceToken internal _governanceToken; + IShareholderRegistry internal _shareholderRegistry; + IVoting internal _voting; + IRedemptionController internal _redemptionController; + ResolutionManager internal _resolutionManager; + InternalMarket internal _internalMarket; + + constructor() { + _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /* + function initialize() { + + } + */ + + function setNeokingdomToken( + INeokingdomToken neokingdomToken + ) public onlyRole(Roles.OPERATOR_ROLE) { + _neokingdomToken = neokingdomToken; + } + + function getNeokingdomToken() public view returns (INeokingdomToken) { + return _neokingdomToken; + } + + function requireMsgSenderToBeNeokingdomToken() public view { + address account = _msgSender(); + require( + account == address(_neokingdomToken), + "DAORoles: sender is not NeokingdomToken" + ); + } + + function setGovernanceToken( + IGovernanceToken governanceToken + ) public onlyRole(Roles.OPERATOR_ROLE) { + _governanceToken = governanceToken; + } + + function getGovernanceToken() public view returns (IGovernanceToken) { + return _governanceToken; + } + + function requireMsgSenderToBeGovernanceToken() public view { + address account = _msgSender(); + require( + account == address(_governanceToken), + "DAORoles: sender is not GovernanceToken" + ); + } + + function setShareholderRegistry( + IShareholderRegistry shareholderRegistry + ) public onlyRole(Roles.OPERATOR_ROLE) { + _shareholderRegistry = shareholderRegistry; + } + + function getShareholderRegistry() + public + view + returns (IShareholderRegistry) + { + return _shareholderRegistry; + } + + function requireMsgSenderToBeShareholderRegistry() public view { + address account = _msgSender(); + require( + account == address(_shareholderRegistry), + "DAORoles: sender is not ShareholderRegistry" + ); + } + + function setVoting(IVoting voting) public onlyRole(Roles.OPERATOR_ROLE) { + _voting = voting; + } + + function getVoting() public view returns (IVoting) { + return _voting; + } + + function requireMsgSenderToBeVoting() public view { + address account = _msgSender(); + require(account == address(_voting), "DAORoles: sender is not Voting"); + } + + function setRedemptionController( + IRedemptionController redemptionController + ) public onlyRole(Roles.OPERATOR_ROLE) { + _redemptionController = redemptionController; + } + + function getRedemptionController() + public + view + returns (IRedemptionController) + { + return _redemptionController; + } + + function requireMsgSenderToBeRedemptionController() public view { + address account = _msgSender(); + require( + account == address(_redemptionController), + "DAORoles: sender is not RedemptionController" + ); + } + + function setResolutionManager( + ResolutionManager resolutionManager + ) public onlyRole(Roles.OPERATOR_ROLE) { + _resolutionManager = resolutionManager; + } + + function getResolutionManager() public view returns (ResolutionManager) { + return _resolutionManager; + } + + function requireMsgSenderToBeResolutionManager() public view { + address account = _msgSender(); + require( + account == address(_resolutionManager), + "DAORoles: sender is not ResolutionManager" + ); + } + + function setInternalMarket( + InternalMarket internalMarket + ) public onlyRole(Roles.OPERATOR_ROLE) { + _internalMarket = internalMarket; + } + + function getInternalMarket() public view returns (InternalMarket) { + return _internalMarket; + } + + function requireMsgSenderToBeInternalMarket() public view { + address account = _msgSender(); + require( + account == address(_internalMarket), + "DAORoles: sender is not InternalMarket" + ); + } +} diff --git a/contracts/extensions/DAORegistryProxy.sol b/contracts/extensions/DAORegistryProxy.sol new file mode 100644 index 0000000..da089f1 --- /dev/null +++ b/contracts/extensions/DAORegistryProxy.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.16; + +import "./DAORegistry.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract DAORegistryProxy is ContextUpgradeable { + DAORegistry internal _daoRegistry; + + function _setDAORegistry(DAORegistry daoRegistry) internal { + require( + address(daoRegistry) != address(0), + "DAORegistryProxy: 0x0 not allowed" + ); + _daoRegistry = daoRegistry; + } + + function getDAORegistry() public view returns (DAORegistry) { + return _daoRegistry; + } + + // Neokingdom Token contract + + function getNeokingdomToken() public view returns (INeokingdomToken) { + return _daoRegistry.getNeokingdomToken(); + } + + modifier onlyNeokingdomToken() { + _daoRegistry.requireMsgSenderToBeNeokingdomToken(); + _; + } + + // Governance Token contract + + function getGovernanceToken() public view returns (IGovernanceToken) { + return _daoRegistry.getGovernanceToken(); + } + + modifier onlyGovernanceToken() { + _daoRegistry.requireMsgSenderToBeGovernanceToken(); + _; + } + + // Shareholders' Registry contract + + function getShareholderRegistry() + public + view + returns (IShareholderRegistry) + { + return _daoRegistry.getShareholderRegistry(); + } + + modifier onlyShareholderRegistry() { + _daoRegistry.requireMsgSenderToBeShareholderRegistry(); + _; + } + + // Voting contract + + function getVoting() public view returns (IVoting) { + return _daoRegistry.getVoting(); + } + + modifier onlyVoting() { + _daoRegistry.requireMsgSenderToBeVoting(); + _; + } + + // Redemption Controller contract + + function getRedemptionController() + public + view + returns (IRedemptionController) + { + return _daoRegistry.getRedemptionController(); + } + + modifier onlyRedemptionController() { + _daoRegistry.requireMsgSenderToBeRedemptionController(); + _; + } + + // Resolution Manager contract + + function getResolutionManager() public view returns (ResolutionManager) { + return _daoRegistry.getResolutionManager(); + } + + modifier onlyResolutionManager() { + _daoRegistry.requireMsgSenderToBeResolutionManager(); + _; + } + + // Internal Market contract + + function getInternalMarket() public view returns (InternalMarket) { + return _daoRegistry.getInternalMarket(); + } + + modifier onlyInternalMarket() { + _daoRegistry.requireMsgSenderToBeInternalMarket(); + _; + } +} diff --git a/contracts/extensions/DAORoles.sol b/contracts/extensions/DAORoles.sol deleted file mode 100644 index 7d2d9c8..0000000 --- a/contracts/extensions/DAORoles.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.16; - -import "@openzeppelin/contracts/access/AccessControl.sol"; - -contract DAORoles is AccessControl { - constructor() { - _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } -} diff --git a/contracts/extensions/HasRole.sol b/contracts/extensions/HasRole.sol deleted file mode 100644 index 2c760d6..0000000 --- a/contracts/extensions/HasRole.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.16; - -import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "./DAORoles.sol"; -import { Roles } from "./Roles.sol"; - -abstract contract HasRole is ContextUpgradeable { - DAORoles internal _roles; - - function _setRoles(DAORoles roles) internal { - _roles = roles; - } - - function setRoles(DAORoles roles) public onlyRole(Roles.OPERATOR_ROLE) { - _setRoles(roles); - } - - function getRoles() public view returns (DAORoles) { - return _roles; - } - - modifier onlyRole(bytes32 role) { - address account = _msgSender(); - if (!_roles.hasRole(role, account)) { - revert( - string( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(account), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ) - ); - } - _; - } -} diff --git a/contracts/extensions/Roles.sol b/contracts/extensions/Roles.sol deleted file mode 100644 index 5ca5b28..0000000 --- a/contracts/extensions/Roles.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.16; - -library Roles { - bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); - bytes32 public constant RESOLUTION_ROLE = keccak256("RESOLUTION_ROLE"); - bytes32 public constant ESCROW_ROLE = keccak256("ESCROW_ROLE"); - bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); - bytes32 public constant MARKET_ROLE = keccak256("MARKET_ROLE"); - bytes32 public constant SHAREHOLDER_REGISTRY_ROLE = - keccak256("SHAREHOLDER_REGISTRY_ROLE"); - bytes32 public constant TOKEN_MANAGER_ROLE = - keccak256("TOKEN_MANAGER_ROLE"); -} From b8cff7350ee8f70bda3fdcb6b0a1d998d739b001 Mon Sep 17 00:00:00 2001 From: Alberto Granzotto Date: Tue, 14 Nov 2023 15:38:48 +0100 Subject: [PATCH 2/4] Rename init function --- contracts/GovernanceToken/GovernanceToken.sol | 2 +- contracts/InternalMarket/InternalMarket.sol | 2 +- contracts/RedemptionController/RedemptionController.sol | 2 +- contracts/ResolutionManager/ResolutionManager.sol | 2 +- contracts/ShareholderRegistry/ShareholderRegistryBase.sol | 2 +- contracts/extensions/DAORegistryProxy.sol | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/GovernanceToken/GovernanceToken.sol b/contracts/GovernanceToken/GovernanceToken.sol index b6fe00e..59a61c0 100644 --- a/contracts/GovernanceToken/GovernanceToken.sol +++ b/contracts/GovernanceToken/GovernanceToken.sol @@ -43,7 +43,7 @@ contract GovernanceToken is Initializable, GovernanceTokenSnapshot { string memory name, string memory symbol ) public initializer { - _setDAORegistry(daoRegistry); + __DAORegistryProxy_init(daoRegistry); _initialize(name, symbol); } diff --git a/contracts/InternalMarket/InternalMarket.sol b/contracts/InternalMarket/InternalMarket.sol index 975c8f2..9696002 100644 --- a/contracts/InternalMarket/InternalMarket.sol +++ b/contracts/InternalMarket/InternalMarket.sol @@ -21,7 +21,7 @@ contract InternalMarket is Initializable, InternalMarketBase { * @param daoRegistry DAORegistry instance containing custom access control roles. */ function initialize(DAORegistry daoRegistry) public initializer { - _setDAORegistry(daoRegistry); + __DAORegistryProxy_init(daoRegistry); _initialize(7 days); } diff --git a/contracts/RedemptionController/RedemptionController.sol b/contracts/RedemptionController/RedemptionController.sol index 152d88a..a77b740 100644 --- a/contracts/RedemptionController/RedemptionController.sol +++ b/contracts/RedemptionController/RedemptionController.sol @@ -28,7 +28,7 @@ contract RedemptionController is Initializable, RedemptionControllerBase { * @param daoRegistry The addresses of DAORoles for this contract. */ function initialize(DAORegistry daoRegistry) public initializer { - _setDAORegistry(daoRegistry); + __DAORegistryProxy_init(daoRegistry); _initialize(); } diff --git a/contracts/ResolutionManager/ResolutionManager.sol b/contracts/ResolutionManager/ResolutionManager.sol index 0453f57..fa4f3bf 100644 --- a/contracts/ResolutionManager/ResolutionManager.sol +++ b/contracts/ResolutionManager/ResolutionManager.sol @@ -16,7 +16,7 @@ contract ResolutionManager is Initializable, ResolutionManagerBase { * @param daoRegistry The roles extension of the DAO. */ function initialize(DAORegistry daoRegistry) public initializer { - _setDAORegistry(daoRegistry); + __DAORegistryProxy_init(daoRegistry); } /// @custom:oz-upgrades-unsafe-allow constructor diff --git a/contracts/ShareholderRegistry/ShareholderRegistryBase.sol b/contracts/ShareholderRegistry/ShareholderRegistryBase.sol index 4254178..25e1e28 100644 --- a/contracts/ShareholderRegistry/ShareholderRegistryBase.sol +++ b/contracts/ShareholderRegistry/ShareholderRegistryBase.sol @@ -25,7 +25,7 @@ contract ShareholderRegistryBase is ERC20Upgradeable, DAORegistryProxy { string memory name, string memory symbol ) internal virtual { - _setDAORegistry(daoRegistry); + __DAORegistryProxy_init(daoRegistry); __ERC20_init(name, symbol); SHAREHOLDER_STATUS = keccak256("SHAREHOLDER_STATUS"); INVESTOR_STATUS = keccak256("INVESTOR_STATUS"); diff --git a/contracts/extensions/DAORegistryProxy.sol b/contracts/extensions/DAORegistryProxy.sol index da089f1..3feb9d5 100644 --- a/contracts/extensions/DAORegistryProxy.sol +++ b/contracts/extensions/DAORegistryProxy.sol @@ -9,7 +9,7 @@ import "@openzeppelin/contracts/utils/Strings.sol"; contract DAORegistryProxy is ContextUpgradeable { DAORegistry internal _daoRegistry; - function _setDAORegistry(DAORegistry daoRegistry) internal { + function __DAORegistryProxy_init(DAORegistry daoRegistry) internal { require( address(daoRegistry) != address(0), "DAORegistryProxy: 0x0 not allowed" From 06ec9dfe8d42432c6cf146f2ce99ea29b3b049bd Mon Sep 17 00:00:00 2001 From: Alberto Granzotto Date: Tue, 14 Nov 2023 16:12:31 +0100 Subject: [PATCH 3/4] WIP --- .../ResolutionManager/IResolutionManager.sol | 4 + .../ResolutionManagerBase.sol | 2 +- .../ShareholderRegistry.sol | 14 ++-- contracts/Voting/Voting.sol | 73 +++---------------- contracts/Voting/VotingBase.sol | 37 +++------- contracts/extensions/DAORegistry.sol | 42 ++++------- contracts/extensions/DAORegistryProxy.sol | 2 +- contracts/mocks/HasRoleMock.sol | 16 ---- contracts/mocks/NeokingdomTokenV2Mock.sol | 1 - contracts/mocks/ResolutionManagerV2Mock.sol | 1 - 10 files changed, 44 insertions(+), 148 deletions(-) create mode 100644 contracts/ResolutionManager/IResolutionManager.sol delete mode 100644 contracts/mocks/HasRoleMock.sol diff --git a/contracts/ResolutionManager/IResolutionManager.sol b/contracts/ResolutionManager/IResolutionManager.sol new file mode 100644 index 0000000..9314e7a --- /dev/null +++ b/contracts/ResolutionManager/IResolutionManager.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +interface IResolutionManager {} diff --git a/contracts/ResolutionManager/ResolutionManagerBase.sol b/contracts/ResolutionManager/ResolutionManagerBase.sol index 5449f69..d19563a 100644 --- a/contracts/ResolutionManager/ResolutionManagerBase.sol +++ b/contracts/ResolutionManager/ResolutionManagerBase.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.16; import "../extensions/DAORegistryProxy.sol"; -contract ResolutionManagerBase is DAORegistryProxy { +abstract contract ResolutionManagerBase is DAORegistryProxy { event ResolutionCreated(address indexed from, uint256 indexed resolutionId); event ResolutionUpdated(address indexed from, uint256 indexed resolutionId); diff --git a/contracts/ShareholderRegistry/ShareholderRegistry.sol b/contracts/ShareholderRegistry/ShareholderRegistry.sol index 9831853..c187318 100644 --- a/contracts/ShareholderRegistry/ShareholderRegistry.sol +++ b/contracts/ShareholderRegistry/ShareholderRegistry.sol @@ -41,7 +41,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { public virtual override - onlyRole(Roles.RESOLUTION_ROLE) + onlyResolutionManager returns (uint256) { return _snapshot(); @@ -56,7 +56,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { function setStatus( bytes32 status, address account - ) public virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual onlyResolutionManager { _setStatus(status, account); } @@ -69,7 +69,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { function mint( address account, uint256 amount - ) public virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual onlyResolutionManager { _mint(account, amount); } @@ -82,7 +82,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { function burn( address account, uint256 amount - ) external virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) external virtual onlyResolutionManager { _burn(account, amount); } @@ -93,7 +93,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { */ function batchTransferFromDAO( address[] memory recipients - ) public virtual onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual onlyResolutionManager { super._batchTransferFromDAO(recipients); } @@ -109,7 +109,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { address from, address to, uint256 amount - ) public virtual override onlyRole(Roles.RESOLUTION_ROLE) returns (bool) { + ) public virtual override onlyResolutionManager returns (bool) { _transfer(from, to, amount); return true; } @@ -124,7 +124,7 @@ contract ShareholderRegistry is Initializable, ShareholderRegistrySnapshot { function transfer( address to, uint256 amount - ) public virtual override onlyRole(Roles.RESOLUTION_ROLE) returns (bool) { + ) public virtual override onlyResolutionManager returns (bool) { return super.transfer(to, amount); } } diff --git a/contracts/Voting/Voting.sol b/contracts/Voting/Voting.sol index 000df4f..8910f14 100644 --- a/contracts/Voting/Voting.sol +++ b/contracts/Voting/Voting.sol @@ -4,78 +4,23 @@ pragma solidity 0.8.16; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "../ShareholderRegistry/IShareholderRegistry.sol"; import "./VotingSnapshot.sol"; -import { Roles } from "../extensions/Roles.sol"; -import "../extensions/DAORoles.sol"; -import "../extensions/HasRole.sol"; /** * @title Voting * @notice The smart contract handles voting power delegation and manages voting snapshots. */ -contract Voting is VotingSnapshot, Initializable, HasRole { +contract Voting is VotingSnapshot, Initializable { /** * @notice Initializes the contract with given DAO roles. - * @param roles Instance of a DAORoles contract. + * @param daoRegistry Instance of a DAORoles contract. */ - function initialize(DAORoles roles) public initializer { - require(address(roles) != address(0), "Voting: 0x0 not allowed"); - _setRoles(roles); + function initialize(DAORegistry daoRegistry) public initializer { + __DAORegistryProxy_init(daoRegistry); } /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} - /** - * @dev Modifier that restricts access to only the token contract. - */ - modifier onlyToken() virtual { - require( - msg.sender == address(_token) || - msg.sender == address(_shareholderRegistry), - "Voting: only Token contract can call this method." - ); - _; - } - - modifier zeroCheck(address address_) { - require(address_ != address(0), "Voting: 0x0 not allowed"); - _; - } - - // Dependencies - - /** - * @notice Sets the token contract address. - * @param token Address of the token contract. - */ - function setToken( - IERC20Upgradeable token - ) - external - virtual - override - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(token)) - { - super._setToken(token); - } - - /** - * @notice Sets the shareholder registry contract address. - * @param shareholderRegistry Address of the shareholder registry contract. - */ - function setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) - external - virtual - override - onlyRole(Roles.OPERATOR_ROLE) - zeroCheck(address(shareholderRegistry)) - { - _setShareholderRegistry(shareholderRegistry); - } - // Snapshottable /** @@ -86,7 +31,7 @@ contract Voting is VotingSnapshot, Initializable, HasRole { public virtual override - onlyRole(Roles.RESOLUTION_ROLE) + onlyResolutionManager returns (uint256) { return _snapshot(); @@ -105,7 +50,7 @@ contract Voting is VotingSnapshot, Initializable, HasRole { address from, address to, uint256 amount - ) external virtual override onlyToken { + ) external virtual override onlyGovernanceToken { _afterTokenTransfer(from, to, amount); } @@ -115,7 +60,7 @@ contract Voting is VotingSnapshot, Initializable, HasRole { */ function beforeRemoveContributor( address account - ) external virtual override onlyRole(Roles.SHAREHOLDER_REGISTRY_ROLE) { + ) external virtual override onlyShareholderRegistry { _beforeRemoveContributor(account); } @@ -125,7 +70,7 @@ contract Voting is VotingSnapshot, Initializable, HasRole { */ function afterAddContributor( address account - ) external virtual override onlyRole(Roles.SHAREHOLDER_REGISTRY_ROLE) { + ) external virtual override onlyShareholderRegistry { _afterAddContributor(account); } @@ -149,7 +94,7 @@ contract Voting is VotingSnapshot, Initializable, HasRole { function delegateFrom( address delegator, address newDelegate - ) public virtual override onlyRole(Roles.RESOLUTION_ROLE) { + ) public virtual override onlyResolutionManager { _delegate(delegator, newDelegate); } } diff --git a/contracts/Voting/VotingBase.sol b/contracts/Voting/VotingBase.sol index fc37a67..241e1d9 100644 --- a/contracts/Voting/VotingBase.sol +++ b/contracts/Voting/VotingBase.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; +import "../extensions/DAORegistryProxy.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "../ShareholderRegistry/IShareholderRegistry.sol"; import "./IVoting.sol"; -abstract contract VotingBase is IVoting { +abstract contract VotingBase is IVoting, DAORegistryProxy { event DelegateChanged( address indexed delegator, address currentDelegate, @@ -18,9 +19,6 @@ abstract contract VotingBase is IVoting { uint256 newVotingPower ); - IShareholderRegistry internal _shareholderRegistry; - IERC20Upgradeable internal _token; - bytes32 internal _contributorRole; mapping(address => address) internal _delegates; @@ -29,17 +27,10 @@ abstract contract VotingBase is IVoting { uint256 internal _totalVotingPower; - // Abstract - function setToken(IERC20Upgradeable token) external virtual; - function beforeRemoveContributor(address account) external virtual; function afterAddContributor(address account) external virtual; - function setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) external virtual; - function canVote(address account) public view virtual returns (bool) { return getDelegate(account) != address(0); } @@ -75,17 +66,6 @@ abstract contract VotingBase is IVoting { // Internal - function _setToken(IERC20Upgradeable token) internal virtual { - _token = token; - } - - function _setShareholderRegistry( - IShareholderRegistry shareholderRegistry - ) internal virtual { - _shareholderRegistry = shareholderRegistry; - _contributorRole = _shareholderRegistry.CONTRIBUTOR_STATUS(); - } - function _afterTokenTransfer( address from, address to, @@ -106,11 +86,11 @@ abstract contract VotingBase is IVoting { // - participants are contributors // (this automatically enforces also that the address is not 0) require( - _shareholderRegistry.isAtLeast(_contributorRole, delegator), + getShareholderRegistry().isAtLeast(_contributorRole, delegator), "Voting: only contributors can delegate." ); require( - _shareholderRegistry.isAtLeast(_contributorRole, newDelegate), + getShareholderRegistry().isAtLeast(_contributorRole, newDelegate), "Voting: only contributors can be delegated." ); // - no sub delegation allowed @@ -138,8 +118,8 @@ abstract contract VotingBase is IVoting { _beforeDelegate(delegator); - uint256 delegatorBalance = _token.balanceOf(delegator) + - _shareholderRegistry.balanceOf(delegator); + uint256 delegatorBalance = getGovernanceToken().balanceOf(delegator) + + getShareholderRegistry().balanceOf(delegator); _delegates[delegator] = newDelegate; if (delegator != newDelegate && newDelegate != address(0)) { @@ -197,8 +177,9 @@ abstract contract VotingBase is IVoting { delete _delegates[account]; - uint256 individualVotingPower = _token.balanceOf(account) + - _shareholderRegistry.balanceOf(account); + uint256 individualVotingPower = getGovernanceToken().balanceOf( + account + ) + getShareholderRegistry().balanceOf(account); if (individualVotingPower > 0) { _moveVotingPower(account, address(0), individualVotingPower); } diff --git a/contracts/extensions/DAORegistry.sol b/contracts/extensions/DAORegistry.sol index cacb736..6f9d7b7 100644 --- a/contracts/extensions/DAORegistry.sol +++ b/contracts/extensions/DAORegistry.sol @@ -2,37 +2,25 @@ pragma solidity 0.8.16; -import "@openzeppelin/contracts/access/AccessControl.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; import "../NeokingdomToken/INeokingdomToken.sol"; import "../GovernanceToken/IGovernanceToken.sol"; import "../ShareholderRegistry/IShareholderRegistry.sol"; import "../Voting/IVoting.sol"; import "../RedemptionController/IRedemptionController.sol"; -import "../ResolutionManager/ResolutionManager.sol"; +import "../ResolutionManager/IResolutionManager.sol"; import "../InternalMarket/InternalMarket.sol"; -contract DAORegistry is AccessControl { +contract DAORegistry is Ownable { INeokingdomToken internal _neokingdomToken; IGovernanceToken internal _governanceToken; IShareholderRegistry internal _shareholderRegistry; IVoting internal _voting; IRedemptionController internal _redemptionController; - ResolutionManager internal _resolutionManager; + IResolutionManager internal _resolutionManager; InternalMarket internal _internalMarket; - constructor() { - _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /* - function initialize() { - - } - */ - - function setNeokingdomToken( - INeokingdomToken neokingdomToken - ) public onlyRole(Roles.OPERATOR_ROLE) { + function setNeokingdomToken(INeokingdomToken neokingdomToken) public owner { _neokingdomToken = neokingdomToken; } @@ -48,9 +36,7 @@ contract DAORegistry is AccessControl { ); } - function setGovernanceToken( - IGovernanceToken governanceToken - ) public onlyRole(Roles.OPERATOR_ROLE) { + function setGovernanceToken(IGovernanceToken governanceToken) public owner { _governanceToken = governanceToken; } @@ -68,7 +54,7 @@ contract DAORegistry is AccessControl { function setShareholderRegistry( IShareholderRegistry shareholderRegistry - ) public onlyRole(Roles.OPERATOR_ROLE) { + ) public owner { _shareholderRegistry = shareholderRegistry; } @@ -88,7 +74,7 @@ contract DAORegistry is AccessControl { ); } - function setVoting(IVoting voting) public onlyRole(Roles.OPERATOR_ROLE) { + function setVoting(IVoting voting) public owner { _voting = voting; } @@ -103,7 +89,7 @@ contract DAORegistry is AccessControl { function setRedemptionController( IRedemptionController redemptionController - ) public onlyRole(Roles.OPERATOR_ROLE) { + ) public owner { _redemptionController = redemptionController; } @@ -124,12 +110,12 @@ contract DAORegistry is AccessControl { } function setResolutionManager( - ResolutionManager resolutionManager - ) public onlyRole(Roles.OPERATOR_ROLE) { + IResolutionManager resolutionManager + ) public owner { _resolutionManager = resolutionManager; } - function getResolutionManager() public view returns (ResolutionManager) { + function getResolutionManager() public view returns (IResolutionManager) { return _resolutionManager; } @@ -141,9 +127,7 @@ contract DAORegistry is AccessControl { ); } - function setInternalMarket( - InternalMarket internalMarket - ) public onlyRole(Roles.OPERATOR_ROLE) { + function setInternalMarket(InternalMarket internalMarket) public owner { _internalMarket = internalMarket; } diff --git a/contracts/extensions/DAORegistryProxy.sol b/contracts/extensions/DAORegistryProxy.sol index 3feb9d5..463b4d6 100644 --- a/contracts/extensions/DAORegistryProxy.sol +++ b/contracts/extensions/DAORegistryProxy.sol @@ -86,7 +86,7 @@ contract DAORegistryProxy is ContextUpgradeable { // Resolution Manager contract - function getResolutionManager() public view returns (ResolutionManager) { + function getResolutionManager() public view returns (IResolutionManager) { return _daoRegistry.getResolutionManager(); } diff --git a/contracts/mocks/HasRoleMock.sol b/contracts/mocks/HasRoleMock.sol deleted file mode 100644 index 65912c8..0000000 --- a/contracts/mocks/HasRoleMock.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.16; - -import "../extensions/HasRole.sol"; - -contract HasRoleMock is Initializable, HasRole { - function initialize(DAORoles roles) public initializer { - _setRoles(roles); - } - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() initializer {} - - function checkOnlyRole(bytes32 role) public onlyRole(role) {} -} diff --git a/contracts/mocks/NeokingdomTokenV2Mock.sol b/contracts/mocks/NeokingdomTokenV2Mock.sol index b65d5f3..814c226 100644 --- a/contracts/mocks/NeokingdomTokenV2Mock.sol +++ b/contracts/mocks/NeokingdomTokenV2Mock.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.16; import "../GovernanceToken/GovernanceToken.sol"; -import "../extensions/Roles.sol"; contract GovernanceTokenV2Mock is GovernanceToken { function transfer(address, uint256) public virtual override returns (bool) { diff --git a/contracts/mocks/ResolutionManagerV2Mock.sol b/contracts/mocks/ResolutionManagerV2Mock.sol index 4f60444..ea4d1a5 100644 --- a/contracts/mocks/ResolutionManagerV2Mock.sol +++ b/contracts/mocks/ResolutionManagerV2Mock.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.16; import "../ResolutionManager/ResolutionManager.sol"; -import "../extensions/Roles.sol"; contract ResolutionManagerV2Mock is ResolutionManager { function reinitialize() public reinitializer(2) { From 7f46171958a5627c80d0b57937130e048244021f Mon Sep 17 00:00:00 2001 From: Alberto Granzotto Date: Tue, 14 Nov 2023 16:12:50 +0100 Subject: [PATCH 4/4] Fix inheritance --- contracts/Voting/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Voting/Voting.sol b/contracts/Voting/Voting.sol index 8910f14..a40515b 100644 --- a/contracts/Voting/Voting.sol +++ b/contracts/Voting/Voting.sol @@ -9,7 +9,7 @@ import "./VotingSnapshot.sol"; * @title Voting * @notice The smart contract handles voting power delegation and manages voting snapshots. */ -contract Voting is VotingSnapshot, Initializable { +contract Voting is VotingSnapshot { /** * @notice Initializes the contract with given DAO roles. * @param daoRegistry Instance of a DAORoles contract.