11// SPDX-License-Identifier: Apache-2.0
22pragma solidity ^ 0.8.0 ;
33
4- import {ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol " ;
5- import { IERC20 , ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol " ;
6- import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol " ;
4+ import {ERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol " ;
5+ import {ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol " ;
6+ import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
7+ import {IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol " ;
8+ import {IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol " ;
9+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol " ;
10+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol " ;
711import {IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol " ;
8- import {Math } from "@openzeppelin/contracts/utils/math/Math .sol " ;
12+ import {MathUpgradeable } from "@openzeppelin/contracts-upgradeable /utils/math/MathUpgradeable .sol " ;
913import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
1014
11- contract MasterVault is ERC4626 , Ownable {
15+ contract MasterVault is Initializable , ERC4626Upgradeable , OwnableUpgradeable {
1216 using SafeERC20 for IERC20 ;
13- using Math for uint256 ;
17+ using MathUpgradeable for uint256 ;
1418
1519 error TooFewSharesReceived ();
1620 error TooManySharesBurned ();
@@ -29,13 +33,13 @@ contract MasterVault is ERC4626, Ownable {
2933
3034 // todo: avoid inflation, rounding, other common 4626 vulns
3135 // we may need a minimum asset or master share amount when setting subvaults (bc of exchange rate calc)
32- ERC4626 public subVault;
36+ IERC4626 public subVault;
3337
3438 // how many subVault shares one MV2 share can be redeemed for
3539 // initially 1 to 1
3640 // constant per subvault
3741 // changes when subvault is set
38- uint256 public subVaultExchRateWad = 1e18 ;
42+ uint256 public subVaultExchRateWad;
3943
4044 // note: the performance fee can be avoided if the underlying strategy can be sandwiched (eg ETH to wstETH dex swap)
4145 // maybe a simpler and more robust implementation would be for the owner to adjust the subVaultExchRateWad directly
@@ -49,16 +53,27 @@ contract MasterVault is ERC4626, Ownable {
4953 event PerformanceFeeToggled (bool enabled );
5054 event BeneficiaryUpdated (address indexed oldBeneficiary , address indexed newBeneficiary );
5155
52- constructor (IERC20 _asset , string memory _name , string memory _symbol ) ERC20 (_name, _symbol) ERC4626 (_asset) Ownable () {}
56+ function vaultInit (IERC20 _asset , string memory _name , string memory _symbol , address _owner ) external initializer {
57+ require (address (_asset) != address (0 ), "INVALID_ASSET " );
58+ require (_owner != address (0 ), "INVALID_OWNER " );
59+
60+ __ERC20_init (_name, _symbol);
61+ __ERC4626_init (IERC20Upgradeable (address (_asset)));
62+ __Ownable_init ();
63+ _transferOwnership (_owner);
64+
65+ subVaultExchRateWad = 1e18 ;
66+ }
67+
5368
5469 function deposit (uint256 assets , address receiver , uint256 minSharesMinted ) public returns (uint256 ) {
55- uint256 shares = super . deposit (assets, receiver);
70+ uint256 shares = deposit (assets, receiver);
5671 if (shares < minSharesMinted) revert TooFewSharesReceived ();
5772 return shares;
5873 }
5974
6075 function withdraw (uint256 assets , address receiver , address _owner , uint256 maxSharesBurned ) public returns (uint256 ) {
61- uint256 shares = super . withdraw (assets, receiver, _owner);
76+ uint256 shares = withdraw (assets, receiver, _owner);
6277 if (shares > maxSharesBurned) revert TooManySharesBurned ();
6378 return shares;
6479 }
@@ -78,7 +93,7 @@ contract MasterVault is ERC4626, Ownable {
7893 /// @notice Set a subvault. Can only be called if there is not already a subvault set.
7994 /// @param _subVault The subvault to set. Must be an ERC4626 vault with the same asset as this MasterVault.
8095 /// @param minSubVaultExchRateWad Minimum acceptable ratio (times 1e18) of new subvault shares to outstanding MasterVault shares after deposit.
81- function setSubVault (ERC4626 _subVault , uint256 minSubVaultExchRateWad ) external onlyOwner {
96+ function setSubVault (IERC4626 _subVault , uint256 minSubVaultExchRateWad ) external onlyOwner {
8297 if (address (subVault) != address (0 )) revert SubVaultAlreadySet ();
8398 _setSubVault (_subVault, minSubVaultExchRateWad);
8499 }
@@ -89,15 +104,15 @@ contract MasterVault is ERC4626, Ownable {
89104 _revokeSubVault (minAssetExchRateWad);
90105 }
91106
92- function _setSubVault (ERC4626 _subVault , uint256 minSubVaultExchRateWad ) internal {
107+ function _setSubVault (IERC4626 _subVault , uint256 minSubVaultExchRateWad ) internal {
93108 if (address (_subVault) == address (0 )) revert SubVaultCannotBeZeroAddress ();
94109 if (totalSupply () == 0 ) revert MustHaveSupplyBeforeSettingSubVault ();
95110 if (address (_subVault.asset ()) != address (asset ())) revert SubVaultAssetMismatch ();
96111
97112 IERC20 (asset ()).safeApprove (address (_subVault), type (uint256 ).max);
98113 uint256 subShares = _subVault.deposit (totalAssets (), address (this ));
99114
100- uint256 _subVaultExchRateWad = subShares.mulDiv (1e18 , totalSupply (), Math .Rounding.Down);
115+ uint256 _subVaultExchRateWad = subShares.mulDiv (1e18 , totalSupply (), MathUpgradeable .Rounding.Down);
101116 if (_subVaultExchRateWad < minSubVaultExchRateWad) revert SubVaultExchangeRateTooLow ();
102117 subVaultExchRateWad = _subVaultExchRateWad;
103118
@@ -107,16 +122,16 @@ contract MasterVault is ERC4626, Ownable {
107122 }
108123
109124 function _revokeSubVault (uint256 minAssetExchRateWad ) internal {
110- ERC4626 oldSubVault = subVault;
125+ IERC4626 oldSubVault = subVault;
111126 if (address (oldSubVault) == address (0 )) revert NoExistingSubVault ();
112127
113128 uint256 _totalSupply = totalSupply ();
114129 uint256 assetReceived = oldSubVault.withdraw (oldSubVault.maxWithdraw (address (this )), address (this ), address (this ));
115- uint256 effectiveAssetExchRateWad = assetReceived.mulDiv (1e18 , _totalSupply, Math .Rounding.Down);
130+ uint256 effectiveAssetExchRateWad = assetReceived.mulDiv (1e18 , _totalSupply, MathUpgradeable .Rounding.Down);
116131 if (effectiveAssetExchRateWad < minAssetExchRateWad) revert TooFewAssetsReceived ();
117132
118133 IERC20 (asset ()).safeApprove (address (oldSubVault), 0 );
119- subVault = ERC4626 (address (0 ));
134+ subVault = IERC4626 (address (0 ));
120135 subVaultExchRateWad = 1e18 ;
121136
122137 emit SubvaultChanged (address (oldSubVault), address (0 ));
@@ -126,19 +141,19 @@ contract MasterVault is ERC4626, Ownable {
126141 /// @param newSubVault The new subvault to switch to, or zero address to revoke current subvault
127142 /// @param minAssetExchRateWad Minimum acceptable ratio (times 1e18) of assets received from old subvault to outstanding MasterVault shares
128143 /// @param minNewSubVaultExchRateWad Minimum acceptable ratio (times 1e18) of new subvault shares to outstanding MasterVault shares after deposit
129- function switchSubVault (ERC4626 newSubVault , uint256 minAssetExchRateWad , uint256 minNewSubVaultExchRateWad ) external onlyOwner {
144+ function switchSubVault (IERC4626 newSubVault , uint256 minAssetExchRateWad , uint256 minNewSubVaultExchRateWad ) external onlyOwner {
130145 _revokeSubVault (minAssetExchRateWad);
131146
132147 if (address (newSubVault) != address (0 )) {
133148 _setSubVault (newSubVault, minNewSubVaultExchRateWad);
134149 }
135150 }
136151
137- function masterSharesToSubShares (uint256 masterShares , Math .Rounding rounding ) public view returns (uint256 ) {
152+ function masterSharesToSubShares (uint256 masterShares , MathUpgradeable .Rounding rounding ) public view returns (uint256 ) {
138153 return masterShares.mulDiv (subVaultExchRateWad, 1e18 , rounding);
139154 }
140155
141- function subSharesToMasterShares (uint256 subShares , Math .Rounding rounding ) public view returns (uint256 ) {
156+ function subSharesToMasterShares (uint256 subShares , MathUpgradeable .Rounding rounding ) public view returns (uint256 ) {
142157 return subShares.mulDiv (1e18 , subVaultExchRateWad, rounding);
143158 }
144159
@@ -165,7 +180,7 @@ contract MasterVault is ERC4626, Ownable {
165180
166181 uint256 totalProfits = totalProfit ();
167182 if (totalProfits > 0 ) {
168- ERC4626 _subVault = subVault;
183+ IERC4626 _subVault = subVault;
169184 if (address (_subVault) != address (0 )) {
170185 _subVault.withdraw (totalProfits, address (this ), address (this ));
171186 }
@@ -175,7 +190,7 @@ contract MasterVault is ERC4626, Ownable {
175190
176191 /** @dev See {IERC4626-totalAssets}. */
177192 function totalAssets () public view virtual override returns (uint256 ) {
178- ERC4626 _subVault = subVault;
193+ IERC4626 _subVault = subVault;
179194 if (address (_subVault) == address (0 )) {
180195 return super .totalAssets ();
181196 }
@@ -196,7 +211,7 @@ contract MasterVault is ERC4626, Ownable {
196211 if (subShares == type (uint256 ).max) {
197212 return type (uint256 ).max;
198213 }
199- return subSharesToMasterShares (subShares, Math .Rounding.Down);
214+ return subSharesToMasterShares (subShares, MathUpgradeable .Rounding.Down);
200215 }
201216
202217 /**
@@ -205,25 +220,25 @@ contract MasterVault is ERC4626, Ownable {
205220 * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset
206221 * would represent an infinite amount of shares.
207222 */
208- function _convertToShares (uint256 assets , Math .Rounding rounding ) internal view virtual override returns (uint256 shares ) {
209- ERC4626 _subVault = subVault;
223+ function _convertToShares (uint256 assets , MathUpgradeable .Rounding rounding ) internal view virtual override returns (uint256 shares ) {
224+ IERC4626 _subVault = subVault;
210225 if (address (_subVault) == address (0 )) {
211226 return super ._convertToShares (assets, rounding);
212227 }
213- uint256 subShares = rounding == Math .Rounding.Up ? _subVault.previewWithdraw (assets) : _subVault.previewDeposit (assets);
228+ uint256 subShares = rounding == MathUpgradeable .Rounding.Up ? _subVault.previewWithdraw (assets) : _subVault.previewDeposit (assets);
214229 return subSharesToMasterShares (subShares, rounding);
215230 }
216231
217232 /**
218233 * @dev Internal conversion function (from shares to assets) with support for rounding direction.
219234 */
220- function _convertToAssets (uint256 shares , Math .Rounding rounding ) internal view virtual override returns (uint256 assets ) {
221- ERC4626 _subVault = subVault;
235+ function _convertToAssets (uint256 shares , MathUpgradeable .Rounding rounding ) internal view virtual override returns (uint256 assets ) {
236+ IERC4626 _subVault = subVault;
222237 if (address (_subVault) == address (0 )) {
223238 return super ._convertToAssets (shares, rounding);
224239 }
225240 uint256 subShares = masterSharesToSubShares (shares, rounding);
226- return rounding == Math .Rounding.Up ? _subVault.previewMint (subShares) : _subVault.previewRedeem (subShares);
241+ return rounding == MathUpgradeable .Rounding.Up ? _subVault.previewMint (subShares) : _subVault.previewRedeem (subShares);
227242 }
228243
229244 function totalProfit () public view returns (uint256 ) {
@@ -241,8 +256,9 @@ contract MasterVault is ERC4626, Ownable {
241256 uint256 shares
242257 ) internal virtual override {
243258 super ._deposit (caller, receiver, assets, shares);
259+
244260 totalPrincipal += assets;
245- ERC4626 _subVault = subVault;
261+ IERC4626 _subVault = subVault;
246262 if (address (_subVault) != address (0 )) {
247263 _subVault.deposit (assets, address (this ));
248264 }
@@ -260,7 +276,7 @@ contract MasterVault is ERC4626, Ownable {
260276 ) internal virtual override {
261277 totalPrincipal -= assets;
262278
263- ERC4626 _subVault = subVault;
279+ IERC4626 _subVault = subVault;
264280 if (address (_subVault) != address (0 )) {
265281 _subVault.withdraw (assets, address (this ), address (this ));
266282 }
0 commit comments