Skip to content

Conversation

@waelsy123
Copy link
Contributor

This isolate the implementation for the master vault and its related factory and subvault.

@cla-bot cla-bot bot added the cla-signed label Sep 29, 2025
Comment on lines 201 to 213
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

Comment on lines 218 to 254
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

@waelsy123 waelsy123 force-pushed the wa/master-vault-isolated branch from 590a713 to fc89964 Compare September 30, 2025 13:43
@godzillaba godzillaba marked this pull request as draft October 1, 2025 13:33
if (address(oldSubVault) == address(0)) revert NoExistingSubVault();

uint256 _totalSupply = totalSupply();
uint256 assetReceived = oldSubVault.withdraw(oldSubVault.maxWithdraw(address(this)), address(this), address(this));
Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function withdraw(uint256 assets, address receiver, address owner) public returns (uint256 shares)

note ERC4626.withdraw returns share withdrawn not assetReceived

Comment on lines +191 to +193
function withdrawPerformanceFees() external onlyRole(FEE_MANAGER_ROLE) {
if (!enablePerformanceFee) revert PerformanceFeeDisabled();
if (beneficiary == address(0)) revert BeneficiaryNotSet();

uint256 totalProfits = totalProfit();
if (totalProfits > 0) {
IERC4626 _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

@waelsy123 waelsy123 force-pushed the wa/master-vault-isolated branch from fdd512f to e9d49e5 Compare November 6, 2025 14:11
waelsy123 and others added 6 commits November 7, 2025 09:35
* feat: access control roles

* remove OwnableUpgradeable

* add note about permissionless fee manager

---------

Co-authored-by: Henry <[email protected]>
MasterVault: remove stored subvault exchange rate
Comment on lines +101 to +115
function setSubVault(IERC4626 _subVault, uint256 minSubVaultExchRateWad) external onlyRole(VAULT_MANAGER_ROLE) {
IERC20 underlyingAsset = IERC20(asset());
if (address(subVault) != address(0)) revert SubVaultAlreadySet();
if (address(_subVault.asset()) != address(underlyingAsset)) revert SubVaultAssetMismatch();

subVault = _subVault;

IERC20(asset()).safeApprove(address(_subVault), type(uint256).max);
_subVault.deposit(underlyingAsset.balanceOf(address(this)), address(this));

uint256 subVaultExchRateWad = _subVault.balanceOf(address(this)).mulDiv(1e18, totalSupply(), MathUpgradeable.Rounding.Down);
if (subVaultExchRateWad < minSubVaultExchRateWad) revert NewSubVaultExchangeRateTooLow();

emit SubvaultChanged(address(0), address(_subVault));
}

Check warning

Code scanning / Slither

Unused return Medium

Comment on lines +119 to +132
function revokeSubVault(uint256 minAssetExchRateWad) external onlyRole(VAULT_MANAGER_ROLE) {
IERC4626 oldSubVault = subVault;
if (address(oldSubVault) == address(0)) revert NoExistingSubVault();

subVault = IERC4626(address(0));

oldSubVault.redeem(oldSubVault.balanceOf(address(this)), address(this), address(this));
IERC20(asset()).safeApprove(address(oldSubVault), 0);

uint256 assetExchRateWad = IERC20(asset()).balanceOf(address(this)).mulDiv(1e18, totalSupply(), MathUpgradeable.Rounding.Down);
if (assetExchRateWad < minAssetExchRateWad) revert SubVaultExchangeRateTooLow();

emit SubvaultChanged(address(oldSubVault), address(0));
}

Check warning

Code scanning / Slither

Unused return Medium

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants