diff --git a/contracts/AssetParameters.sol b/contracts/AssetParameters.sol index 070a5d7..b1723c9 100644 --- a/contracts/AssetParameters.sol +++ b/contracts/AssetParameters.sol @@ -12,6 +12,7 @@ import "./interfaces/IAssetParameters.sol"; import "./interfaces/ISystemPoolsRegistry.sol"; import "./interfaces/IBasicPool.sol"; import "./interfaces/IPriceManager.sol"; +import "./interfaces/IRoleManager.sol"; import "./libraries/PureParameters.sol"; @@ -50,6 +51,7 @@ contract AssetParameters is IAssetParameters, AbstractDependant { address internal _systemOwnerAddr; ISystemParameters internal _systemParameters; ISystemPoolsRegistry internal _systemPoolsRegistry; + IRoleManager internal _roleManager; mapping(bytes32 => mapping(bytes32 => PureParameters.Param)) internal _parameters; @@ -61,11 +63,8 @@ contract AssetParameters is IAssetParameters, AbstractDependant { _; } - modifier onlySystemOwner() { - require( - msg.sender == _systemOwnerAddr, - "AssetParameters: Only system owner can call this function." - ); + modifier onlyAssetParametersManager() { + _onlyAssetParametersManager(); _; } @@ -75,6 +74,7 @@ contract AssetParameters is IAssetParameters, AbstractDependant { _systemOwnerAddr = registry_.getSystemOwner(); _systemParameters = ISystemParameters(registry_.getSystemParametersContract()); _systemPoolsRegistry = ISystemPoolsRegistry(registry_.getSystemPoolsRegistryContract()); + _roleManager = IRoleManager(registry_.getRoleManagerContract()); } function setPoolInitParams( @@ -103,7 +103,7 @@ contract AssetParameters is IAssetParameters, AbstractDependant { function setupAnnualBorrowRate( bytes32 assetKey_, uint256 newAnnualBorrowRate_ - ) external override onlySystemOwner onlyExists(assetKey_) { + ) external override onlyAssetParametersManager onlyExists(assetKey_) { require( _systemParameters.getStablePoolsAvailability(), "AssetParameters: Stable pools unavailable." @@ -135,34 +135,36 @@ contract AssetParameters is IAssetParameters, AbstractDependant { function setupMainParameters( bytes32 assetKey_, MainPoolParams calldata mainParams_ - ) external override onlySystemOwner onlyExists(assetKey_) { + ) external override onlyAssetParametersManager onlyExists(assetKey_) { _setupMainParameters(assetKey_, mainParams_); } function setupInterestRateModel( bytes32 assetKey_, InterestRateParams calldata interestParams_ - ) external override onlySystemOwner onlyExists(assetKey_) { + ) external override onlyAssetParametersManager onlyExists(assetKey_) { _setupInterestRateParams(assetKey_, interestParams_); } function setupDistributionsMinimums( bytes32 assetKey_, DistributionMinimums calldata distrMinimums_ - ) external override onlySystemOwner onlyExists(assetKey_) { + ) external override onlyAssetParametersManager onlyExists(assetKey_) { _setupDistributionsMinimums(assetKey_, distrMinimums_); } function setupAllParameters( bytes32 assetKey_, AllPoolParams calldata poolParams_ - ) external override onlySystemOwner onlyExists(assetKey_) { + ) external override onlyAssetParametersManager onlyExists(assetKey_) { _setupInterestRateParams(assetKey_, poolParams_.interestRateParams); _setupMainParameters(assetKey_, poolParams_.mainParams); _setupDistributionsMinimums(assetKey_, poolParams_.distrMinimums); } - function freeze(bytes32 assetKey_) external override onlySystemOwner onlyExists(assetKey_) { + function freeze( + bytes32 assetKey_ + ) external override onlyAssetParametersManager onlyExists(assetKey_) { _parameters[assetKey_][FREEZE_KEY] = PureParameters.makeBoolParam(true); emit FreezeParamUpdated(assetKey_, true); @@ -171,7 +173,7 @@ contract AssetParameters is IAssetParameters, AbstractDependant { function enableCollateral( bytes32 assetKey_, bool forPRT_ - ) external override onlySystemOwner onlyExists(assetKey_) { + ) external override onlyAssetParametersManager onlyExists(assetKey_) { forPRT_ ? _parameters[assetKey_][ENABLE_COLLATERAL_WITH_PRT_KEY] = PureParameters .makeBoolParam(true) @@ -255,6 +257,10 @@ contract AssetParameters is IAssetParameters, AbstractDependant { return _getParam(assetKey_, MAX_UTILIZATION_RATIO_KEY).getUintFromParam(); } + function _onlyAssetParametersManager() internal { + _roleManager.isAssetParametersManager(msg.sender); + } + function _setupInterestRateParams( bytes32 assetKey_, InterestRateParams calldata interestParams_ diff --git a/contracts/DefiCore.sol b/contracts/DefiCore.sol index 31a34b1..e98c6a4 100644 --- a/contracts/DefiCore.sol +++ b/contracts/DefiCore.sol @@ -19,6 +19,7 @@ import "./interfaces/ISystemPoolsRegistry.sol"; import "./interfaces/IRewardsDistribution.sol"; import "./interfaces/IBasicPool.sol"; import "./interfaces/IPRT.sol"; +import "./interfaces/IRoleManager.sol"; import "./libraries/AssetsHelperLibrary.sol"; import "./libraries/MathHelper.sol"; @@ -43,14 +44,12 @@ contract DefiCore is ISystemPoolsRegistry internal _systemPoolsRegistry; IRewardsDistribution internal _rewardsDistribution; IPRT internal _prt; + IRoleManager internal _roleManager; mapping(address => mapping(bytes32 => bool)) public override disabledCollateralAssets; - modifier onlySystemOwner() { - require( - msg.sender == _systemOwnerAddr, - "DefiCore: Only system owner can call this function." - ); + modifier onlyDefiCorePauser() { + _onlyDefiCorePauser(); _; } @@ -69,13 +68,14 @@ contract DefiCore is _rewardsDistribution = IRewardsDistribution(registry_.getRewardsDistributionContract()); _systemPoolsRegistry = ISystemPoolsRegistry(registry_.getSystemPoolsRegistryContract()); _prt = IPRT(registry_.getPRTContract()); + _roleManager = IRoleManager(registry_.getRoleManagerContract()); } - function pause() external override onlySystemOwner { + function pause() external override onlyDefiCorePauser { _pause(); } - function unpause() external override onlySystemOwner { + function unpause() external override onlyDefiCorePauser { _unpause(); } @@ -645,6 +645,10 @@ contract DefiCore is } } + function _onlyDefiCorePauser() internal { + _roleManager.isDefiCorePauser(msg.sender); + } + function _borrowInternal( bytes32 assetKey_, uint256 borrowAmount_, diff --git a/contracts/PRT.sol b/contracts/PRT.sol index ec3096e..310c978 100644 --- a/contracts/PRT.sol +++ b/contracts/PRT.sol @@ -10,19 +10,19 @@ import "./interfaces/IPRT.sol"; import "./interfaces/IRegistry.sol"; import "./interfaces/IDefiCore.sol"; import "./interfaces/IUserInfoRegistry.sol"; - -import "./common/Globals.sol"; +import "./interfaces/IRoleManager.sol"; contract PRT is IPRT, ERC721Upgradeable, AbstractDependant, ReentrancyGuardUpgradeable { uint256 internal _tokenIdCounter; address internal _systemOwnerAddr; IDefiCore internal _defiCore; IUserInfoRegistry internal _userInfoRegistry; + IRoleManager internal _roleManager; PRTParams internal _prtParams; - modifier onlySystemOwner() { - require(msg.sender == _systemOwnerAddr, "PRT: Only system owner can call this function"); + modifier onlyPRTParamUpdater() { + _onlyPRTParamUpdater(); _; } @@ -42,9 +42,10 @@ contract PRT is IPRT, ERC721Upgradeable, AbstractDependant, ReentrancyGuardUpgra _systemOwnerAddr = registry_.getSystemOwner(); _defiCore = IDefiCore(registry_.getDefiCoreContract()); _userInfoRegistry = IUserInfoRegistry(registry_.getUserInfoRegistryContract()); + _roleManager = IRoleManager(registry_.getRoleManagerContract()); } - function updatePRTParams(PRTParams calldata prtParams_) external override onlySystemOwner { + function updatePRTParams(PRTParams calldata prtParams_) external override onlyPRTParamUpdater { _prtParams = prtParams_; } @@ -100,6 +101,10 @@ contract PRT is IPRT, ERC721Upgradeable, AbstractDependant, ReentrancyGuardUpgra _burn(tokenId_); } + function _onlyPRTParamUpdater() internal { + _roleManager.isPRTParamUpdater(msg.sender); + } + function _checkUserPRTStats( address userAddr_, IUserInfoRegistry.LastSavedUserPosition memory userPosition_, diff --git a/contracts/Registry.sol b/contracts/Registry.sol index cf0d9fb..0c4a67f 100644 --- a/contracts/Registry.sol +++ b/contracts/Registry.sol @@ -23,6 +23,8 @@ contract Registry is IRegistry, OwnableContractsRegistry { string public constant PRT_NAME = "PLATFORM_REPUTATION_TOKEN"; + string public constant ROLE_MANAGER_NAME = "ROLE_MANAGER_NAME"; + function transferOwnershipAndInject( address newOwner_, string[] calldata names_ @@ -83,4 +85,8 @@ contract Registry is IRegistry, OwnableContractsRegistry { function getPRTContract() external view override returns (address) { return getContract(PRT_NAME); } + + function getRoleManagerContract() external view override returns (address) { + return getContract(ROLE_MANAGER_NAME); + } } diff --git a/contracts/RewardsDistribution.sol b/contracts/RewardsDistribution.sol index d10bdbf..afb6d28 100644 --- a/contracts/RewardsDistribution.sol +++ b/contracts/RewardsDistribution.sol @@ -10,6 +10,7 @@ import "./interfaces/IAssetParameters.sol"; import "./interfaces/IRewardsDistribution.sol"; import "./interfaces/ISystemPoolsRegistry.sol"; import "./interfaces/IBasicPool.sol"; +import "./interfaces/IRoleManager.sol"; import "./libraries/MathHelper.sol"; @@ -22,6 +23,7 @@ contract RewardsDistribution is IRewardsDistribution, AbstractDependant { address internal _defiCoreAddr; IAssetParameters internal _assetParameters; ISystemPoolsRegistry internal _systemPoolsRegistry; + IRoleManager internal _roleManager; mapping(bytes32 => LiquidityPoolInfo) public liquidityPoolsInfo; mapping(bytes32 => mapping(address => UserDistributionInfo)) public usersDistributionInfo; @@ -34,11 +36,8 @@ contract RewardsDistribution is IRewardsDistribution, AbstractDependant { _; } - modifier onlySystemOwner() { - require( - msg.sender == _systemOwnerAddr, - "RewardsDistribution: Only system owner can call this function." - ); + modifier onlyRewardsDistributionManager() { + _onlyRewardsDistributionManager(); _; } @@ -49,6 +48,7 @@ contract RewardsDistribution is IRewardsDistribution, AbstractDependant { _defiCoreAddr = registry_.getDefiCoreContract(); _assetParameters = IAssetParameters(registry_.getAssetParametersContract()); _systemPoolsRegistry = ISystemPoolsRegistry(registry_.getSystemPoolsRegistryContract()); + _roleManager = IRoleManager(registry_.getRoleManagerContract()); } function updateCumulativeSums( @@ -85,7 +85,7 @@ contract RewardsDistribution is IRewardsDistribution, AbstractDependant { function setupRewardsPerBlockBatch( bytes32[] calldata assetKeys_, uint256[] calldata rewardsPerBlock_ - ) external override onlySystemOwner { + ) external override onlyRewardsDistributionManager { require( _onlyExistingRewardsAssetKey(), "RewardsDistributionL Unable to setup rewards per block." @@ -183,6 +183,10 @@ contract RewardsDistribution is IRewardsDistribution, AbstractDependant { } } + function _onlyRewardsDistributionManager() internal { + _roleManager.isRewardsDistributionManager(msg.sender); + } + function _updateSumsWithUserReward( address userAddr_, bytes32 assetKey_, diff --git a/contracts/RoleManager.sol b/contracts/RoleManager.sol new file mode 100644 index 0000000..29f11b3 --- /dev/null +++ b/contracts/RoleManager.sol @@ -0,0 +1,92 @@ +pragma solidity 0.8.17; + +import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; + +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; + +contract RoleManager is AccessControlUpgradeable { + bytes32 constant ROLE_MANAGER_ADMIN = keccak256("ROLE_MANAGER_ADMIN"); + + bytes32 constant ROLE_MANAGER_ROLE_GOVERNOR = keccak256("ROLE_MANAGER_ROLE_GOVERNOR"); + + bytes32 constant ASSET_PARAMETERS_MANAGER = keccak256("ASSET_PARAMETERS_MANAGER"); + + bytes32 constant DEFI_CORE_PAUSER = keccak256("DEFI_CORE_PAUSER"); + + bytes32 constant PRT_PARAM_UPDATER = keccak256("PRT_PARAM_UPDATER"); + + bytes32 constant REWARDS_DISTRIBUTION_MANAGER = keccak256("REWARDS_DISTRIBUTION_MANAGER"); + + bytes32 constant SYSTEM_PARAMETERS_MANAGER = keccak256("SYSTEM_PARAMETERS_MANAGER"); + + bytes32 constant SYSTEM_POOLS_MANAGER = keccak256("SYSTEM_POOLS_MANAGER"); + + bytes32 constant SYSTEM_POOLS_RESERVE_FUNDS_MANAGER = + keccak256("SYSTEM_POOLS_RESERVE_FUNDS_MANAGER"); + + function roleManagerInitialize( + bytes32[] calldata roles_, + address[] calldata accounts_ + ) external initializer { + require( + roles_.length == accounts_.length, + "RoleManager: passed arrays are of different sizes" + ); + for (uint256 i = 0; i < roles_.length; ++i) { + _setupRole(roles_[i], accounts_[i]); + } + _setupRole(ROLE_MANAGER_ADMIN, msg.sender); + } + + function isAssetParametersManager(address account_) external view { + _hasRoleOrAdmin(ASSET_PARAMETERS_MANAGER, account_); + } + + function isDefiCorePauser(address account_) external view { + _hasRoleOrAdmin(DEFI_CORE_PAUSER, account_); + } + + function isPRTParamUpdater(address account_) external view { + _hasRoleOrAdmin(PRT_PARAM_UPDATER, account_); + } + + function isRewardsDistributionManager(address account_) external view { + _hasRoleOrAdmin(REWARDS_DISTRIBUTION_MANAGER, account_); + } + + function isSystemParametersManager(address account_) external view { + _hasRoleOrAdmin(SYSTEM_PARAMETERS_MANAGER, account_); + } + + function isSystemPoolsManager(address account_) external view { + _hasRoleOrAdmin(SYSTEM_POOLS_MANAGER, account_); + } + + function isSystemPoolsReserveFundsManager(address account_) external view { + _hasRoleOrAdmin(SYSTEM_POOLS_RESERVE_FUNDS_MANAGER, account_); + } + + function grantRole(bytes32 role_, address account_) public override { + _hasRoleOrAdmin(ROLE_MANAGER_ROLE_GOVERNOR, msg.sender); + + _grantRole(role_, account_); + } + + function revokeRole(bytes32 role_, address account_) public override { + _hasRoleOrAdmin(ROLE_MANAGER_ROLE_GOVERNOR, msg.sender); + + _revokeRole(role_, account_); + } + + function _hasRoleOrAdmin(bytes32 role_, address account_) internal view virtual { + require( + hasRole(role_, account_) || hasRole(ROLE_MANAGER_ADMIN, account_), + string( + abi.encodePacked( + "RoleManager: account is missing role ", + StringsUpgradeable.toHexString(uint256(role_), 32) + ) + ) + ); + } +} diff --git a/contracts/SystemParameters.sol b/contracts/SystemParameters.sol index f56e3a6..4865e46 100644 --- a/contracts/SystemParameters.sol +++ b/contracts/SystemParameters.sol @@ -5,6 +5,7 @@ import "@dlsl/dev-modules/contracts-registry/AbstractDependant.sol"; import "./interfaces/IRegistry.sol"; import "./interfaces/ISystemParameters.sol"; +import "./interfaces/IRoleManager.sol"; import "./libraries/PureParameters.sol"; @@ -19,22 +20,25 @@ contract SystemParameters is ISystemParameters, AbstractDependant { bytes32 public constant MIN_CURRENCY_AMOUNT_KEY = keccak256("MIN_CURRENCY_AMOUNT"); address internal _systemOwnerAddr; + IRoleManager internal _roleManager; mapping(bytes32 => PureParameters.Param) internal _parameters; - modifier onlySystemOwner() { - require( - msg.sender == _systemOwnerAddr, - "SystemParameters: Only system owner can call this function." - ); + modifier onlySystemParametersManager() { + _onlySystemParametersManager(); _; } function setDependencies(address contractsRegistry_) external override dependant { - _systemOwnerAddr = IRegistry(contractsRegistry_).getSystemOwner(); + IRegistry registry_ = IRegistry(contractsRegistry_); + + _systemOwnerAddr = registry_.getSystemOwner(); + _roleManager = IRoleManager(registry_.getRoleManagerContract()); } - function setRewardsTokenAddress(address rewardsToken_) external override onlySystemOwner { + function setRewardsTokenAddress( + address rewardsToken_ + ) external override onlySystemParametersManager { PureParameters.Param memory currentParam_ = _parameters[REWARDS_TOKEN_KEY]; if (PureParameters.paramExists(currentParam_)) { @@ -49,7 +53,9 @@ contract SystemParameters is ISystemParameters, AbstractDependant { emit RewardsTokenUpdated(rewardsToken_); } - function setupLiquidationBoundary(uint256 newValue_) external override onlySystemOwner { + function setupLiquidationBoundary( + uint256 newValue_ + ) external override onlySystemParametersManager { require( newValue_ >= PRECISION * 50 && newValue_ <= PRECISION * 80, "SystemParameters: The new value of the liquidation boundary is invalid." @@ -60,7 +66,9 @@ contract SystemParameters is ISystemParameters, AbstractDependant { emit LiquidationBoundaryUpdated(newValue_); } - function setupStablePoolsAvailability(bool newValue_) external override onlySystemOwner { + function setupStablePoolsAvailability( + bool newValue_ + ) external override onlySystemParametersManager { _parameters[STABLE_POOLS_AVAILABILITY_KEY] = PureParameters.makeBoolParam(newValue_); emit StablePoolsAvailabilityUpdated(newValue_); @@ -68,7 +76,7 @@ contract SystemParameters is ISystemParameters, AbstractDependant { function setupMinCurrencyAmount( uint256 newMinCurrencyAmount_ - ) external override onlySystemOwner { + ) external override onlySystemParametersManager { _parameters[MIN_CURRENCY_AMOUNT_KEY] = PureParameters.makeUintParam(newMinCurrencyAmount_); emit MinCurrencyAmountUpdated(newMinCurrencyAmount_); @@ -90,6 +98,10 @@ contract SystemParameters is ISystemParameters, AbstractDependant { return _getParam(MIN_CURRENCY_AMOUNT_KEY).getUintFromParam(); } + function _onlySystemParametersManager() internal { + _roleManager.isSystemParametersManager(msg.sender); + } + function _getParam(bytes32 paramKey_) internal view returns (PureParameters.Param memory) { require( PureParameters.paramExists(_parameters[paramKey_]), diff --git a/contracts/SystemPoolsRegistry.sol b/contracts/SystemPoolsRegistry.sol index 6a2593e..3fd5bf3 100644 --- a/contracts/SystemPoolsRegistry.sol +++ b/contracts/SystemPoolsRegistry.sol @@ -18,6 +18,7 @@ import "./interfaces/ISystemPoolsRegistry.sol"; import "./interfaces/IBasicPool.sol"; import "./interfaces/IPriceManager.sol"; import "./interfaces/ISystemPoolsFactory.sol"; +import "./interfaces/IRoleManager.sol"; contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDependant { using Paginator for EnumerableSet.Bytes32Set; @@ -35,17 +36,20 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep IRewardsDistribution internal _rewardsDistribution; ISystemPoolsFactory internal _systemPoolsFactory; IPriceManager internal _priceManager; + IRoleManager internal _roleManager; EnumerableSet.Bytes32Set internal _allSupportedAssetKeys; mapping(bytes32 => PoolInfo) public override poolsInfo; mapping(address => bool) public override existingLiquidityPools; mapping(PoolType => PoolTypeInfo) internal _poolTypesInfo; - modifier onlySystemOwner() { - require( - msg.sender == _systemOwnerAddr, - "SystemPoolsRegistry: Only system owner can call this function." - ); + modifier onlySystemPoolsManager() { + _onlySystemPoolsManager(); + _; + } + + modifier onlySystemPoolsReserveFundsManager() { + _onlySystemPoolsReserveFundsManager(); _; } @@ -73,9 +77,10 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep _priceManager = IPriceManager(registry_.getPriceManagerContract()); _rewardsDistribution = IRewardsDistribution(registry_.getRewardsDistributionContract()); _systemPoolsFactory = ISystemPoolsFactory(registry_.getSystemPoolsFactoryContract()); + _roleManager = IRoleManager(registry_.getRoleManagerContract()); } - function updateRewardsAssetKey(bytes32 newRewardsAssetKey_) external onlySystemOwner { + function updateRewardsAssetKey(bytes32 newRewardsAssetKey_) external onlySystemPoolsManager { require( IBasicPool(poolsInfo[newRewardsAssetKey_].poolAddr).assetAddr() == _systemParameters.getRewardsTokenAddress(), @@ -88,7 +93,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep function addPoolsBeacon( PoolType poolType_, address poolImpl_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsManager { PoolTypeInfo storage _poolTypeInfo = _poolTypesInfo[poolType_]; require( @@ -106,7 +111,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep string calldata tokenSymbol_, bool isCollateral_, bool isCollateralWithPRT_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsManager { _addPool( assetAddr_, assetKey_, @@ -122,7 +127,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep address assetAddr_, bytes32 assetKey_, address chainlinkOracle_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsManager { require( _systemParameters.getStablePoolsAvailability(), "SystemPoolsRegistry: Stable pools are unavailable." @@ -136,7 +141,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep bytes32 assetKey_, uint256 amountToWithdraw_, bool isAllFunds_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsReserveFundsManager { require(onlyExistingPool(assetKey_), "SystemPoolsRegistry: Pool doesn't exist."); if (!isAllFunds_) { @@ -157,7 +162,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep address recipientAddr_, uint256 offset_, uint256 limit_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsReserveFundsManager { bytes32[] memory _assetsKeys = getSupportedAssetKeys(offset_, limit_); for (uint256 i = 0; i < _assetsKeys.length; i++) { @@ -172,7 +177,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep function upgradePoolsImpl( PoolType poolType_, address newPoolsImpl_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsManager { address poolBeacon_ = _poolTypesInfo[poolType_].poolBeaconAddr; require(poolBeacon_ != address(0), "SystemPoolsRegistry: Unsupported pool type."); @@ -180,7 +185,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep UpgradeableBeacon(poolBeacon_).upgradeTo(newPoolsImpl_); } - function injectDependenciesToExistingPools() external override onlySystemOwner { + function injectDependenciesToExistingPools() external override onlySystemPoolsManager { IRegistry registry_ = _registry; address[] memory allPools_ = getAllPools(); @@ -193,7 +198,7 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep function injectDependencies( uint256 offset_, uint256 limit_ - ) external override onlySystemOwner { + ) external override onlySystemPoolsManager { IRegistry registry_ = _registry; address[] memory _pools = getPools(offset_, limit_); @@ -340,6 +345,14 @@ contract SystemPoolsRegistry is ISystemPoolsRegistry, Initializable, AbstractDep return _getPoolsAddresses(getSupportedAssetKeysByType(poolType_, offset_, limit_)); } + function _onlySystemPoolsManager() internal { + _roleManager.isSystemPoolsManager(msg.sender); + } + + function _onlySystemPoolsReserveFundsManager() internal { + _roleManager.isSystemPoolsReserveFundsManager(msg.sender); + } + function _addPool( address assetAddr_, bytes32 assetKey_, diff --git a/contracts/interfaces/IAssetParameters.sol b/contracts/interfaces/IAssetParameters.sol index 1982173..598561c 100644 --- a/contracts/interfaces/IAssetParameters.sol +++ b/contracts/interfaces/IAssetParameters.sol @@ -111,19 +111,19 @@ interface IAssetParameters { ) external; /// @notice Function for setting the annual borrow rate of the stable pool - /// @dev Only contract owner can call this function. Only for stable pools + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function. Only for stable pools /// @param assetKey_ pool key for which parameters will be set /// @param newAnnualBorrowRate_ new annual borrow rate parameter function setupAnnualBorrowRate(bytes32 assetKey_, uint256 newAnnualBorrowRate_) external; /// @notice Function for setting the main parameters of the pool - /// @dev Only contract owner can call this function + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function /// @param assetKey_ pool key for which parameters will be set /// @param mainParams_ structure with the main parameters of the pool function setupMainParameters(bytes32 assetKey_, MainPoolParams calldata mainParams_) external; /// @notice Function for setting the interest rate parameters of the pool - /// @dev Only contract owner can call this function + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function /// @param assetKey_ pool key for which parameters will be set /// @param interestParams_ structure with the interest rate parameters of the pool function setupInterestRateModel( @@ -132,7 +132,7 @@ interface IAssetParameters { ) external; /// @notice Function for setting the distribution minimums of the pool - /// @dev Only contract owner can call this function + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function /// @param assetKey_ pool key for which parameters will be set /// @param distrMinimums_ structure with the distribution minimums of the pool function setupDistributionsMinimums( @@ -141,18 +141,18 @@ interface IAssetParameters { ) external; /// @notice Function for setting all pool parameters - /// @dev Only contract owner can call this function + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function /// @param assetKey_ pool key for which parameters will be set /// @param poolParams_ structure with all pool parameters function setupAllParameters(bytes32 assetKey_, AllPoolParams calldata poolParams_) external; /// @notice Function for freezing the pool - /// @dev Only contract owner can call this function + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function /// @param assetKey_ pool key to be frozen function freeze(bytes32 assetKey_) external; /// @notice Function to enable the pool as a collateral - /// @dev Only contract owner can call this function + /// @dev Only ASSET_PARAMETERS_MANAGER can call this function /// @param assetKey_ the pool key to be enabled as a collateral /// @param forPRT_ whether to enable the asset as a collateral for the users with PRT function enableCollateral(bytes32 assetKey_, bool forPRT_) external; diff --git a/contracts/interfaces/IDefiCore.sol b/contracts/interfaces/IDefiCore.sol index 4a3c39e..68ebee7 100644 --- a/contracts/interfaces/IDefiCore.sol +++ b/contracts/interfaces/IDefiCore.sol @@ -75,11 +75,11 @@ interface IDefiCore { event DistributionRewardWithdrawn(address indexed userAddr, uint256 rewardAmount); /// @notice Function for pausing all user interactions with the system - /// @dev Only contract owner can call this function + /// @dev Only DEFI_CORE_PAUSER can call this function function pause() external; /// @notice Function for unpausing all user interactions with the system - /// @dev Only contract owner can call this function + /// @dev Only DEFI_CORE_PAUSER can call this function function unpause() external; /// @notice With this function you can change the value of the disabled of the asset as a collateral diff --git a/contracts/interfaces/IPRT.sol b/contracts/interfaces/IPRT.sol index b2f503c..2a0b719 100644 --- a/contracts/interfaces/IPRT.sol +++ b/contracts/interfaces/IPRT.sol @@ -25,7 +25,7 @@ interface IPRT { } /// @notice A system function that is needed to update the requirements for PRT - /// @dev Only the system owner can call this function + /// @dev Only the PRT_PARAM_UPDATER can call this function /// @param prtParams_ element type PRTParams structure with the new requirements function updatePRTParams(PRTParams calldata prtParams_) external; diff --git a/contracts/interfaces/IRegistry.sol b/contracts/interfaces/IRegistry.sol index 7de61da..f34852e 100644 --- a/contracts/interfaces/IRegistry.sol +++ b/contracts/interfaces/IRegistry.sol @@ -6,6 +6,12 @@ pragma solidity 0.8.17; * With this contract you can add new contracts, update the implementation of proxy contracts */ interface IRegistry { + /// @notice Function for transfeerring the ownership and injecting the dependecies to the contracts + /// @dev zero address passed in the newOwner_ argument will trigger the revertion of the function call + /// @param newOwner_ new system owner + /// @param names_ an array of the contract names to inject dependencies in + function transferOwnershipAndInject(address newOwner_, string[] calldata names_) external; + /// @notice Function to get the address of the system owner /// @return a system owner address function getSystemOwner() external view returns (address); @@ -59,4 +65,9 @@ interface IRegistry { /// @dev Used in dependency injection mechanism in the system /// @return PRT contract address function getPRTContract() external view returns (address); + + /// @notice Function to get the address of the RoleManager contract + /// @dev Used in dependency injection mechanism in the system + /// @return RoleManager contract address + function getRoleManagerContract() external view returns (address); } diff --git a/contracts/interfaces/IRewardsDistribution.sol b/contracts/interfaces/IRewardsDistribution.sol index 8ff99cc..521934c 100644 --- a/contracts/interfaces/IRewardsDistribution.sol +++ b/contracts/interfaces/IRewardsDistribution.sol @@ -60,7 +60,7 @@ interface IRewardsDistribution { ) external returns (uint256 userReward_); /// @notice Function to update block rewards for desired pools - /// @dev Can call only by contract owner. The passed arrays must be of the same length + /// @dev Can call only by REWARDS_DISTRIBUTION_MANAGER. The passed arrays must be of the same length /// @param assetKeys_ array of pool identifiers /// @param rewardsPerBlock_ array of new rewards per block function setupRewardsPerBlockBatch( diff --git a/contracts/interfaces/IRoleManager.sol b/contracts/interfaces/IRoleManager.sol new file mode 100644 index 0000000..02365dc --- /dev/null +++ b/contracts/interfaces/IRoleManager.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.17; + +/** + * This contract stores different roles across the protocol and their corresponding accounts. + * It also can be used for checking whether a particular user has a specific role, creating, granting and revoking the roles. + * The contract also stores a set of constants with the role names which are used across the protocol. + */ +interface IRoleManager { + /// @notice Function for granting the role to the specified account + /// @dev Only user with the ROLE_MANAGER_ROLE_GOVERNOR or ROLE_MANAGER_ADMIN role can call this function + /// @param role_ role to grant to the account + /// @param account_ account to grant the role to + function grantRole(bytes32 role_, address account_) external; + + /// @notice Function for revoking the role from the specified account + /// @dev Only user with the ROLE_MANAGER_ROLE_GOVERNOR or ROLE_MANAGER_ADMIN role can call this function + /// @param role_ role to revoke from the account + /// @param account_ account to revoke the role from + function revokeRole(bytes32 role_, address account_) external; + + /// @notice Function to check whether the address has the ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN role + /// @dev Used in AssetParameters contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isAssetParametersManager(address account_) external view; + + /// @notice Function to check whether the address has the DEFI_CORE_PAUSER or ROLE_MANAGER_ADMIN role + /// @dev Used in DefiCore contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isDefiCorePauser(address account_) external view; + + /// @notice Function to check whether the address has the PRT_PARAM_UPDATER or ROLE_MANAGER_ADMIN role + /// @dev Used in PRT contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isPRTParamUpdater(address account_) external view; + + /// @notice Function to check whether the address has the REWARDS_DISTRIBUTION_MANAGER or ROLE_MANAGER_ADMIN role + /// @dev Used in RewardsDistribution contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isRewardsDistributionManager(address account_) external view; + + /// @notice Function to check whether the address has the SYSTEM_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN role + /// @dev Used in SystemParameters contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isSystemParametersManager(address account_) external view; + + /// @notice Function to check whether the address has the SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN role + /// @dev Used in SystemPoolManager contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isSystemPoolsManager(address account_) external view; + + /// @notice Function to check whether the address has the SYSTEM_POOLS_RESERVE_FUNDS_MANAGER or ROLE_MANAGER_ADMIN role + /// @dev Used in SystemPoolManager contract to check the caller's roles in some function. Reverts if the account has neither of the roles mentioned above. + function isSystemPoolsReserveFundsManager(address account_) external view; +} diff --git a/contracts/interfaces/ISystemParameters.sol b/contracts/interfaces/ISystemParameters.sol index 990a281..81a7893 100644 --- a/contracts/interfaces/ISystemParameters.sol +++ b/contracts/interfaces/ISystemParameters.sol @@ -22,22 +22,22 @@ interface ISystemParameters { event MinCurrencyAmountUpdated(uint256 newValue); /// @notice The function that updates the rewards token address. Can update only if current rewards token address is zero address - /// @dev Only owner of this contract can call this function + /// @dev Only SYSTEM_PARAMETERS_MANAGER can call this function /// @param rewardsToken_ new value of the rewards token parameter function setRewardsTokenAddress(address rewardsToken_) external; /// @notice The function that updates the parameter of the same name to a new value - /// @dev Only owner of this contract can call this function + /// @dev Only SYSTEM_PARAMETERS_MANAGER can call this function /// @param newValue_ new value of the liquidation boundary parameter function setupLiquidationBoundary(uint256 newValue_) external; /// @notice The function that updates the parameter of the same name to a new value - /// @dev Only owner of this contract can call this function + /// @dev Only SYSTEM_PARAMETERS_MANAGER can call this function /// @param newValue_ new value of the stable pools availability parameter function setupStablePoolsAvailability(bool newValue_) external; /// @notice The function that updates the parameter of the same name - /// @dev Only owner of this contract can call this function + /// @dev Only SYSTEM_PARAMETERS_MANAGER can call this function /// @param newMinCurrencyAmount_ new value of the min currency amount parameter function setupMinCurrencyAmount(uint256 newMinCurrencyAmount_) external; diff --git a/contracts/interfaces/ISystemPoolsRegistry.sol b/contracts/interfaces/ISystemPoolsRegistry.sol index 8d68a60..fa1b156 100644 --- a/contracts/interfaces/ISystemPoolsRegistry.sol +++ b/contracts/interfaces/ISystemPoolsRegistry.sol @@ -101,13 +101,13 @@ interface ISystemPoolsRegistry { event PoolAdded(bytes32 assetKey, address assetAddr, address poolAddr, PoolType poolType); /// @notice Function to add a beacon contract for the desired type of pools - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_MANAGER can call this function /// @param poolType_ the type of pool for which the beacon contract will be added /// @param poolImpl_ the implementation address for the desired pool type function addPoolsBeacon(PoolType poolType_, address poolImpl_) external; /// @notice The function is needed to add new liquidity pools - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_MANAGER call this function /// @param assetAddr_ address of the underlying liquidity pool asset /// @param assetKey_ pool key of the added liquidity pool /// @param chainlinkOracle_ the address of the chainlink oracle for the passed asset @@ -124,7 +124,7 @@ interface ISystemPoolsRegistry { ) external; /// @notice The function is needed to add new stable pools - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_MANAGER can call this function /// @param assetAddr_ address of the underlying stable pool asset /// @param assetKey_ pool key of the added stable pool /// @param chainlinkOracle_ the address of the chainlink oracle for the passed asset @@ -135,7 +135,7 @@ interface ISystemPoolsRegistry { ) external; /// @notice Withdraws a certain amount of reserve funds from a certain pool to a certain recipient - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_RESERVE_FUNDS_MANAGER can call this function /// @param recipientAddr_ the address of the user to whom the withdrawal will be sent /// @param assetKey_ key of the required pool /// @param amountToWithdraw_ amount for withdrawal of reserve funds @@ -148,7 +148,7 @@ interface ISystemPoolsRegistry { ) external; /// @notice Withdrawal of all reserve funds from pools with pagination - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_RESERVE_FUNDS_MANAGER can call this function /// @param recipientAddr_ the address of the user to whom the withdrawal will be sent /// @param offset_ offset for pagination /// @param limit_ maximum number of elements for pagination @@ -158,18 +158,23 @@ interface ISystemPoolsRegistry { uint256 limit_ ) external; + /// @notice The function is needed to update the reward asset + /// @dev Only SYSTEM_POOLS_MANAGER can call this function + /// @param newRewardsAssetKey_ key of the new rewards asset + function updateRewardsAssetKey(bytes32 newRewardsAssetKey_) external; + /// @notice The function is needed to update the implementation of the pools - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_MANAGER can call this function /// @param poolType_ needed pool type from PoolType enum /// @param newPoolsImpl_ address of the new pools implementation function upgradePoolsImpl(PoolType poolType_, address newPoolsImpl_) external; /// @notice The function inject dependencies to existing liquidity pools - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_MANAGER can call this function function injectDependenciesToExistingPools() external; /// @notice The function inject dependencies with pagination - /// @dev Only contract owner can call this function + /// @dev Only SYSTEM_POOLS_MANAGER can call this function function injectDependencies(uint256 offset_, uint256 limit_) external; /// @notice The function returns the native asset key diff --git a/contracts/interfaces/IUserInfoRegistry.sol b/contracts/interfaces/IUserInfoRegistry.sol index 7af8c0c..ea108d5 100644 --- a/contracts/interfaces/IUserInfoRegistry.sol +++ b/contracts/interfaces/IUserInfoRegistry.sol @@ -180,6 +180,12 @@ interface IUserInfoRegistry { uint256 amount_ ) external; + /// @notice A system function that is needed to update users' PRT stats + /// @dev Only DefiCore contract can call this function + /// @param userAddr_ the user we want to update stats for + /// @param repaysCount_ the number of the borrow repays the user did + /// @param liquidationsCount_ the number of times user got liquidated + /// @param isSupply_ whether we update stats for user supply or borrow function updateUserStatsForPRT( address userAddr_, uint256 repaysCount_, diff --git a/deploy/1_contracts_deploy.js b/deploy/1_contracts_deploy.js index 059575c..3d30f1c 100644 --- a/deploy/1_contracts_deploy.js +++ b/deploy/1_contracts_deploy.js @@ -11,8 +11,8 @@ const RewardsDistribution = artifacts.require("RewardsDistribution"); const UserInfoRegistry = artifacts.require("UserInfoRegistry"); const PriceManager = artifacts.require("PriceManager"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); -const { artifacts } = require("hardhat"); const { isStablePoolsAvailable } = require("./helpers/deployHelper.js"); require("dotenv").config(); @@ -55,6 +55,9 @@ module.exports = async (deployer, logger) => { await deployer.deploy(Prt); const prt = await Prt.deployed(); + await deployer.deploy(RoleManager); + const roleManager = await RoleManager.deployed(); + await deployer.deploy(LiquidityPool); if (isStablePoolsAvailable()) { @@ -112,6 +115,11 @@ module.exports = async (deployer, logger) => { "Add Prt contract proxy to the registry" ); + logger.logTransaction( + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), roleManager.address), + "Add RoleManager contract proxy to the registry" + ); + console.log(); logger.logTransaction( diff --git a/deploy/2_init_all.js b/deploy/2_init_all.js index 535b58c..f715f2d 100644 --- a/deploy/2_init_all.js +++ b/deploy/2_init_all.js @@ -5,8 +5,7 @@ const SystemPoolsRegistry = artifacts.require("SystemPoolsRegistry"); const LiquidityPool = artifacts.require("LiquidityPool"); const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const Prt = artifacts.require("PRT"); - -const { artifacts } = require("hardhat"); +const RoleManager = artifacts.require("RoleManager"); const { parsePrtData, @@ -24,6 +23,7 @@ module.exports = async (deployer, logger) => { const systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); const interestRateLibrary = await InterestRateLibrary.at(await registry.getInterestRateLibraryContract()); const prt = await Prt.at(await registry.getPRTContract()); + const roleManager = await RoleManager.at(await registry.getRoleManagerContract()); const rewardsAssetKey = getAssetKey(rewardsAssetSymbol()); const nativeTokenKey = getAssetKey(nativeAssetSymbol()); @@ -34,6 +34,7 @@ module.exports = async (deployer, logger) => { logger.logTransaction(await defiCore.defiCoreInitialize(), "Init DefiCore"); logger.logTransaction(await prt.prtInitialize(prtArr.name, prtArr.symbol, prtArr.prtParams), "Init PRT"); + logger.logTransaction(await roleManager.roleManagerInitialize([], []), "Init RoleManager"); logger.logTransaction( await systemPoolsRegistry.systemPoolsRegistryInitialize( ( @@ -110,7 +111,8 @@ module.exports = async (deployer, logger) => { ["PriceManager", await registry.getPriceManagerContract()], ["InterestRateLibrary", interestRateLibrary.address], ["RewardsToken", rewardsAssetAddress], - ["PRT", prt.address] + ["PRT", prt.address], + ["RoleManager", roleManager.address] ); console.log("+--------------------------------------------------------------------------------+"); diff --git a/test/assetParameters.test.js b/test/assetParameters.test.js index 57e567c..f49db4d 100644 --- a/test/assetParameters.test.js +++ b/test/assetParameters.test.js @@ -2,6 +2,7 @@ const { toBytes, fromBytes, compareKeys } = require("./helpers/bytesCompareLibra const { getInterestRateLibraryAddr } = require("./helpers/coverage-helper"); const { toBN, accounts, wei, getPrecision, getPercentage100 } = require("../scripts/utils/utils"); const { ZERO_ADDR } = require("../scripts/utils/constants"); +const { utils } = require("ethers"); const truffleAssert = require("truffle-assertions"); const Reverter = require("./helpers/reverter"); @@ -21,6 +22,7 @@ const PriceManager = artifacts.require("PriceManager"); const ChainlinkOracleMock = artifacts.require("ChainlinkOracleMock"); const SystemPoolsRegistry = artifacts.require("SystemPoolsRegistry"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); DefiCore.numberFormat = "BigNumber"; AssetParameters.numberFormat = "BigNumber"; @@ -68,6 +70,7 @@ describe("AssetParameters", () => { let systemPoolsRegistry; let rewardsToken; let prt; + let roleManager; async function createLiquidityPool(assetKey, symbol, isCollateral) { const token = await MockERC20.new("Mock" + symbol, symbol); @@ -144,6 +147,7 @@ describe("AssetParameters", () => { const _stablePoolImpl = await StablePool.new(); const _systemPoolsRegistry = await SystemPoolsRegistry.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -156,6 +160,7 @@ describe("AssetParameters", () => { await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.SYSTEM_POOLS_REGISTRY_NAME(), _systemPoolsRegistry.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -163,6 +168,7 @@ describe("AssetParameters", () => { assetParameters = await AssetParameters.at(await registry.getAssetParametersContract()); rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); const defiCore = await DefiCore.at(await registry.getDefiCoreContract()); @@ -177,6 +183,7 @@ describe("AssetParameters", () => { await registry.injectDependencies(await registry.PRT_NAME()); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, rewardsTokenKey, zeroKey); await systemPoolsRegistry.addPoolsBeacon(1, _stablePoolImpl.address); @@ -274,12 +281,13 @@ describe("AssetParameters", () => { }); describe("setupAllParameters", () => { - it("should get exception if called not by systemOwner", async () => { - const reason = "AssetParameters: Only system owner can call this function."; + it("should get exception if called not by an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts( assetParameters.setupAllParameters( - stableKey, + rewardsTokenKey, [ [colRatio, colRatio, reserveFactor, liquidationDiscount, maxUR], [0, firstSlope, secondSlope, utilizationBreakingPoint], @@ -353,8 +361,11 @@ describe("AssetParameters", () => { assert.equal(result.logs[0].args.newValue, true); }); - it("should not access to freeze the pool by not core", async () => { - await truffleAssert.reverts(assetParameters.freeze(daiKey, { from: SOMEBODY })); + it("should not access to freeze the pool by not an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; + + await truffleAssert.reverts(assetParameters.freeze(daiKey, { from: SOMEBODY }), reason); }); it("should not access to freeze pool of the not exists token", async () => { @@ -385,8 +396,9 @@ describe("AssetParameters", () => { assert.equal(result.receipt.logs[0].args.isCollateral, true); }); - it("should get exception if called not by systemOwner", async () => { - const reason = "AssetParameters: Only system owner can call this function."; + it("should get exception if called not by an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts(assetParameters.enableCollateral(daiKey, false, { from: USER2 }), reason); }); @@ -489,8 +501,9 @@ describe("AssetParameters", () => { ); }); - it("should get exception if called not by systemOwner", async () => { - const reason = "AssetParameters: Only system owner can call this function."; + it("should get exception if called not by an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts( assetParameters.setupInterestRateModel(daiKey, [0, firstSlope, secondSlope, utilizationBreakingPoint], { @@ -603,8 +616,10 @@ describe("AssetParameters", () => { ); }); - it("should get exception if called not by systemOwner", async () => { - const reason = "AssetParameters: Only system owner can call this function."; + it("should get exception if called not by an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; + await truffleAssert.reverts( assetParameters.setupMainParameters(daiKey, [colRatio, colRatio, reserveFactor, liquidationDiscount, maxUR], { from: USER2, @@ -671,8 +686,9 @@ describe("AssetParameters", () => { ); }); - it("should get exception if called not by systemOwner", async () => { - const reason = "AssetParameters: Only system owner can call this function."; + it("should get exception if called not by an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts( assetParameters.setupDistributionsMinimums(daiKey, [minSupplyDistributionPart, minBorrowDistributionPart], { @@ -740,8 +756,9 @@ describe("AssetParameters", () => { await truffleAssert.reverts(assetParameters.setupAnnualBorrowRate(stableKey, newRate), reason); }); - it("should get exception if called not by systemOwner", async () => { - const reason = "AssetParameters: Only system owner can call this function."; + it("should get exception if called not by an ASSET_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts( assetParameters.setupAnnualBorrowRate(stableKey, annualBorrowRate, { from: USER2 }), diff --git a/test/defiCore.test.js b/test/defiCore.test.js index 19c7cdd..8084e93 100644 --- a/test/defiCore.test.js +++ b/test/defiCore.test.js @@ -3,10 +3,11 @@ const { toBytes, compareKeys, deepCompareKeys } = require("./helpers/bytesCompar const { getInterestRateLibraryAddr } = require("./helpers/coverage-helper"); const { toBN, accounts, getPrecision, getPercentage100, wei } = require("../scripts/utils/utils"); const { ZERO_ADDR } = require("../scripts/utils/constants"); +const { utils } = require("ethers"); const Reverter = require("./helpers/reverter"); const truffleAssert = require("truffle-assertions"); -const { web3 } = require("hardhat"); +const { web3, artifacts } = require("hardhat"); const { assert } = require("chai"); const Registry = artifacts.require("Registry"); @@ -24,6 +25,7 @@ const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const WETH = artifacts.require("WETH"); const StablePermitToken = artifacts.require("StablePermitTokenMock"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); const MockERC20 = artifacts.require("MockERC20"); const ChainlinkOracleMock = artifacts.require("ChainlinkOracleMock"); @@ -48,6 +50,7 @@ describe("DefiCore", async () => { let userInfoRegistry; let systemPoolsRegistry; let prt; + let roleManager; let nativePool; let daiPool; @@ -195,6 +198,7 @@ describe("DefiCore", async () => { const _stablePoolImpl = await StablePool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -209,6 +213,7 @@ describe("DefiCore", async () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -218,6 +223,7 @@ describe("DefiCore", async () => { systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); await registry.injectDependencies(await registry.DEFI_CORE_NAME()); await registry.injectDependencies(await registry.SYSTEM_PARAMETERS_NAME()); @@ -234,6 +240,7 @@ describe("DefiCore", async () => { tokens.push(nativeToken); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, nativeTokenKey, zeroKey); await systemPoolsRegistry.addPoolsBeacon(1, _stablePoolImpl.address); @@ -2350,8 +2357,9 @@ describe("DefiCore", async () => { await defiCore.borrowFor(daiKey, borrowAmount, USER1, { from: USER1 }); }); - it("should get exception if called by not a system owner", async () => { - const reason = "DefiCore: Only system owner can call this function."; + it("should get exception if called not by an DEFI_CORE_PAUSER or ROLE_MANAGER_ADMIN", async () => { + const DEFI_CORE_PAUSER_ROLE = utils.keccak256(utils.toUtf8Bytes("DEFI_CORE_PAUSER")); + const reason = `RoleManager: account is missing role ${DEFI_CORE_PAUSER_ROLE}`; await truffleAssert.reverts(defiCore.pause({ from: USER1 }), reason); await truffleAssert.reverts(defiCore.unpause({ from: USER1 }), reason); diff --git a/test/liquidityPool.test.js b/test/liquidityPool.test.js index 7a2fae1..6f7c92d 100644 --- a/test/liquidityPool.test.js +++ b/test/liquidityPool.test.js @@ -2,6 +2,7 @@ const { setNextBlockTime, mine, getCurrentBlockNumber } = require("./helpers/blo const { toBytes, deepCompareKeys } = require("./helpers/bytesCompareLibrary"); const { getInterestRateLibraryAddr } = require("./helpers/coverage-helper"); const { toBN, accounts, getPrecision, getPercentage100, wei } = require("../scripts/utils/utils"); +const { utils } = require("ethers"); const { ZERO_ADDR } = require("../scripts/utils/constants"); const truffleAssert = require("truffle-assertions"); @@ -24,6 +25,7 @@ const PriceManager = artifacts.require("PriceManager"); const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const WETH = artifacts.require("WETH"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); const MockERC20 = artifacts.require("MockERC20"); const ChainlinkOracleMock = artifacts.require("ChainlinkOracleMock"); @@ -48,6 +50,7 @@ describe("LiquidityPool", () => { let userInfoRegistry; let systemPoolsRegistry; let prt; + let roleManager; let nativePool; let liquidityPool; @@ -217,6 +220,7 @@ describe("LiquidityPool", () => { const _liquidityPoolImpl = await LiquidityPool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -229,6 +233,7 @@ describe("LiquidityPool", () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -238,6 +243,7 @@ describe("LiquidityPool", () => { systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); await registry.injectDependencies(await registry.DEFI_CORE_NAME()); await registry.injectDependencies(await registry.SYSTEM_PARAMETERS_NAME()); @@ -254,6 +260,7 @@ describe("LiquidityPool", () => { tokens.push(nativeToken); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, nativeTokenKey, zeroKey); await deployRewardsPool(rewardsToken.address, await rewardsToken.symbol()); @@ -1894,8 +1901,11 @@ describe("LiquidityPool", () => { ); }); - it("should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_RESERVE_FUNDS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE = utils.keccak256( + utils.toUtf8Bytes("SYSTEM_POOLS_RESERVE_FUNDS_MANAGER") + ); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE}`; await truffleAssert.reverts( systemPoolsRegistry.withdrawReservedFunds(RECIPIENT, tokenKey, 0, true, { from: USER1 }), @@ -1959,8 +1969,11 @@ describe("LiquidityPool", () => { assert.equal(toBN(await liquidityPool.totalReserves()).toString(), 0); }); - it("should get an exception if called not by systemowner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_RESERVE_FUNDS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE = utils.keccak256( + utils.toUtf8Bytes("SYSTEM_POOLS_RESERVE_FUNDS_MANAGER") + ); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE}`; await truffleAssert.reverts( systemPoolsRegistry.withdrawAllReservedFunds(RECIPIENT, 0, 2, { from: USER2 }), diff --git a/test/priceManager.test.js b/test/priceManager.test.js index 472eae0..667c1f1 100644 --- a/test/priceManager.test.js +++ b/test/priceManager.test.js @@ -41,6 +41,7 @@ describe("PriceManager", async () => { await registry.addContract(await registry.DEFI_CORE_NAME(), NOTHING); await registry.addContract(await registry.REWARDS_DISTRIBUTION_NAME(), NOTHING); await registry.addContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), NOTHING); + await registry.addContract(await registry.ROLE_MANAGER_NAME(), NOTHING); priceManager = await PriceManager.at(await registry.getPriceManagerContract()); diff --git a/test/prt.test.js b/test/prt.test.js index 9e57f60..c05d200 100644 --- a/test/prt.test.js +++ b/test/prt.test.js @@ -1,6 +1,7 @@ const { toBytes, compareKeys, deepCompareKeys } = require("./helpers/bytesCompareLibrary"); const { getInterestRateLibraryAddr } = require("./helpers/coverage-helper"); const { toBN, accounts, getPrecision, getPercentage100, wei } = require("../scripts/utils/utils"); +const { utils } = require("ethers"); const { ZERO_ADDR } = require("../scripts/utils/constants"); const { setNextBlockTime, setTime, mine, getCurrentBlockTime } = require("./helpers/block-helper"); @@ -24,6 +25,7 @@ const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const Prt = artifacts.require("PRT"); const WETH = artifacts.require("WETH"); const StablePermitToken = artifacts.require("StablePermitTokenMock"); +const RoleManager = artifacts.require("RoleManager"); const MockERC20 = artifacts.require("MockERC20"); const ChainlinkOracleMock = artifacts.require("ChainlinkOracleMock"); @@ -51,6 +53,7 @@ describe("PRT", () => { let systemPoolsRegistry; let rewardsDistribution; let prt; + let roleManager; let nativePool; let daiPool; @@ -183,6 +186,7 @@ describe("PRT", () => { const _stablePoolImpl = await StablePool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -196,6 +200,7 @@ describe("PRT", () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -206,6 +211,7 @@ describe("PRT", () => { rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); prt = await Prt.at(await registry.getPRTContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); const _prtReentrancy = await PRTReentrancy.new( await registry.getPRTContract(), @@ -232,6 +238,7 @@ describe("PRT", () => { tokens.push(nativeToken); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, nativeTokenKey, zeroKey); await prt.prtInitialize("Platform Reputation Token", "PRT", [ [1000000000000, 100], @@ -568,8 +575,10 @@ describe("PRT", () => { }); describe("updatePRTRarams()", () => { - it("should revert if not the system owner tries to update the PRT params", async () => { - let reason = "PRT: Only system owner can call this function"; + it("should get exception if called not by an PRT_PARAM_UPDATER or ROLE_MANAGER_ADMIN", async () => { + const PRT_PARAM_UPDATER_ROLE = utils.keccak256(utils.toUtf8Bytes("PRT_PARAM_UPDATER")); + const reason = `RoleManager: account is missing role ${PRT_PARAM_UPDATER_ROLE}`; + await truffleAssert.reverts( prt.updatePRTParams( [ @@ -581,7 +590,7 @@ describe("PRT", () => { reason ); }); - it("should pass if the system owner tries to update the PRT params", async () => { + it("should pass if the PRT_PARAM_UPDATER tries to update the PRT params", async () => { await truffleAssert.passes( prt.updatePRTParams([ [3000000000000, 100], diff --git a/test/registry.test.js b/test/registry.test.js index 0a5c676..1380109 100644 --- a/test/registry.test.js +++ b/test/registry.test.js @@ -16,6 +16,7 @@ const PriceManager = artifacts.require("PriceManager"); const UserInfoRegistry = artifacts.require("UserInfoRegistry"); const SystemPoolsFactory = artifacts.require("SystemPoolsFactory"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); describe("Registry", async () => { const reverter = new Reverter(); @@ -29,6 +30,7 @@ describe("Registry", async () => { let systemParameters; let systemPoolsRegistry; let prt; + let roleManager; before("setup", async () => { LIQUIDITY_POOL_REGISTRY = await accounts(9); @@ -45,6 +47,7 @@ describe("Registry", async () => { const _userInfoRegistry = await UserInfoRegistry.new(); const _systemPoolsFactory = await SystemPoolsFactory.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -58,12 +61,14 @@ describe("Registry", async () => { await registry.addProxyContract(await registry.REWARDS_DISTRIBUTION_NAME(), _rewardsDistribution.address); await registry.addProxyContract(await registry.SYSTEM_POOLS_REGISTRY_NAME(), _systemPoolsRegistry.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); defiCore = await DefiCore.at(await registry.getDefiCoreContract()); assetParameters = await AssetParameters.at(await registry.getAssetParametersContract()); systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); await reverter.snapshot(); }); diff --git a/test/rewardsDistribution.test.js b/test/rewardsDistribution.test.js index 94efb1a..8fe8dbe 100644 --- a/test/rewardsDistribution.test.js +++ b/test/rewardsDistribution.test.js @@ -3,6 +3,7 @@ const { toBytes } = require("./helpers/bytesCompareLibrary"); const { getInterestRateLibraryAddr } = require("./helpers/coverage-helper"); const { toBN, accounts, getPrecision, getPercentage100, wei } = require("../scripts/utils/utils"); const { ZERO_ADDR } = require("../scripts/utils/constants"); +const { utils } = require("ethers"); const Reverter = require("./helpers/reverter"); const truffleAssert = require("truffle-assertions"); @@ -22,6 +23,7 @@ const Prt = artifacts.require("PRT"); const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const WETH = artifacts.require("WETH"); const StablePermitToken = artifacts.require("StablePermitTokenMock"); +const RoleManager = artifacts.require("RoleManager"); const MockERC20 = artifacts.require("MockERC20"); const ChainlinkOracleMock = artifacts.require("ChainlinkOracleMock"); @@ -43,6 +45,7 @@ describe("RewardsDistribution", () => { let rewardsDistribution; let systemPoolsRegistry; let prt; + let roleManager; let daiPool; @@ -223,6 +226,7 @@ describe("RewardsDistribution", () => { const _stablePoolImpl = await StablePool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -237,6 +241,7 @@ describe("RewardsDistribution", () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -245,6 +250,7 @@ describe("RewardsDistribution", () => { systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); await registry.injectDependencies(await registry.DEFI_CORE_NAME()); await registry.injectDependencies(await registry.SYSTEM_PARAMETERS_NAME()); @@ -261,6 +267,7 @@ describe("RewardsDistribution", () => { tokens.push(nativeToken); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, nativeTokenKey, zeroKey); await systemPoolsRegistry.addPoolsBeacon(1, _stablePoolImpl.address); @@ -838,11 +845,12 @@ describe("RewardsDistribution", () => { await truffleAssert.reverts(rewardsDistribution.setupRewardsPerBlockBatch(keys, rewardsPerBlock), reason); }); - it("should get exception if not system onwer try to call this function", async () => { + it("should get exception if called not by an REWARDS_DISTRIBUTION_MANAGER or ROLE_MANAGER_ADMIN", async () => { await systemParameters.setRewardsTokenAddress(rewardsToken.address); await systemPoolsRegistry.updateRewardsAssetKey(rewardsTokenKey); - const reason = "RewardsDistribution: Only system owner can call this function."; + const REWARDS_DISTRIBUTION_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("REWARDS_DISTRIBUTION_MANAGER")); + const reason = `RoleManager: account is missing role ${REWARDS_DISTRIBUTION_MANAGER_ROLE}`; await truffleAssert.reverts( rewardsDistribution.setupRewardsPerBlockBatch([daiKey], [oneToken], { from: USER1 }), diff --git a/test/roleManager.test.js b/test/roleManager.test.js new file mode 100644 index 0000000..f21e9e6 --- /dev/null +++ b/test/roleManager.test.js @@ -0,0 +1,317 @@ +const truffleAssert = require("truffle-assertions"); +const Reverter = require("./helpers/reverter"); + +const { utils } = require("ethers"); +const { accounts } = require("../scripts/utils/utils"); +const { assert } = require("chai"); + +const RoleManager = artifacts.require("RoleManager"); + +describe("RoleManager", () => { + const reverter = new Reverter(); + + let ROLE_MANAGER_ADMIN; + let ANOTHER_ACCOUNT; + let ANOTHER_ACCOUNT1; + let ANOTHER_ACCOUNT2; + + let roleManager; + + before("setup", async () => { + ROLE_MANAGER_ADMIN = await accounts(0); + ANOTHER_ACCOUNT = await accounts(1); + ANOTHER_ACCOUNT1 = await accounts(2); + ANOTHER_ACCOUNT2 = await accounts(3); + + const _roleManager = await RoleManager.new(); + + roleManager = await RoleManager.at(_roleManager.address); + + await reverter.snapshot(); + }); + + afterEach("revert", reverter.revert); + + describe("roleManagerInitialize()", () => { + it("should correctly grant the ROLE_MANAGER_ADMIN role to msg.sender", async () => { + await roleManager.roleManagerInitialize([], []); + const ROLE_MANAGER_ADMIN_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ADMIN")); + assert.equal(await roleManager.hasRole(ROLE_MANAGER_ADMIN_ROLE, ROLE_MANAGER_ADMIN), true); + }); + + it("should grant the roles passed to the corresponding accounts", async () => { + const ROLE_MANAGER_ADMIN_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ADMIN")); + const PRT_PARAM_UPDATER_ROLE = utils.keccak256(utils.toUtf8Bytes("PRT_PARAM_UPDATER")); + const DEFI_CORE_PAUSER_ROLE = utils.keccak256(utils.toUtf8Bytes("DEFI_CORE_PAUSER")); + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + + await roleManager.roleManagerInitialize( + [PRT_PARAM_UPDATER_ROLE, DEFI_CORE_PAUSER_ROLE, SYSTEM_POOLS_MANAGER_ROLE], + [ANOTHER_ACCOUNT, ANOTHER_ACCOUNT1, ANOTHER_ACCOUNT2] + ); + + assert.equal(await roleManager.hasRole(ROLE_MANAGER_ADMIN_ROLE, ROLE_MANAGER_ADMIN), true); + assert.equal(await roleManager.hasRole(PRT_PARAM_UPDATER_ROLE, ANOTHER_ACCOUNT), true); + assert.equal(await roleManager.hasRole(DEFI_CORE_PAUSER_ROLE, ANOTHER_ACCOUNT1), true); + assert.equal(await roleManager.hasRole(SYSTEM_POOLS_MANAGER_ROLE, ANOTHER_ACCOUNT2), true); + + assert.equal(await roleManager.hasRole(ROLE_MANAGER_ADMIN_ROLE, ANOTHER_ACCOUNT), false); + assert.equal(await roleManager.hasRole(PRT_PARAM_UPDATER_ROLE, ANOTHER_ACCOUNT2), false); + assert.equal(await roleManager.hasRole(DEFI_CORE_PAUSER_ROLE, ROLE_MANAGER_ADMIN), false); + assert.equal(await roleManager.hasRole(SYSTEM_POOLS_MANAGER_ROLE, ANOTHER_ACCOUNT1), false); + }); + + it("should grant the same roles to multiple accounts if needed", async () => { + const PRT_PARAM_UPDATER_ROLE = utils.keccak256(utils.toUtf8Bytes("PRT_PARAM_UPDATER")); + + await roleManager.roleManagerInitialize( + [PRT_PARAM_UPDATER_ROLE, PRT_PARAM_UPDATER_ROLE, PRT_PARAM_UPDATER_ROLE], + [ANOTHER_ACCOUNT, ANOTHER_ACCOUNT1, ANOTHER_ACCOUNT2] + ); + + assert.equal(await roleManager.hasRole(PRT_PARAM_UPDATER_ROLE, ANOTHER_ACCOUNT), true); + assert.equal(await roleManager.hasRole(PRT_PARAM_UPDATER_ROLE, ANOTHER_ACCOUNT1), true); + assert.equal(await roleManager.hasRole(PRT_PARAM_UPDATER_ROLE, ANOTHER_ACCOUNT2), true); + }); + + it("should revert if called after the initilizing", async () => { + const reason = "Initializable: contract is already initialized"; + await roleManager.roleManagerInitialize([], []); + await truffleAssert.reverts(roleManager.roleManagerInitialize([], []), reason); + }); + + it("should revert if roles and accounts arrays are of different size", async () => { + const reason = "RoleManager: passed arrays are of different sizes"; + + const PRT_PARAM_UPDATER_ROLE = utils.keccak256(utils.toUtf8Bytes("PRT_PARAM_UPDATER")); + const DEFI_CORE_PAUSER_ROLE = utils.keccak256(utils.toUtf8Bytes("DEFI_CORE_PAUSER")); + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + + await truffleAssert.reverts( + roleManager.roleManagerInitialize( + [PRT_PARAM_UPDATER_ROLE, DEFI_CORE_PAUSER_ROLE, SYSTEM_POOLS_MANAGER_ROLE], + [ANOTHER_ACCOUNT, ANOTHER_ACCOUNT1] + ), + reason + ); + + await truffleAssert.reverts( + roleManager.roleManagerInitialize( + [PRT_PARAM_UPDATER_ROLE, DEFI_CORE_PAUSER_ROLE], + [ANOTHER_ACCOUNT, ANOTHER_ACCOUNT1, ANOTHER_ACCOUNT2] + ), + reason + ); + }); + }); + + describe("isAssetParametersManager()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or ASSET_PARAMETERS_MANAGER role", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + + await roleManager.roleManagerInitialize([ASSET_PARAMETERS_MANAGER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isAssetParametersManager(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isAssetParametersManager(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor ASSET_PARAMETERS_MANAGER role", async () => { + const ASSET_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${ASSET_PARAMETERS_MANAGER_ROLE}`; + + await truffleAssert.reverts(roleManager.isAssetParametersManager(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("isDefiCorePauser()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or DEFI_CORE_PAUSER role", async () => { + const DEFI_CORE_PAUSER_ROLE = utils.keccak256(utils.toUtf8Bytes("DEFI_CORE_PAUSER")); + + await roleManager.roleManagerInitialize([DEFI_CORE_PAUSER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isDefiCorePauser(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isDefiCorePauser(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor DEFI_CORE_PAUSER role", async () => { + const DEFI_CORE_PAUSER_ROLE = utils.keccak256(utils.toUtf8Bytes("DEFI_CORE_PAUSER")); + const reason = `RoleManager: account is missing role ${DEFI_CORE_PAUSER_ROLE}`; + + await truffleAssert.reverts(roleManager.isDefiCorePauser(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("isPRTParamUpdater()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or PRT_PARAM_UPDATER role", async () => { + const PRT_PARAM_UPDATER_ROLE = utils.keccak256(utils.toUtf8Bytes("PRT_PARAM_UPDATER")); + + await roleManager.roleManagerInitialize([PRT_PARAM_UPDATER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isPRTParamUpdater(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isPRTParamUpdater(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor PRT_PARAM_UPDATER role", async () => { + const PRT_PARAM_UPDATER_ROLE = utils.keccak256(utils.toUtf8Bytes("PRT_PARAM_UPDATER")); + const reason = `RoleManager: account is missing role ${PRT_PARAM_UPDATER_ROLE}`; + + await truffleAssert.reverts(roleManager.isPRTParamUpdater(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("isRewardsDistributionManager()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or REWARDS_DISTRIBUTION_MANAGER role", async () => { + const REWARDS_DISTRIBUTION_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("REWARDS_DISTRIBUTION_MANAGER")); + + await roleManager.roleManagerInitialize([REWARDS_DISTRIBUTION_MANAGER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isRewardsDistributionManager(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isRewardsDistributionManager(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor REWARDS_DISTRIBUTION_MANAGER role", async () => { + const REWARDS_DISTRIBUTION_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("REWARDS_DISTRIBUTION_MANAGER")); + const reason = `RoleManager: account is missing role ${REWARDS_DISTRIBUTION_MANAGER_ROLE}`; + + await truffleAssert.reverts(roleManager.isRewardsDistributionManager(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("isSystemParametersManager()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or SYSTEM_PARAMETERS_MANAGER role", async () => { + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + + await roleManager.roleManagerInitialize([SYSTEM_PARAMETERS_MANAGER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isSystemParametersManager(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isSystemParametersManager(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor SYSTEM_PARAMETERS_MANAGER role", async () => { + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_PARAMETERS_MANAGER_ROLE}`; + + await truffleAssert.reverts(roleManager.isSystemParametersManager(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("isSystemPoolsManager()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or SYSTEM_POOLS_MANAGER role", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + + await roleManager.roleManagerInitialize([SYSTEM_POOLS_MANAGER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isSystemPoolsManager(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isSystemPoolsManager(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor SYSTEM_POOLS_MANAGER role", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; + + await truffleAssert.reverts(roleManager.isSystemPoolsManager(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("isSystemPoolsReserveFundsManager()", () => { + it("should successfuly pass if the account has the ROLE_MANAGER_ADMIN or SYSTEM_POOLS_RESERVE_FUNDS_MANAGER role", async () => { + const SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE = utils.keccak256( + utils.toUtf8Bytes("SYSTEM_POOLS_RESERVE_FUNDS_MANAGER") + ); + + await roleManager.roleManagerInitialize([SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.isSystemPoolsReserveFundsManager(ANOTHER_ACCOUNT)); + await truffleAssert.passes(roleManager.isSystemPoolsReserveFundsManager(ROLE_MANAGER_ADMIN)); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor SYSTEM_POOLS_RESERVE_FUNDS_MANAGER role", async () => { + const SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE = utils.keccak256( + utils.toUtf8Bytes("SYSTEM_POOLS_RESERVE_FUNDS_MANAGER") + ); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_RESERVE_FUNDS_MANAGER_ROLE}`; + + await truffleAssert.reverts(roleManager.isSystemPoolsReserveFundsManager(ANOTHER_ACCOUNT), reason); + }); + }); + + describe("grantRole()", () => { + it("should successfuly pass if the calling account has the ROLE_MANAGER_ADMIN or ROLE_MANAGER_ROLE_GOVERNOR role", async () => { + const ROLE_MANAGER_ROLE_GOVERNOR_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ROLE_GOVERNOR")); + + const ROLE_TO_GRANT = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + + await roleManager.roleManagerInitialize([ROLE_MANAGER_ROLE_GOVERNOR_ROLE], [ANOTHER_ACCOUNT]); + + await truffleAssert.passes(roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT })); + await truffleAssert.passes(roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1, { from: ROLE_MANAGER_ADMIN })); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor ROLE_MANAGER_ROLE_GOVERNOR role", async () => { + const ROLE_MANAGER_ROLE_GOVERNOR_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ROLE_GOVERNOR")); + const reason = `RoleManager: account is missing role ${ROLE_MANAGER_ROLE_GOVERNOR_ROLE}`; + + const ROLE_TO_GRANT = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + + await truffleAssert.reverts( + roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT }), + reason + ); + }); + }); + + describe("revokeRole()", () => { + it("should successfuly pass if the calling account has the ROLE_MANAGER_ADMIN or ROLE_MANAGER_ROLE_GOVERNOR role", async () => { + const ROLE_MANAGER_ROLE_GOVERNOR_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ROLE_GOVERNOR")); + const ROLE_TO_GRANT = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const ROLE_TO_REVOKE = ROLE_TO_GRANT; + + await roleManager.roleManagerInitialize([ROLE_MANAGER_ROLE_GOVERNOR_ROLE], [ANOTHER_ACCOUNT]); + + await roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1); + await roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT }); + + await roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1); + await roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1); + }); + + it("should successfuly pass if try to revoke the role the account doesn't have", async () => { + const ROLE_MANAGER_ROLE_GOVERNOR_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ROLE_GOVERNOR")); + const ROLE_TO_GRANT = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + const ROLE_TO_REVOKE = ROLE_TO_GRANT; + const ANOTHER_ROLE_TO_REVOKE = utils.keccak256(utils.toUtf8Bytes("ANOTHER ROLE")); + + await roleManager.roleManagerInitialize([ROLE_MANAGER_ROLE_GOVERNOR_ROLE], [ANOTHER_ACCOUNT]); + + await roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1); + await roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT }); + await roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT }); + + await roleManager.revokeRole(ANOTHER_ROLE_TO_REVOKE, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT }); + + await roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1); + await roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1); + await roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1); + + await roleManager.revokeRole(ANOTHER_ROLE_TO_REVOKE, ANOTHER_ACCOUNT1); + }); + + it("should revert if the account has neither ROLE_MANAGER_ADMIN nor ROLE_MANAGER_ROLE_GOVERNOR role", async () => { + const ROLE_MANAGER_ROLE_GOVERNOR_ROLE = utils.keccak256(utils.toUtf8Bytes("ROLE_MANAGER_ROLE_GOVERNOR")); + const reason = `RoleManager: account is missing role ${ROLE_MANAGER_ROLE_GOVERNOR_ROLE}`; + + await roleManager.roleManagerInitialize([], []); + + const ROLE_TO_GRANT = utils.keccak256(utils.toUtf8Bytes("ASSET_PARAMETERS_MANAGER")); + + await roleManager.grantRole(ROLE_TO_GRANT, ANOTHER_ACCOUNT1); + + const ROLE_TO_REVOKE = ROLE_TO_GRANT; + + await truffleAssert.reverts( + roleManager.revokeRole(ROLE_TO_REVOKE, ANOTHER_ACCOUNT1, { from: ANOTHER_ACCOUNT2 }), + reason + ); + }); + }); +}); diff --git a/test/stablePool.test.js b/test/stablePool.test.js index 58de125..919ed49 100644 --- a/test/stablePool.test.js +++ b/test/stablePool.test.js @@ -19,6 +19,8 @@ const LiquidityPool = artifacts.require("LiquidityPool"); const StablePool = artifacts.require("StablePool"); const PriceManager = artifacts.require("PriceManager"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); + const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const WETH = artifacts.require("WETH"); const StablePermitToken = artifacts.require("StablePermitTokenMock"); @@ -42,6 +44,7 @@ describe("StablePool", async () => { let systemPoolsRegistry; let stableToken; let prt; + let roleManager; let stablePool; @@ -161,6 +164,7 @@ describe("StablePool", async () => { const _stablePoolImpl = await StablePool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -175,12 +179,14 @@ describe("StablePool", async () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); defiCore = await DefiCore.at(await registry.getDefiCoreContract()); assetParameters = await AssetParameters.at(await registry.getAssetParametersContract()); systemPoolsRegistry = await SystemPoolsRegistry.at(await registry.getSystemPoolsRegistryContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); const systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); const rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); @@ -199,6 +205,7 @@ describe("StablePool", async () => { tokens.push(nativeToken); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize( _liquidityPoolImpl.address, nativeTokenKey, diff --git a/test/systemParameters.test.js b/test/systemParameters.test.js index df80990..f9a88ea 100644 --- a/test/systemParameters.test.js +++ b/test/systemParameters.test.js @@ -1,11 +1,13 @@ const { accounts, getPrecision, wei } = require("../scripts/utils/utils"); const { ZERO_ADDR } = require("../scripts/utils/constants"); +const { utils } = require("ethers"); const truffleAssert = require("truffle-assertions"); const Reverter = require("./helpers/reverter"); const SystemParameters = artifacts.require("SystemParameters"); const Registry = artifacts.require("Registry"); +const RoleManager = artifacts.require("RoleManager"); SystemParameters.numberFormat = "BigNumber"; Registry.numberFormat = "BigNumber"; @@ -19,6 +21,7 @@ describe("SystemParameters", () => { let systemParameters; let registry; + let roleManager; const minCurrencyAmount = wei(0.1); before("setup", async () => { @@ -28,12 +31,17 @@ describe("SystemParameters", () => { registry = await Registry.new(); const _systemParameters = await SystemParameters.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); await registry.addProxyContract(await registry.SYSTEM_PARAMETERS_NAME(), _systemParameters.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); + + roleManager.roleManagerInitialize([], []); await registry.injectDependencies(await registry.SYSTEM_PARAMETERS_NAME()); @@ -50,8 +58,9 @@ describe("SystemParameters", () => { }); describe("setupMinCurrencyAmount", () => { - it("should get exception if called by not a system owner", async () => { - const reason = "SystemParameters: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts( systemParameters.setupMinCurrencyAmount(minCurrencyAmount, { from: SOMEBODY }), @@ -85,8 +94,9 @@ describe("SystemParameters", () => { await truffleAssert.reverts(systemParameters.setRewardsTokenAddress(ZERO_ADDR), reason); }); - it("should get exception if called by not a system owner", async () => { - const reason = "SystemParameters: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts(systemParameters.setRewardsTokenAddress(ZERO_ADDR, { from: SOMEBODY }), reason); await truffleAssert.reverts(systemParameters.setRewardsTokenAddress(ZERO_ADDR, { from: SOMEBODY }), reason); @@ -113,6 +123,16 @@ describe("SystemParameters", () => { await truffleAssert.reverts(systemParameters.setupLiquidationBoundary(newValue), reason); }); + + it("should get exception if called not by an SYSTEM_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_PARAMETERS_MANAGER_ROLE}`; + + const newValue = getPrecision().times(50); + + await truffleAssert.reverts(systemParameters.setupLiquidationBoundary(newValue, { from: SOMEBODY }), reason); + await truffleAssert.reverts(systemParameters.setupLiquidationBoundary(newValue, { from: SOMEBODY }), reason); + }); }); describe("setupStablePoolsAvailability", () => { @@ -123,12 +143,22 @@ describe("SystemParameters", () => { assert.equal(txReceipt.receipt.logs[0].event, "StablePoolsAvailabilityUpdated"); assert.equal(txReceipt.receipt.logs[0].args.newValue, true); }); + + it("should get exception if called not by an SYSTEM_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_PARAMETERS_MANAGER_ROLE}`; + + await truffleAssert.reverts(systemParameters.setupStablePoolsAvailability(true, { from: SOMEBODY }), reason); + await truffleAssert.reverts(systemParameters.setupStablePoolsAvailability(true, { from: SOMEBODY }), reason); + }); }); - describe("onlySystemOwner modifier", () => { - it("should get exception if not system owner try to call functions", async () => { + describe("onlySystemParametersManager modifier", () => { + it("should get exception if called not by an SYSTEM_PARAMETERS_MANAGER or ROLE_MANAGER_ADMIN", async () => { const newValue = getPrecision().times(50); - const reason = "SystemParameters: Only system owner can call this function."; + + const SYSTEM_PARAMETERS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_PARAMETERS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_PARAMETERS_MANAGER_ROLE}`; await truffleAssert.reverts(systemParameters.setupLiquidationBoundary(newValue, { from: SOMEBODY }), reason); await truffleAssert.reverts(systemParameters.setupStablePoolsAvailability(true, { from: SOMEBODY }), reason); diff --git a/test/systemPoolsRegistry.test.js b/test/systemPoolsRegistry.test.js index e629a00..1d01b26 100644 --- a/test/systemPoolsRegistry.test.js +++ b/test/systemPoolsRegistry.test.js @@ -2,12 +2,12 @@ const { setNextBlockTime, getCurrentBlockTime } = require("./helpers/block-helpe const { toBytes, fromBytes, deepCompareKeys, compareKeys } = require("./helpers/bytesCompareLibrary"); const { getInterestRateLibraryAddr } = require("./helpers/coverage-helper"); const { toBN, accounts, getPrecision, getPercentage100, wei } = require("../scripts/utils/utils"); +const { utils } = require("ethers"); const { ZERO_ADDR } = require("../scripts/utils/constants"); const truffleAssert = require("truffle-assertions"); const Reverter = require("./helpers/reverter"); const { assert } = require("chai"); -const { artifacts } = require("hardhat"); const Registry = artifacts.require("Registry"); const DefiCore = artifacts.require("DefiCore"); @@ -24,6 +24,8 @@ const LiquidityPoolMock = artifacts.require("LiquidityPoolMock"); const AbstractPool = artifacts.require("AbstractPool"); const PriceManager = artifacts.require("PriceManager"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); + const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const WETH = artifacts.require("WETH"); const StablePermitToken = artifacts.require("StablePermitTokenMock"); @@ -71,6 +73,7 @@ describe("SystemPoolsRegistry", () => { let systemPoolsRegistry; let liquidityPoolFactory; let prt; + let roleManager; let rewardsToken; @@ -169,6 +172,7 @@ describe("SystemPoolsRegistry", () => { const _liquidityPoolImpl = await LiquidityPool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -181,6 +185,7 @@ describe("SystemPoolsRegistry", () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -190,6 +195,7 @@ describe("SystemPoolsRegistry", () => { rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); liquidityPoolFactory = await SystemPoolsFactory.at(await registry.getSystemPoolsFactoryContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); await registry.injectDependencies(await registry.DEFI_CORE_NAME()); await registry.injectDependencies(await registry.SYSTEM_PARAMETERS_NAME()); @@ -202,6 +208,7 @@ describe("SystemPoolsRegistry", () => { await registry.injectDependencies(await registry.PRT_NAME()); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, nativeTokenKey, zeroKey); await systemParameters.setupStablePoolsAvailability(true); @@ -283,8 +290,9 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(systemPoolsRegistry.updateRewardsAssetKey(someKey), reason); }); - it("should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; assert.isTrue(compareKeys(await systemPoolsRegistry.rewardsAssetKey(), zeroKey)); assert.equal(await getLiquidityPoolAddr(zeroKey), await systemPoolsRegistry.getRewardsLiquidityPool()); @@ -318,8 +326,9 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(systemPoolsRegistry.addPoolsBeacon(0, NOTHING), reason); }); - it("should get exception if not system owner try to call this function", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; await truffleAssert.reverts(systemPoolsRegistry.addPoolsBeacon(1, NOTHING, { from: USER1 }), reason); }); @@ -385,8 +394,9 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(liquidityPoolFactory.newLiquidityPool(TEST_ASSET, daiKey, "DAI"), reason); }); - it("should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; await truffleAssert.reverts( systemPoolsRegistry.addLiquidityPool(TEST_ASSET, daiKey, chainlinkOracle.address, "DAI", true, true, { @@ -431,8 +441,9 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(systemPoolsRegistry.addStablePool(NOTHING, someKey, NOTHING), reason); }); - it("should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; await truffleAssert.reverts( systemPoolsRegistry.addStablePool(someToken.address, someKey, NOTHING, { from: USER1 }), @@ -468,8 +479,10 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(systemPoolsRegistry.upgradePoolsImpl(1, NOTHING), reason); }); - it("should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("should get exception if called not by an SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; + await createLiquidityPool(daiKey, "DAI", true); const daiPool = await LiquidityPool.at(await getLiquidityPoolAddr(daiKey)); @@ -560,8 +573,9 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(abstractPools[0].setDependencies(registry.address, { from: USER2 }), reason); }); - it("injectDependenciesToExistingPools() should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("injectDependenciesToExistingPools() should get exception if called by not a SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; const currentPriceManager = await registry.getPriceManagerContract(); const newPriceManager = await PriceManager.new(); @@ -574,8 +588,10 @@ describe("SystemPoolsRegistry", () => { await truffleAssert.reverts(systemPoolsRegistry.injectDependenciesToExistingPools({ from: USER1 }), reason); }); - it("injectDependencies() should get exception if called by not a system owner", async () => { - const reason = "SystemPoolsRegistry: Only system owner can call this function."; + it("injectDependencies() should get exception if called by not a SYSTEM_POOLS_MANAGER or ROLE_MANAGER_ADMIN", async () => { + const SYSTEM_POOLS_MANAGER_ROLE = utils.keccak256(utils.toUtf8Bytes("SYSTEM_POOLS_MANAGER")); + const reason = `RoleManager: account is missing role ${SYSTEM_POOLS_MANAGER_ROLE}`; + const currentPriceManager = await registry.getPriceManagerContract(); const newPriceManager = await PriceManager.new(); diff --git a/test/userInfoRegistry.test.js b/test/userInfoRegistry.test.js index b3d7190..d075af6 100644 --- a/test/userInfoRegistry.test.js +++ b/test/userInfoRegistry.test.js @@ -20,6 +20,8 @@ const StablePool = artifacts.require("StablePool"); const PriceManager = artifacts.require("PriceManager"); const InterestRateLibrary = artifacts.require("InterestRateLibrary"); const Prt = artifacts.require("PRT"); +const RoleManager = artifacts.require("RoleManager"); + const WETH = artifacts.require("WETH"); const StablePermitToken = artifacts.require("StablePermitTokenMock"); @@ -49,6 +51,7 @@ describe("UserInfoRegistry", () => { let systemPoolsRegistry; let rewardsDistribution; let prt; + let roleManager; let nativePool; let daiPool; @@ -186,6 +189,7 @@ describe("UserInfoRegistry", () => { const _stablePoolImpl = await StablePool.new(); const _priceManager = await PriceManager.new(); const _prt = await Prt.new(); + const _roleManager = await RoleManager.new(); await registry.__OwnableContractsRegistry_init(); @@ -200,6 +204,7 @@ describe("UserInfoRegistry", () => { await registry.addProxyContract(await registry.SYSTEM_POOLS_FACTORY_NAME(), _liquidityPoolFactory.address); await registry.addProxyContract(await registry.PRICE_MANAGER_NAME(), _priceManager.address); await registry.addProxyContract(await registry.PRT_NAME(), _prt.address); + await registry.addProxyContract(await registry.ROLE_MANAGER_NAME(), _roleManager.address); await registry.addContract(await registry.INTEREST_RATE_LIBRARY_NAME(), interestRateLibrary.address); @@ -210,6 +215,7 @@ describe("UserInfoRegistry", () => { rewardsDistribution = await RewardsDistribution.at(await registry.getRewardsDistributionContract()); systemParameters = await SystemParameters.at(await registry.getSystemParametersContract()); prt = await Prt.at(await registry.getPRTContract()); + roleManager = await RoleManager.at(await registry.getRoleManagerContract()); await registry.injectDependencies(await registry.DEFI_CORE_NAME()); await registry.injectDependencies(await registry.SYSTEM_PARAMETERS_NAME()); @@ -226,6 +232,7 @@ describe("UserInfoRegistry", () => { tokens.push(nativeToken); await defiCore.defiCoreInitialize(); + await roleManager.roleManagerInitialize([], []); await systemPoolsRegistry.systemPoolsRegistryInitialize(_liquidityPoolImpl.address, nativeTokenKey, zeroKey); await prt.prtInitialize("Platform Reputation Token", "PRT", [ [1000000000000, 100],