Skip to content

Commit fdd512f

Browse files
committed
feat: access control roles
1 parent e9d49e5 commit fdd512f

File tree

5 files changed

+259
-30
lines changed

5 files changed

+259
-30
lines changed

contracts/tokenbridge/libraries/vault/IMasterVaultFactory.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ pragma solidity ^0.8.0;
33

44
interface IMasterVaultFactory {
55
event VaultDeployed(address indexed token, address indexed vault);
6-
event SubVaultSet(address indexed masterVault, address indexed subVault);
76

87
function initialize(address _owner) external;
98
function deployVault(address token) external returns (address vault);
109
function calculateVaultAddress(address token) external view returns (address);
1110
function getVault(address token) external returns (address);
12-
function setSubVault(address masterVault, address subVault, uint256 minSubVaultExchRateWad) external;
1311
}

contracts/tokenbridge/libraries/vault/MasterVault.sol

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@ import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/
66
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
77
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
88
import {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";
1011
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
1112
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
1213
import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
1314
import {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;

contracts/tokenbridge/libraries/vault/MasterVaultFactory.sol

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ contract MasterVaultFactory is IMasterVaultFactory, OwnableUpgradeable {
1717

1818
BeaconProxyFactory public beaconProxyFactory;
1919

20+
// todo: consider replacing OwnableUpgradeable with simple owner variable
2021
function initialize(address _owner) public initializer {
2122
_transferOwnership(_owner);
2223

@@ -44,7 +45,7 @@ contract MasterVaultFactory is IMasterVaultFactory, OwnableUpgradeable {
4445
string memory name = string(abi.encodePacked("Master ", tokenMetadata.name()));
4546
string memory symbol = string(abi.encodePacked("m", tokenMetadata.symbol()));
4647

47-
MasterVault(vault).initialize(IERC20(token), name, symbol, address(this));
48+
MasterVault(vault).initialize(IERC20(token), name, symbol, owner());
4849

4950
emit VaultDeployed(token, vault);
5051
}
@@ -65,14 +66,4 @@ contract MasterVaultFactory is IMasterVaultFactory, OwnableUpgradeable {
6566
}
6667
return vault;
6768
}
68-
69-
// todo: consider a method to enable bridge owner to transfer specific master vault ownership to new address
70-
function setSubVault(
71-
address masterVault,
72-
address subVault,
73-
uint256 minSubVaultExchRateWad
74-
) external onlyOwner {
75-
IMasterVault(masterVault).setSubVault(subVault, minSubVaultExchRateWad);
76-
emit SubVaultSet(masterVault, subVault);
77-
}
7869
}

0 commit comments

Comments
 (0)