Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 46 additions & 11 deletions contracts/tokenbridge/libraries/vault/MasterVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
event SubvaultChanged(address indexed oldSubvault, address indexed newSubvault);
event PerformanceFeeToggled(bool enabled);
event BeneficiaryUpdated(address indexed oldBeneficiary, address indexed newBeneficiary);
event PerformanceFeesWithdrawn(address indexed beneficiary, uint256 amount);

function initialize(IERC20 _asset, string memory _name, string memory _symbol, address _owner) external initializer {
if (address(_asset) == address(0)) revert InvalidAsset();
Expand All @@ -79,15 +80,26 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
_grantRole(DEFAULT_ADMIN_ROLE, _owner);
_grantRole(VAULT_MANAGER_ROLE, _owner);
_grantRole(PAUSER_ROLE, _owner);

_pause();
}

function distributePerformanceFee() external whenNotPaused {
if (!enablePerformanceFee) revert PerformanceFeeDisabled();
if (beneficiary == address(0)) {
revert BeneficiaryNotSet();
}
subVault.redeem(totalProfitInSubVaultShares(MathUpgradeable.Rounding.Down), beneficiary, address(this));
// todo emit event

uint256 profit = totalProfit(MathUpgradeable.Rounding.Down);
if (profit == 0) return;

if (address(subVault) != address(0)) {
subVault.redeem(totalProfitInSubVaultShares(MathUpgradeable.Rounding.Down), beneficiary, address(this));
} else {
IERC20(asset()).safeTransfer(beneficiary, profit);
}

emit PerformanceFeesWithdrawn(beneficiary, profit);
}

/// @notice Set a subvault. Can only be called if there is not already a subvault set.
Expand All @@ -103,8 +115,11 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
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();
uint256 supply = totalSupply();
if (supply > 0) {
uint256 subVaultExchRateWad = _subVault.balanceOf(address(this)).mulDiv(1e18, supply, MathUpgradeable.Rounding.Down);
if (subVaultExchRateWad < minSubVaultExchRateWad) revert NewSubVaultExchangeRateTooLow();
}

emit SubvaultChanged(address(0), address(_subVault));
}
Expand All @@ -120,8 +135,11 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
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();
uint256 supply = totalSupply();
if (supply > 0) {
uint256 assetExchRateWad = IERC20(asset()).balanceOf(address(this)).mulDiv(1e18, supply, MathUpgradeable.Rounding.Down);
if (assetExchRateWad < minAssetExchRateWad) revert SubVaultExchangeRateTooLow();
}

emit SubvaultChanged(address(oldSubVault), address(0));
}
Expand Down Expand Up @@ -253,9 +271,16 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
* would represent an infinite amount of shares.
*/
function _convertToShares(uint256 assets, MathUpgradeable.Rounding rounding) internal view virtual override returns (uint256 shares) {
uint256 supply = totalSupply();

if (address(subVault) == address(0)) {
uint256 effectiveTotalAssets = enablePerformanceFee ? _min(totalAssets(), totalPrincipal) : totalAssets();
return totalSupply().mulDiv(assets, effectiveTotalAssets, rounding);

if (supply == 0 || effectiveTotalAssets == 0) {
return assets;
}

return supply.mulDiv(assets, effectiveTotalAssets, rounding);
}

uint256 totalSubShares = subVault.balanceOf(address(this));
Expand All @@ -265,20 +290,30 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
// and we are subtracting profit from it, we should use the same rounding direction for profit
totalSubShares -= totalProfitInSubVaultShares(_flipRounding(rounding));
}

uint256 subShares = _assetsToSubVaultShares(assets, rounding);

return totalSupply().mulDiv(subShares, totalSubShares, rounding);
if (supply == 0 || totalSubShares == 0) {
return subShares;
}

return supply.mulDiv(subShares, totalSubShares, rounding);
}

/**
* @dev Internal conversion function (from shares to assets) with support for rounding direction.
*/
function _convertToAssets(uint256 shares, MathUpgradeable.Rounding rounding) internal view virtual override returns (uint256 assets) {
uint256 _totalSupply = totalSupply();

if(_totalSupply == 0) {
return shares;
}

// if we have no subvault, we just do normal pro-rata calculation
if (address(subVault) == address(0)) {
uint256 effectiveTotalAssets = enablePerformanceFee ? _min(totalAssets(), totalPrincipal) : totalAssets();
return effectiveTotalAssets.mulDiv(shares, totalSupply(), rounding);
return effectiveTotalAssets.mulDiv(shares, _totalSupply, rounding);
}

uint256 totalSubShares = subVault.balanceOf(address(this));
Expand All @@ -290,7 +325,7 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
}

// totalSubShares * shares / totalMasterShares
uint256 subShares = totalSubShares.mulDiv(shares, totalSupply(), rounding);
uint256 subShares = totalSubShares.mulDiv(shares, _totalSupply, rounding);

return _subVaultSharesToAssets(subShares, rounding);
}
Expand Down
4 changes: 4 additions & 0 deletions contracts/tokenbridge/test/MockSubVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@ contract MockSubVault is ERC4626 {
function totalAssets() public view override returns (uint256) {
return IERC20(asset()).balanceOf(address(this));
}

function adminMint(address to, uint256 amount) external {
_mint(to, amount);
}
}
4 changes: 4 additions & 0 deletions contracts/tokenbridge/test/TestERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ contract TestERC20 is aeERC20 {
function mint() external {
_mint(msg.sender, 50000000);
}

function mint(uint256 amount) external {
_mint(msg.sender, amount);
}
}

// test token code inspired from maker
Expand Down
Loading
Loading