-
Notifications
You must be signed in to change notification settings - Fork 156
Make master vault upgradable #127
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: wa/master-vault-isolated
Are you sure you want to change the base?
Conversation
b9d6f0d
to
7922536
Compare
if (token == address(0)) { | ||
revert ZeroAddress(); | ||
} | ||
if (address(beaconProxyFactory) == 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.
Could check the initialized
variable?
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.
will add extra condition to cover that
beacon = new UpgradeableBeacon(address(masterVaultImplementation)); | ||
beaconProxyFactory = new BeaconProxyFactory(); | ||
beaconProxyFactory.initialize(address(beacon)); | ||
beacon.transferOwnership(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.
Isnt this contract already the owner? Should this be beacon.transferOwnership(_owner);
?
I guess we should also add a test to show the beacon can be upgraded
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.
good catch yes that should be assigned to the owner
_transferOwnership(_owner); | ||
|
||
MasterVault masterVaultImplementation = new MasterVault(); | ||
beacon = new UpgradeableBeacon(address(masterVaultImplementation)); |
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 you think we should deploy these with create2? Or is create fine?
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.
I don't have strong opinion on it but my philosophy is to keep simple if there is no need for something more
); | ||
|
||
vault = Create2.deploy(0, bytes32(0), bytecode); | ||
MasterVault(vault).vaultInit(IERC20(token), name, symbol, 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 a bunch of onlyOwner functions on the master vault which won't be callable - we only have setSubVault. Is that expected?
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.
yes that's expected. all of the onlyOwner calls are around:
- switching between sub vaults.
- managing perf fee (set, withdraw)
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.
if we only want setSubVault to be callable we should remove the othere external owner functions
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.
it depends on whether we want to allow owner to switch or revoke already assigned subvault. if this feature isn't required then we can drop switchSubvault
and revokeSubvault
methods.
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.
okay, let's table this for now but we might want to just give the upgradeexecutor ownership of the vaults themselves. that way we don't need to forward calls through the factory
maybe we can include that change in our inevitable roles based access control PR
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.
agreed - thanks for flagging that, will keep note of it to handle this in the coming PRs related to ownerships
if (address(_subVault) != address(0)) { | ||
_subVault.deposit(assets, address(this)); | ||
uint256 subShares = _subVault.deposit(assets, address(this)); | ||
if (subShares == 0) revert NoSubvaultShares(); |
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.
what's the rationale behind this check?
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.
it's just to satisfy slither as there is a rule to not have unused variables / unused returns
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.
ok i think we can ignore slither
if (address(_subVault) != address(0)) { | ||
_subVault.withdraw(assets, address(this), address(this)); | ||
uint256 sharesBurned = _subVault.withdraw(assets, address(this), address(this)); | ||
if (sharesBurned == 0) revert NoSharesBurned(); |
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.
@godzillaba same here
|
||
__ERC20_init(_name, _symbol); | ||
__ERC4626_init(IERC20Upgradeable(address(_asset))); | ||
__Ownable_init(); |
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.
_transferOwnership suffices
__Ownable_init(); |
uint256 _totalAssets = totalAssets(); | ||
uint256 _totalSupply = totalSupply(); | ||
|
||
subVault = _subVault; | ||
|
||
IERC20(asset()).safeApprove(address(_subVault), type(uint256).max); | ||
uint256 subShares = _subVault.deposit(totalAssets(), address(this)); | ||
uint256 subShares = _subVault.deposit(_totalAssets, address(this)); | ||
|
||
uint256 _subVaultExchRateWad = subShares.mulDiv(1e18, totalSupply(), Math.Rounding.Down); | ||
uint256 _subVaultExchRateWad = subShares.mulDiv(1e18, _totalSupply, MathUpgradeable.Rounding.Down); | ||
if (_subVaultExchRateWad < minSubVaultExchRateWad) revert SubVaultExchangeRateTooLow(); | ||
subVaultExchRateWad = _subVaultExchRateWad; | ||
|
||
subVault = _subVault; | ||
|
||
emit SubvaultChanged(address(0), address(_subVault)); |
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 we leave this alone except for changing Math
to MathUpgradeable
? what is the purpose of the changes?
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.
this include a bug for a race condition which cause this line to fail:
uint256 _subVaultExchRateWad = subShares.mulDiv(1e18, totalSupply(), Math.Rounding.Down);
that's not related to the upgradability but the issue is that this line will always fail with error "division by zero" because of totalSupply() returns zero after (master vault deposited all funds into new subvault
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.
I think an easy fix would be to move subVault = _subVault;
in between deposit and _subVaultExchRateWad calculation. just pushed a fix for that.
uint256 maxWithdrawAmount = oldSubVault.maxWithdraw(address(this)); | ||
|
||
IERC20(asset()).safeApprove(address(oldSubVault), 0); | ||
subVault = ERC4626(address(0)); | ||
subVault = IERC4626(address(0)); | ||
subVaultExchRateWad = 1e18; | ||
|
||
uint256 assetReceived = oldSubVault.withdraw(maxWithdrawAmount, address(this), address(this)); | ||
IERC20(asset()).safeApprove(address(oldSubVault), 0); | ||
|
||
uint256 effectiveAssetExchRateWad = assetReceived.mulDiv(1e18, _totalSupply, MathUpgradeable.Rounding.Down); | ||
if (effectiveAssetExchRateWad < minAssetExchRateWad) revert TooFewAssetsReceived(); | ||
|
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.
what is the purpose of the changes here?
uint256 sharesRedeemed = _subVault.withdraw(totalProfits, address(this), address(this)); | ||
if (sharesRedeemed == 0) revert NoSharesRedeemed(); |
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.
let's leave this as is since it's not relevant to upgradeability
uint256 sharesRedeemed = _subVault.withdraw(totalProfits, address(this), address(this)); | |
if (sharesRedeemed == 0) revert NoSharesRedeemed(); | |
_subVault.withdraw(totalProfits, address(this), address(this)); |
error ZeroAddress(); | ||
error BeaconNotDeployed(); | ||
|
||
UpgradeableBeacon public beacon; |
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.
i'm not sure we need to store the beacon since the factory doesn't use it
event BeneficiaryUpdated(address indexed oldBeneficiary, address indexed newBeneficiary); | ||
|
||
constructor(IERC20 _asset, string memory _name, string memory _symbol) ERC20(_name, _symbol) ERC4626(_asset) Ownable() {} | ||
function vaultInit(IERC20 _asset, string memory _name, string memory _symbol, address _owner) external initializer { |
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.
nit, for convention
function vaultInit(IERC20 _asset, string memory _name, string memory _symbol, address _owner) external initializer { | |
function initialize(IERC20 _asset, string memory _name, string memory _symbol, address _owner) external initializer { |
); | ||
|
||
vault = Create2.deploy(0, bytes32(0), bytecode); | ||
MasterVault(vault).vaultInit(IERC20(token), name, symbol, 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.
okay, let's table this for now but we might want to just give the upgradeexecutor ownership of the vaults themselves. that way we don't need to forward calls through the factory
maybe we can include that change in our inevitable roles based access control PR
Sherlock AI
|
This convert MasterVault to be beacon upgradable