-
Notifications
You must be signed in to change notification settings - Fork 156
feat: add master vault #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
function _setSubVault(ERC4626 _subVault, uint256 minSubVaultExchRateWad) internal { | ||
if (address(_subVault) == address(0)) revert SubVaultCannotBeZeroAddress(); | ||
if (totalSupply() == 0) revert MustHaveSupplyBeforeSettingSubVault(); | ||
if (address(_subVault.asset()) != address(asset())) revert SubVaultAssetMismatch(); | ||
|
||
IERC20(asset()).safeApprove(address(_subVault), type(uint256).max); | ||
uint256 subShares = _subVault.deposit(totalAssets(), address(this)); | ||
|
||
uint256 _subVaultExchRateWad = subShares.mulDiv(1e18, totalSupply(), Math.Rounding.Down); | ||
if (_subVaultExchRateWad < minSubVaultExchRateWad) revert SubVaultExchangeRateTooLow(); | ||
subVaultExchRateWad = _subVaultExchRateWad; | ||
|
||
subVault = _subVault; | ||
|
||
emit SubvaultChanged(address(0), address(_subVault)); | ||
} |
Check warning
Code scanning / Slither
Dangerous strict equalities Medium
function _setSubVault(ERC4626 _subVault, uint256 minSubVaultExchRateWad) internal { | ||
if (address(_subVault) == address(0)) revert SubVaultCannotBeZeroAddress(); | ||
if (totalSupply() == 0) revert MustHaveSupplyBeforeSettingSubVault(); | ||
if (address(_subVault.asset()) != address(asset())) revert SubVaultAssetMismatch(); | ||
|
||
IERC20(asset()).safeApprove(address(_subVault), type(uint256).max); | ||
uint256 subShares = _subVault.deposit(totalAssets(), address(this)); | ||
|
||
uint256 _subVaultExchRateWad = subShares.mulDiv(1e18, totalSupply(), Math.Rounding.Down); | ||
if (_subVaultExchRateWad < minSubVaultExchRateWad) revert SubVaultExchangeRateTooLow(); | ||
subVaultExchRateWad = _subVaultExchRateWad; | ||
|
||
subVault = _subVault; | ||
|
||
emit SubvaultChanged(address(0), address(_subVault)); | ||
} |
Check warning
Code scanning / Slither
Reentrancy vulnerabilities Medium
External calls:
- IERC20(asset()).safeApprove(address(_subVault),type()(uint256).max)
- subShares = _subVault.deposit(totalAssets(),address(this))
State variables written after the call(s):
- subVault = _subVault
MasterVault.subVault can be used in cross function reentrancies:
- MasterVault._convertToAssets(uint256,Math.Rounding)
- MasterVault._convertToShares(uint256,Math.Rounding)
- MasterVault._deposit(address,address,uint256,uint256)
- MasterVault._revokeSubVault(uint256)
- MasterVault._setSubVault(ERC4626,uint256)
- MasterVault._withdraw(address,address,address,uint256,uint256)
- MasterVault.maxDeposit(address)
- MasterVault.maxMint(address)
- MasterVault.setSubVault(ERC4626,uint256)
- MasterVault.subVault
- MasterVault.totalAssets()
- MasterVault.withdrawPerformanceFees()
function _revokeSubVault(uint256 minAssetExchRateWad) internal { | ||
ERC4626 oldSubVault = subVault; | ||
if (address(oldSubVault) == address(0)) revert NoExistingSubVault(); | ||
|
||
uint256 _totalSupply = totalSupply(); | ||
uint256 assetReceived = oldSubVault.withdraw(oldSubVault.maxWithdraw(address(this)), address(this), address(this)); | ||
uint256 effectiveAssetExchRateWad = assetReceived.mulDiv(1e18, _totalSupply, Math.Rounding.Down); | ||
if (effectiveAssetExchRateWad < minAssetExchRateWad) revert TooFewAssetsReceived(); | ||
|
||
IERC20(asset()).safeApprove(address(oldSubVault), 0); | ||
subVault = ERC4626(address(0)); | ||
subVaultExchRateWad = 1e18; | ||
|
||
emit SubvaultChanged(address(oldSubVault), address(0)); | ||
} |
Check warning
Code scanning / Slither
Reentrancy vulnerabilities Medium
External calls:
- assetReceived = oldSubVault.withdraw(oldSubVault.maxWithdraw(address(this)),address(this),address(this))
- IERC20(asset()).safeApprove(address(oldSubVault),0)
State variables written after the call(s):
- subVault = ERC4626(address(0))
MasterVault.subVault can be used in cross function reentrancies:
- MasterVault._convertToAssets(uint256,Math.Rounding)
- MasterVault._convertToShares(uint256,Math.Rounding)
- MasterVault._deposit(address,address,uint256,uint256)
- MasterVault._revokeSubVault(uint256)
- MasterVault._setSubVault(ERC4626,uint256)
- MasterVault._withdraw(address,address,address,uint256,uint256)
- MasterVault.maxDeposit(address)
- MasterVault.maxMint(address)
- MasterVault.setSubVault(ERC4626,uint256)
- MasterVault.subVault
- MasterVault.totalAssets()
- MasterVault.withdrawPerformanceFees()
function _deposit( | ||
address caller, | ||
address receiver, | ||
uint256 assets, | ||
uint256 shares | ||
) internal virtual override { | ||
super._deposit(caller, receiver, assets, shares); | ||
totalPrincipal += assets; | ||
ERC4626 _subVault = subVault; | ||
if (address(_subVault) != address(0)) { | ||
_subVault.deposit(assets, address(this)); | ||
} | ||
} |
Check warning
Code scanning / Slither
Unused return Medium
function _withdraw( | ||
address caller, | ||
address receiver, | ||
address _owner, | ||
uint256 assets, | ||
uint256 shares | ||
) internal virtual override { | ||
ERC4626 _subVault = subVault; | ||
if (address(_subVault) != address(0)) { | ||
_subVault.withdraw(assets, address(this), address(this)); | ||
} | ||
|
||
////// PERF FEE STUFF ////// | ||
// determine profit portion and principal portion of assets | ||
uint256 _totalProfit = totalProfit(); | ||
// use shares because they are rounded up vs assets which are rounded down | ||
uint256 profitPortion = shares.mulDiv(_totalProfit, totalSupply(), Math.Rounding.Up); | ||
uint256 principalPortion = assets - profitPortion; | ||
|
||
// subtract principal portion from totalPrincipal | ||
totalPrincipal -= principalPortion; | ||
|
||
// send fee to owner (todo should be a separate beneficiary addr set by owner) | ||
if (performanceFeeBps > 0 && profitPortion > 0) { | ||
uint256 fee = profitPortion.mulDiv(performanceFeeBps, 10000, Math.Rounding.Up); | ||
// send fee to owner | ||
IERC20(asset()).safeTransfer(owner(), fee); | ||
|
||
// note subtraction | ||
assets -= fee; | ||
} | ||
|
||
////// END PERF FEE STUFF ////// | ||
|
||
// call super._withdraw with remaining assets | ||
super._withdraw(caller, receiver, _owner, assets, shares); | ||
} |
Check warning
Code scanning / Slither
Unused return Medium
function withdrawPerformanceFees() external onlyOwner { | ||
if (!enablePerformanceFee) revert PerformanceFeeDisabled(); | ||
if (beneficiary == address(0)) revert BeneficiaryNotSet(); | ||
|
||
uint256 totalProfits = totalProfit(); | ||
if (totalProfits > 0) { | ||
ERC4626 _subVault = subVault; | ||
if (address(_subVault) != address(0)) { | ||
_subVault.withdraw(totalProfits, address(this), address(this)); | ||
} | ||
IERC20(asset()).safeTransfer(beneficiary, totalProfits); | ||
} | ||
} |
Check warning
Code scanning / Slither
Unused return Medium
590a713
to
fc89964
Compare
if (address(oldSubVault) == address(0)) revert NoExistingSubVault(); | ||
|
||
uint256 _totalSupply = totalSupply(); | ||
uint256 assetReceived = oldSubVault.withdraw(oldSubVault.maxWithdraw(address(this)), address(this), address(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is an edge case here - the subvault may not have enough liquidity to serve this big withdrawal all at once.
we probably need to make switching vaults more robust to those liquidity constaints.
the same could be said about depositing to the new vault, it could be such a large deposit that slippage starts to become a serious issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could do check whether maxWithdraw
will return same amount of what master vault actually own
This isolate the implementation for the master vault and its related factory and subvault.