@@ -6,16 +6,21 @@ import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/
66import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
77import {IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol " ;
88import {IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol " ;
9- import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol " ;
9+ import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol " ;
10+ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol " ;
1011import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol " ;
1112import {IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol " ;
1213import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol " ;
1314import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
1415
15- contract MasterVault is Initializable , ERC4626Upgradeable , OwnableUpgradeable {
16+ contract MasterVault is Initializable , ERC4626Upgradeable , AccessControlUpgradeable , PausableUpgradeable {
1617 using SafeERC20 for IERC20 ;
1718 using MathUpgradeable for uint256 ;
1819
20+ bytes32 public constant VAULT_MANAGER_ROLE = keccak256 ("VAULT_MANAGER_ROLE " );
21+ bytes32 public constant FEE_MANAGER_ROLE = keccak256 ("FEE_MANAGER_ROLE " );
22+ bytes32 public constant PAUSER_ROLE = keccak256 ("PAUSER_ROLE " );
23+
1924 error TooFewSharesReceived ();
2025 error TooManySharesBurned ();
2126 error TooManyAssetsDeposited ();
@@ -33,8 +38,6 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
3338 error InvalidAsset ();
3439 error InvalidOwner ();
3540
36- // todo: avoid inflation, rounding, other common 4626 vulns
37- // we may need a minimum asset or master share amount when setting subvaults (bc of exchange rate calc)
3841 IERC4626 public subVault;
3942
4043 // how many subVault shares one MV2 share can be redeemed for
@@ -61,7 +64,17 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
6164
6265 __ERC20_init (_name, _symbol);
6366 __ERC4626_init (IERC20Upgradeable (address (_asset)));
64- _transferOwnership (_owner);
67+ __AccessControl_init ();
68+ __Pausable_init ();
69+
70+ _setRoleAdmin (VAULT_MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
71+ _setRoleAdmin (FEE_MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
72+ _setRoleAdmin (PAUSER_ROLE, DEFAULT_ADMIN_ROLE);
73+
74+ _grantRole (DEFAULT_ADMIN_ROLE, _owner);
75+ _grantRole (VAULT_MANAGER_ROLE, _owner);
76+ _grantRole (FEE_MANAGER_ROLE, _owner);
77+ _grantRole (PAUSER_ROLE, _owner);
6578
6679 subVaultExchRateWad = 1e18 ;
6780 }
@@ -94,14 +107,14 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
94107 /// @notice Set a subvault. Can only be called if there is not already a subvault set.
95108 /// @param _subVault The subvault to set. Must be an ERC4626 vault with the same asset as this MasterVault.
96109 /// @param minSubVaultExchRateWad Minimum acceptable ratio (times 1e18) of new subvault shares to outstanding MasterVault shares after deposit.
97- function setSubVault (IERC4626 _subVault , uint256 minSubVaultExchRateWad ) external onlyOwner {
110+ function setSubVault (IERC4626 _subVault , uint256 minSubVaultExchRateWad ) external onlyRole (VAULT_MANAGER_ROLE) {
98111 if (address (subVault) != address (0 )) revert SubVaultAlreadySet ();
99112 _setSubVault (_subVault, minSubVaultExchRateWad);
100113 }
101114
102115 /// @notice Revokes the current subvault, moving all assets back to MasterVault
103116 /// @param minAssetExchRateWad Minimum acceptable ratio (times 1e18) of assets received from subvault to outstanding MasterVault shares
104- function revokeSubVault (uint256 minAssetExchRateWad ) external onlyOwner {
117+ function revokeSubVault (uint256 minAssetExchRateWad ) external onlyRole (VAULT_MANAGER_ROLE) {
105118 _revokeSubVault (minAssetExchRateWad);
106119 }
107120
@@ -142,7 +155,7 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
142155 /// @param newSubVault The new subvault to switch to, or zero address to revoke current subvault
143156 /// @param minAssetExchRateWad Minimum acceptable ratio (times 1e18) of assets received from old subvault to outstanding MasterVault shares
144157 /// @param minNewSubVaultExchRateWad Minimum acceptable ratio (times 1e18) of new subvault shares to outstanding MasterVault shares after deposit
145- function switchSubVault (IERC4626 newSubVault , uint256 minAssetExchRateWad , uint256 minNewSubVaultExchRateWad ) external onlyOwner {
158+ function switchSubVault (IERC4626 newSubVault , uint256 minAssetExchRateWad , uint256 minNewSubVaultExchRateWad ) external onlyRole (VAULT_MANAGER_ROLE) {
146159 _revokeSubVault (minAssetExchRateWad);
147160
148161 if (address (newSubVault) != address (0 )) {
@@ -160,22 +173,22 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
160173
161174 /// @notice Toggle performance fee collection on/off
162175 /// @param enabled True to enable performance fees, false to disable
163- function setPerformanceFee (bool enabled ) external onlyOwner {
176+ function setPerformanceFee (bool enabled ) external onlyRole (VAULT_MANAGER_ROLE) {
164177 enablePerformanceFee = enabled;
165178 emit PerformanceFeeToggled (enabled);
166179 }
167180
168181 /// @notice Set the beneficiary address for performance fees
169182 /// @param newBeneficiary Address to receive performance fees, zero address defaults to owner
170- function setBeneficiary (address newBeneficiary ) external onlyOwner {
183+ function setBeneficiary (address newBeneficiary ) external onlyRole (FEE_MANAGER_ROLE) {
171184 address oldBeneficiary = beneficiary;
172185 beneficiary = newBeneficiary;
173186 emit BeneficiaryUpdated (oldBeneficiary, newBeneficiary);
174187 }
175188
176189 /// @notice Withdraw all accumulated performance fees to beneficiary
177- /// @dev Only callable by owner when performance fees are enabled
178- function withdrawPerformanceFees () external onlyOwner {
190+ /// @dev Only callable by fee manager when performance fees are enabled
191+ function withdrawPerformanceFees () external onlyRole (FEE_MANAGER_ROLE) {
179192 if (! enablePerformanceFee) revert PerformanceFeeDisabled ();
180193 if (beneficiary == address (0 )) revert BeneficiaryNotSet ();
181194
@@ -189,6 +202,14 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
189202 }
190203 }
191204
205+ function pause () external onlyRole (PAUSER_ROLE) {
206+ _pause ();
207+ }
208+
209+ function unpause () external onlyRole (PAUSER_ROLE) {
210+ _unpause ();
211+ }
212+
192213 /** @dev See {IERC4626-totalAssets}. */
193214 function totalAssets () public view virtual override returns (uint256 ) {
194215 IERC4626 _subVault = subVault;
@@ -208,6 +229,9 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
208229
209230 /** @dev See {IERC4626-maxMint}. */
210231 function maxMint (address ) public view virtual override returns (uint256 ) {
232+ if (address (subVault) == address (0 )) {
233+ return type (uint256 ).max;
234+ }
211235 uint256 subShares = subVault.maxMint (address (this ));
212236 if (subShares == type (uint256 ).max) {
213237 return type (uint256 ).max;
@@ -255,7 +279,7 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
255279 address receiver ,
256280 uint256 assets ,
257281 uint256 shares
258- ) internal virtual override {
282+ ) internal virtual override whenNotPaused {
259283 super ._deposit (caller, receiver, assets, shares);
260284
261285 totalPrincipal += assets;
@@ -274,7 +298,7 @@ contract MasterVault is Initializable, ERC4626Upgradeable, OwnableUpgradeable {
274298 address _owner ,
275299 uint256 assets ,
276300 uint256 shares
277- ) internal virtual override {
301+ ) internal virtual override whenNotPaused {
278302 totalPrincipal -= assets;
279303
280304 IERC4626 _subVault = subVault;
0 commit comments