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  ();
@@ -26,16 +30,18 @@ contract MasterVault is ERC4626, Ownable {
2630    error NewSubVaultExchangeRateTooLow  ();
2731    error BeneficiaryNotSet  ();
2832    error PerformanceFeeDisabled  ();
33+     error InvalidAsset  ();
34+     error InvalidOwner  ();
2935
3036    // todo: avoid inflation, rounding, other common 4626 vulns 
3137    // we may need a minimum asset or master share amount when setting subvaults (bc of exchange rate calc) 
32-     ERC4626  public  subVault;
38+     IERC4626  public  subVault;
3339
3440    // how many subVault shares one MV2 share can be redeemed for 
3541    // initially 1 to 1 
3642    // constant per subvault 
3743    // changes when subvault is set 
38-     uint256  public  subVaultExchRateWad  =   1e18 ;
44+     uint256  public  subVaultExchRateWad;
3945
4046    // note: the performance fee can be avoided if the underlying strategy can be sandwiched (eg ETH to wstETH dex swap) 
4147    // maybe a simpler and more robust implementation would be for the owner to adjust the subVaultExchRateWad directly 
@@ -49,16 +55,26 @@ contract MasterVault is ERC4626, Ownable {
4955    event PerformanceFeeToggled  (bool  enabled );
5056    event BeneficiaryUpdated  (address  indexed  oldBeneficiary , address  indexed  newBeneficiary );
5157
52-     constructor (IERC20  _asset , string  memory  _name , string  memory  _symbol ) ERC20 (_name, _symbol) ERC4626 (_asset) Ownable () {}
58+     function initialize  (IERC20  _asset , string  memory  _name , string  memory  _symbol , address  _owner ) external  initializer {
59+         if  (address (_asset) ==  address (0 )) revert  InvalidAsset ();
60+         if  (_owner ==  address (0 )) revert  InvalidOwner ();
61+ 
62+         __ERC20_init (_name, _symbol);
63+         __ERC4626_init (IERC20Upgradeable (address (_asset)));
64+         _transferOwnership (_owner);
65+ 
66+         subVaultExchRateWad =  1e18 ;
67+     }
68+ 
5369
5470    function deposit  (uint256  assets , address  receiver , uint256  minSharesMinted ) public  returns  (uint256 ) {
55-         uint256  shares =  super . deposit (assets, receiver);
71+         uint256  shares =  deposit (assets, receiver);
5672        if  (shares <  minSharesMinted) revert  TooFewSharesReceived ();
5773        return  shares;
5874    }
5975
6076    function withdraw  (uint256  assets , address  receiver , address  _owner , uint256  maxSharesBurned ) public  returns  (uint256 ) {
61-         uint256  shares =  super . withdraw (assets, receiver, _owner);
77+         uint256  shares =  withdraw (assets, receiver, _owner);
6278        if  (shares >  maxSharesBurned) revert  TooManySharesBurned ();
6379        return  shares;
6480    }
@@ -78,7 +94,7 @@ contract MasterVault is ERC4626, Ownable {
7894    /// @notice Set a subvault. Can only be called if there is not already a subvault set. 
7995    /// @param  _subVault The subvault to set. Must be an ERC4626 vault with the same asset as this MasterVault. 
8096    /// @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 {
97+     function setSubVault  (IERC4626  _subVault , uint256  minSubVaultExchRateWad ) external  onlyOwner {
8298        if  (address (subVault) !=  address (0 )) revert  SubVaultAlreadySet ();
8399        _setSubVault (_subVault, minSubVaultExchRateWad);
84100    }
@@ -89,34 +105,34 @@ contract MasterVault is ERC4626, Ownable {
89105        _revokeSubVault (minAssetExchRateWad);
90106    }
91107
92-     function _setSubVault  (ERC4626  _subVault , uint256  minSubVaultExchRateWad ) internal  {
108+     function _setSubVault  (IERC4626  _subVault , uint256  minSubVaultExchRateWad ) internal  {
93109        if  (address (_subVault) ==  address (0 )) revert  SubVaultCannotBeZeroAddress ();
94110        if  (totalSupply () ==  0 ) revert  MustHaveSupplyBeforeSettingSubVault ();
95111        if  (address (_subVault.asset ()) !=  address (asset ())) revert  SubVaultAssetMismatch ();
96112
97113        IERC20 (asset ()).safeApprove (address (_subVault), type (uint256 ).max);
98114        uint256  subShares =  _subVault.deposit (totalAssets (), address (this ));
99115
100-         uint256  _subVaultExchRateWad =  subShares.mulDiv (1e18 , totalSupply (), Math.Rounding.Down);
116+         subVault =  _subVault;
117+ 
118+         uint256  _subVaultExchRateWad =  subShares.mulDiv (1e18 , totalAssets (), MathUpgradeable.Rounding.Down);
101119        if  (_subVaultExchRateWad <  minSubVaultExchRateWad) revert  SubVaultExchangeRateTooLow ();
102120        subVaultExchRateWad =  _subVaultExchRateWad;
103121
104-         subVault =  _subVault;
105- 
106122        emit  SubvaultChanged (address (0 ), address (_subVault));
107123    }
108124
109125    function _revokeSubVault  (uint256  minAssetExchRateWad ) internal  {
110-         ERC4626  oldSubVault =  subVault;
126+         IERC4626  oldSubVault =  subVault;
111127        if  (address (oldSubVault) ==  address (0 )) revert  NoExistingSubVault ();
112128
113129        uint256  _totalSupply =  totalSupply ();
114130        uint256  assetReceived =  oldSubVault.withdraw (oldSubVault.maxWithdraw (address (this )), address (this ), address (this ));
115-         uint256  effectiveAssetExchRateWad =  assetReceived.mulDiv (1e18 , _totalSupply, Math .Rounding.Down);
131+         uint256  effectiveAssetExchRateWad =  assetReceived.mulDiv (1e18 , _totalSupply, MathUpgradeable .Rounding.Down);
116132        if  (effectiveAssetExchRateWad <  minAssetExchRateWad) revert  TooFewAssetsReceived ();
117133
118134        IERC20 (asset ()).safeApprove (address (oldSubVault), 0 );
119-         subVault =  ERC4626 (address (0 ));
135+         subVault =  IERC4626 (address (0 ));
120136        subVaultExchRateWad =  1e18 ;
121137
122138        emit  SubvaultChanged (address (oldSubVault), address (0 ));
@@ -126,19 +142,19 @@ contract MasterVault is ERC4626, Ownable {
126142    /// @param newSubVault The new subvault to switch to, or zero address to revoke current subvault 
127143    /// @param minAssetExchRateWad Minimum acceptable ratio (times 1e18) of assets received from old subvault to outstanding MasterVault shares 
128144    /// @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 {
145+     function switchSubVault  (IERC4626  newSubVault , uint256  minAssetExchRateWad , uint256  minNewSubVaultExchRateWad ) external  onlyOwner {
130146        _revokeSubVault (minAssetExchRateWad);
131147
132148        if  (address (newSubVault) !=  address (0 )) {
133149            _setSubVault (newSubVault, minNewSubVaultExchRateWad);
134150        }
135151    }
136152
137-     function masterSharesToSubShares  (uint256  masterShares , Math .Rounding rounding ) public  view  returns  (uint256 ) {
153+     function masterSharesToSubShares  (uint256  masterShares , MathUpgradeable .Rounding rounding ) public  view  returns  (uint256 ) {
138154        return  masterShares.mulDiv (subVaultExchRateWad, 1e18 , rounding);
139155    }
140156
141-     function subSharesToMasterShares  (uint256  subShares , Math .Rounding rounding ) public  view  returns  (uint256 ) {
157+     function subSharesToMasterShares  (uint256  subShares , MathUpgradeable .Rounding rounding ) public  view  returns  (uint256 ) {
142158        return  subShares.mulDiv (1e18 , subVaultExchRateWad, rounding);
143159    }
144160
@@ -165,7 +181,7 @@ contract MasterVault is ERC4626, Ownable {
165181
166182        uint256  totalProfits =  totalProfit ();
167183        if  (totalProfits >  0 ) {
168-             ERC4626  _subVault =  subVault;
184+             IERC4626  _subVault =  subVault;
169185            if  (address (_subVault) !=  address (0 )) {
170186                _subVault.withdraw (totalProfits, address (this ), address (this ));
171187            }
@@ -175,7 +191,7 @@ contract MasterVault is ERC4626, Ownable {
175191
176192    /** @dev See {IERC4626-totalAssets}. */ 
177193    function totalAssets  () public  view  virtual  override  returns  (uint256 ) {
178-         ERC4626  _subVault =  subVault;
194+         IERC4626  _subVault =  subVault;
179195        if  (address (_subVault) ==  address (0 )) {
180196            return  super .totalAssets ();
181197        }
@@ -196,7 +212,7 @@ contract MasterVault is ERC4626, Ownable {
196212        if  (subShares ==  type (uint256 ).max) {
197213            return  type (uint256 ).max;
198214        }
199-         return  subSharesToMasterShares (subShares, Math .Rounding.Down);
215+         return  subSharesToMasterShares (subShares, MathUpgradeable .Rounding.Down);
200216    }
201217
202218    /** 
@@ -205,25 +221,25 @@ contract MasterVault is ERC4626, Ownable {
205221     * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset 
206222     * would represent an infinite amount of shares. 
207223     */ 
208-     function _convertToShares  (uint256  assets , Math .Rounding rounding ) internal  view  virtual  override  returns  (uint256  shares ) {
209-         ERC4626  _subVault =  subVault;
224+     function _convertToShares  (uint256  assets , MathUpgradeable .Rounding rounding ) internal  view  virtual  override  returns  (uint256  shares ) {
225+         IERC4626  _subVault =  subVault;
210226        if  (address (_subVault) ==  address (0 )) {
211227            return  super ._convertToShares (assets, rounding);
212228        }
213-         uint256  subShares =  rounding ==  Math .Rounding.Up ?  _subVault.previewWithdraw (assets) :  _subVault.previewDeposit (assets);
229+         uint256  subShares =  rounding ==  MathUpgradeable .Rounding.Up ?  _subVault.previewWithdraw (assets) :  _subVault.previewDeposit (assets);
214230        return  subSharesToMasterShares (subShares, rounding);
215231    }
216232
217233    /** 
218234     * @dev Internal conversion function (from shares to assets) with support for rounding direction. 
219235     */ 
220-     function _convertToAssets  (uint256  shares , Math .Rounding rounding ) internal  view  virtual  override  returns  (uint256  assets ) {
221-         ERC4626  _subVault =  subVault;
236+     function _convertToAssets  (uint256  shares , MathUpgradeable .Rounding rounding ) internal  view  virtual  override  returns  (uint256  assets ) {
237+         IERC4626  _subVault =  subVault;
222238        if  (address (_subVault) ==  address (0 )) {
223239            return  super ._convertToAssets (shares, rounding);
224240        }
225241        uint256  subShares =  masterSharesToSubShares (shares, rounding);
226-         return  rounding ==  Math .Rounding.Up ?  _subVault.previewMint (subShares) :  _subVault.previewRedeem (subShares);
242+         return  rounding ==  MathUpgradeable .Rounding.Up ?  _subVault.previewMint (subShares) :  _subVault.previewRedeem (subShares);
227243    }
228244
229245    function totalProfit  () public  view  returns  (uint256 ) {
@@ -241,10 +257,11 @@ contract MasterVault is ERC4626, Ownable {
241257        uint256  shares 
242258    ) internal  virtual  override  {
243259        super ._deposit (caller, receiver, assets, shares);
260+ 
244261        totalPrincipal +=  assets;
245-         ERC4626  _subVault =  subVault;
262+         IERC4626  _subVault =  subVault;
246263        if  (address (_subVault) !=  address (0 )) {
247-              _subVault.deposit (assets, address (this ));
264+            _subVault.deposit (assets, address (this ));
248265        }
249266    }
250267
@@ -260,7 +277,7 @@ contract MasterVault is ERC4626, Ownable {
260277    ) internal  virtual  override  {
261278        totalPrincipal -=  assets;
262279
263-         ERC4626  _subVault =  subVault;
280+         IERC4626  _subVault =  subVault;
264281        if  (address (_subVault) !=  address (0 )) {
265282            _subVault.withdraw (assets, address (this ), address (this ));
266283        }
0 commit comments