-
Notifications
You must be signed in to change notification settings - Fork 159
MasterVault reserve #141
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
MasterVault reserve #141
Conversation
* wip: dead shares and fix div by zero * doc * add one
| } | ||
|
|
||
| function deployVault(address token) public returns (address vault) { | ||
| if (token == address(0)) { |
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.
do we actually need a vault with asset with zero address?
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.
no, i don't think we need to prevent it though
| subVault.withdraw(assetsToWithdraw, address(this), address(this)); | ||
| } | ||
|
|
||
| super._withdraw(caller, receiver, _owner, assets, shares); |
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.
can this fail silently?
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.
no
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal virtual {
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
// If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
// `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
// shares are burned and after the assets are transferred, which is a valid state.
_burn(owner, shares);
SafeERC20Upgradeable.safeTransfer(_asset, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
}|
looks solid implementation to me, we may need a bit of fuzz testing to capture edge cases |
|
|
||
| oldSubVault.redeem(oldSubVault.balanceOf(address(this)), address(this), address(this)); | ||
| IERC20(asset()).safeApprove(address(oldSubVault), 0); | ||
| function rebalance() public { |
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.
since that this method is public, could be exploited by slippage by manipulating sub assets to shares ratio?
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.
hmm, maybe? what would be the exploit?
Sherlock AI |
| function _rebalance() internal { | ||
| uint256 totalAssetsUp = _totalAssetsLessProfit(MathUpgradeable.Rounding.Up); | ||
| uint256 totalAssetsDown = _totalAssetsLessProfit(MathUpgradeable.Rounding.Down); | ||
| uint256 idleTargetUp = totalAssetsUp.mulDiv(1e18 - targetAllocationWad, 1e18, MathUpgradeable.Rounding.Up); | ||
| uint256 idleTargetDown = totalAssetsDown.mulDiv(1e18 - targetAllocationWad, 1e18, MathUpgradeable.Rounding.Down); | ||
| uint256 idleBalance = IERC20(asset()).balanceOf(address(this)); | ||
|
|
||
| if (idleTargetDown <= idleBalance && idleBalance <= idleTargetUp) { | ||
| return; | ||
| } | ||
|
|
||
| if (idleBalance < idleTargetDown) { | ||
| // we need to withdraw from subvault | ||
| uint256 assetsToWithdraw = idleTargetDown - idleBalance; | ||
| subVault.withdraw(assetsToWithdraw, address(this), address(this)); | ||
| } | ||
| else { | ||
| // we need to deposit into subvault | ||
| uint256 assetsToDeposit = idleBalance - idleTargetUp; | ||
| subVault.deposit(assetsToDeposit, address(this)); | ||
| } | ||
| } |
Check warning
Code scanning / Slither
Unused return Medium
| function _rebalance() internal { | ||
| uint256 totalAssetsUp = _totalAssetsLessProfit(MathUpgradeable.Rounding.Up); | ||
| uint256 totalAssetsDown = _totalAssetsLessProfit(MathUpgradeable.Rounding.Down); | ||
| uint256 idleTargetUp = totalAssetsUp.mulDiv(1e18 - targetAllocationWad, 1e18, MathUpgradeable.Rounding.Up); | ||
| uint256 idleTargetDown = totalAssetsDown.mulDiv(1e18 - targetAllocationWad, 1e18, MathUpgradeable.Rounding.Down); | ||
| uint256 idleBalance = IERC20(asset()).balanceOf(address(this)); | ||
|
|
||
| if (idleTargetDown <= idleBalance && idleBalance <= idleTargetUp) { | ||
| return; | ||
| } | ||
|
|
||
| if (idleBalance < idleTargetDown) { | ||
| // we need to withdraw from subvault | ||
| uint256 assetsToWithdraw = idleTargetDown - idleBalance; | ||
| subVault.withdraw(assetsToWithdraw, address(this), address(this)); | ||
| } | ||
| else { | ||
| // we need to deposit into subvault | ||
| uint256 assetsToDeposit = idleBalance - idleTargetUp; | ||
| subVault.deposit(assetsToDeposit, address(this)); | ||
| } | ||
| } |
Check warning
Code scanning / Slither
Unused return Medium
| /// @notice Extra decimals added to the ERC20 decimals of the underlying asset to determine the decimals of the MasterVault | ||
| /// @dev This is done to mitigate the "first depositor" problem described in the OpenZeppelin ERC4626 documentation. | ||
| /// See https://docs.openzeppelin.com/contracts/5.x/erc4626 for more details on the mitigation. | ||
| uint8 public constant EXTRA_DECIMALS = 18; |
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.
inspired by morpho:
DECIMALS_OFFSET = uint8(uint256(18).zeroFloorSub(IERC20Metadata(_asset).decimals()));
where zeroFloorSub is max(0, x-y) = max(0, 18-asset.decimals)
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 don't really want an offset of 0, because then the mitigation no longer works
* tests: core functionality * handle total supply or total asset zero values * fix mint method when zero totalAssets or totalSupply * perf fees tests and fixes * remove ds store * MasterVault reserve (#141) * wip * wip: conversion and deposit/withdraw functions look okay * settargetAllocationWad * deploy with initial vault * use factory in core test setup * slippage tolerance on setTargetAllocationWad * big simplify * WIP: mastervault donation attack mitigation (#142) * wip: dead shares and fix div by zero * doc * add one * initial approval and small refactor * tests * fix PerformanceFeesWithdrawn event * move event * rebalancing does not count profit * distribute fees before disabling * fix maxMint * fix outdated comment * remove unused errors * nonReentrant * handle max in maxMint * sanity check we have no sub shares when switching * init reentrancy guard * rebalance after setting target * update docs * update outdated comment * fix nonReentrant modifier placement * remove testFoo * fix tests --------- Co-authored-by: Henry <[email protected]>
testing in progress, but the implementation should be solid unless testing uncovers issues