Skip to content

Commit bd1dffb

Browse files
authored
Merge pull request #133 from OffchainLabs/ha/mv-remove-stored-exch-rate
MasterVault: remove stored subvault exchange rate
2 parents cd1a25d + 9b4809c commit bd1dffb

File tree

2 files changed

+17
-33
lines changed

2 files changed

+17
-33
lines changed

contracts/tokenbridge/libraries/vault/MasterVault.sol

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
2626
error TooManyAssetsDeposited();
2727
error TooFewAssetsReceived();
2828
error SubVaultAlreadySet();
29-
error SubVaultCannotBeZeroAddress();
30-
error MustHaveSupplyBeforeSettingSubVault();
3129
error SubVaultAssetMismatch();
3230
error SubVaultExchangeRateTooLow();
3331
error NoExistingSubVault();
34-
error MustHaveSupplyBeforeSwitchingSubVault();
3532
error NewSubVaultExchangeRateTooLow();
3633
error BeneficiaryNotSet();
3734
error PerformanceFeeDisabled();
@@ -42,12 +39,6 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
4239
// we may need a minimum asset or master share amount when setting subvaults (bc of exchange rate calc)
4340
IERC4626 public subVault;
4441

45-
// how many subVault shares one MV2 share can be redeemed for
46-
// initially 1 to 1
47-
// constant per subvault
48-
// changes when subvault is set
49-
uint256 public subVaultExchRateWad;
50-
5142
// note: the performance fee can be avoided if the underlying strategy can be sandwiched (eg ETH to wstETH dex swap)
5243
// maybe a simpler and more robust implementation would be for the owner to adjust the subVaultExchRateWad directly
5344
// this would also avoid the need for totalPrincipal tracking
@@ -77,8 +68,6 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
7768
_grantRole(VAULT_MANAGER_ROLE, _owner);
7869
_grantRole(FEE_MANAGER_ROLE, _owner); // todo: consider permissionless by default
7970
_grantRole(PAUSER_ROLE, _owner);
80-
81-
subVaultExchRateWad = 1e18;
8271
}
8372

8473

@@ -110,7 +99,6 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
11099
/// @param _subVault The subvault to set. Must be an ERC4626 vault with the same asset as this MasterVault.
111100
/// @param minSubVaultExchRateWad Minimum acceptable ratio (times 1e18) of new subvault shares to outstanding MasterVault shares after deposit.
112101
function setSubVault(IERC4626 _subVault, uint256 minSubVaultExchRateWad) external onlyRole(VAULT_MANAGER_ROLE) {
113-
if (address(subVault) != address(0)) revert SubVaultAlreadySet();
114102
_setSubVault(_subVault, minSubVaultExchRateWad);
115103
}
116104

@@ -121,18 +109,17 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
121109
}
122110

123111
function _setSubVault(IERC4626 _subVault, uint256 minSubVaultExchRateWad) internal {
124-
if (address(_subVault) == address(0)) revert SubVaultCannotBeZeroAddress();
125-
if (totalSupply() == 0) revert MustHaveSupplyBeforeSettingSubVault();
126-
if (address(_subVault.asset()) != address(asset())) revert SubVaultAssetMismatch();
127-
128-
IERC20(asset()).safeApprove(address(_subVault), type(uint256).max);
129-
uint256 subShares = _subVault.deposit(totalAssets(), address(this));
112+
IERC20 underlyingAsset = IERC20(asset());
113+
if (address(subVault) != address(0)) revert SubVaultAlreadySet();
114+
if (address(_subVault.asset()) != address(underlyingAsset)) revert SubVaultAssetMismatch();
130115

131116
subVault = _subVault;
132117

133-
uint256 _subVaultExchRateWad = subShares.mulDiv(1e18, totalAssets(), MathUpgradeable.Rounding.Down);
134-
if (_subVaultExchRateWad < minSubVaultExchRateWad) revert SubVaultExchangeRateTooLow();
135-
subVaultExchRateWad = _subVaultExchRateWad;
118+
IERC20(asset()).safeApprove(address(_subVault), type(uint256).max);
119+
_subVault.deposit(underlyingAsset.balanceOf(address(this)), address(this));
120+
121+
uint256 subVaultExchRateWad = _subVault.balanceOf(address(this)).mulDiv(1e18, totalSupply(), MathUpgradeable.Rounding.Down);
122+
if (subVaultExchRateWad < minSubVaultExchRateWad) revert NewSubVaultExchangeRateTooLow();
136123

137124
emit SubvaultChanged(address(0), address(_subVault));
138125
}
@@ -141,14 +128,13 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
141128
IERC4626 oldSubVault = subVault;
142129
if (address(oldSubVault) == address(0)) revert NoExistingSubVault();
143130

144-
uint256 _totalSupply = totalSupply();
145-
uint256 assetReceived = oldSubVault.withdraw(oldSubVault.maxWithdraw(address(this)), address(this), address(this));
146-
uint256 effectiveAssetExchRateWad = assetReceived.mulDiv(1e18, _totalSupply, MathUpgradeable.Rounding.Down);
147-
if (effectiveAssetExchRateWad < minAssetExchRateWad) revert TooFewAssetsReceived();
131+
subVault = IERC4626(address(0));
148132

133+
oldSubVault.redeem(oldSubVault.balanceOf(address(this)), address(this), address(this));
149134
IERC20(asset()).safeApprove(address(oldSubVault), 0);
150-
subVault = IERC4626(address(0));
151-
subVaultExchRateWad = 1e18;
135+
136+
uint256 assetExchRateWad = IERC20(asset()).balanceOf(address(this)).mulDiv(1e18, totalSupply(), MathUpgradeable.Rounding.Down);
137+
if (assetExchRateWad < minAssetExchRateWad) revert SubVaultExchangeRateTooLow();
152138

153139
emit SubvaultChanged(address(oldSubVault), address(0));
154140
}
@@ -166,11 +152,13 @@ contract MasterVault is Initializable, ERC4626Upgradeable, AccessControlUpgradea
166152
}
167153

168154
function masterSharesToSubShares(uint256 masterShares, MathUpgradeable.Rounding rounding) public view returns (uint256) {
169-
return masterShares.mulDiv(subVaultExchRateWad, 1e18, rounding);
155+
// masterShares * totalSubVaultShares / totalMasterShares
156+
return masterShares.mulDiv(subVault.balanceOf(address(this)), totalSupply(), rounding);
170157
}
171158

172159
function subSharesToMasterShares(uint256 subShares, MathUpgradeable.Rounding rounding) public view returns (uint256) {
173-
return subShares.mulDiv(1e18, subVaultExchRateWad, rounding);
160+
// subShares * totalMasterShares / totalSubVaultShares
161+
return subShares.mulDiv(totalSupply(), subVault.balanceOf(address(this)), rounding);
174162
}
175163

176164
/// @notice Toggle performance fee collection on/off

test-foundry/libraries/vault/MasterVault.t.sol

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ contract MasterVaultTest is Test {
122122
vault.setSubVault(subVault, minSubVaultExchRateWad);
123123

124124
assertEq(address(vault.subVault()), address(subVault), "SubVault should be set");
125-
assertEq(vault.subVaultExchRateWad(), 1e18, "Exchange rate should be 1:1 initially");
126125
assertEq(vault.totalAssets(), depositAmount, "Total assets should remain the same");
127126
assertEq(subVault.balanceOf(address(vault)), depositAmount, "SubVault should have received assets");
128127
}
@@ -164,7 +163,6 @@ contract MasterVaultTest is Test {
164163
vault.switchSubVault(newSubVault, minAssetExchRateWad, minNewSubVaultExchRateWad);
165164

166165
assertEq(address(vault.subVault()), address(newSubVault), "New subvault should be set");
167-
assertEq(vault.subVaultExchRateWad(), 1e18, "Exchange rate should remain 1:1");
168166
assertEq(vault.totalAssets(), depositAmount, "Total assets should remain the same");
169167
assertEq(oldSubVault.balanceOf(address(vault)), 0, "Old subvault should have no assets");
170168
assertEq(newSubVault.balanceOf(address(vault)), depositAmount, "New subvault should have received assets");
@@ -188,7 +186,6 @@ contract MasterVaultTest is Test {
188186

189187
assertEq(address(vault.subVault()), address(subVault), "SubVault should be set");
190188
assertEq(subVault.balanceOf(address(vault)), depositAmount, "SubVault should have assets");
191-
assertEq(vault.subVaultExchRateWad(), 1e18, "Exchange rate should be 1:1");
192189

193190
uint256 minAssetExchRateWad = 1e18;
194191

@@ -198,7 +195,6 @@ contract MasterVaultTest is Test {
198195
vault.revokeSubVault(minAssetExchRateWad);
199196

200197
assertEq(address(vault.subVault()), address(0), "SubVault should be revoked");
201-
assertEq(vault.subVaultExchRateWad(), 1e18, "Exchange rate should reset to 1:1");
202198
assertEq(vault.totalAssets(), depositAmount, "Total assets should remain the same");
203199
assertEq(subVault.balanceOf(address(vault)), 0, "SubVault should have no assets");
204200
assertEq(token.balanceOf(address(vault)), depositAmount, "MasterVault should have assets directly");

0 commit comments

Comments
 (0)