From a191878e64c34831ec05434937df4f0dc79da632 Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Tue, 29 Aug 2023 23:00:45 -0500 Subject: [PATCH 1/6] fix(liquidate): liquidate even if maxMint reached --- lib/pt-v5-liquidator-interfaces | 2 +- src/Vault.sol | 59 ++++++++++++++---- test/fuzz/Vault.t.sol | 26 ++++---- test/unit/Vault/Deposit.feature | 4 +- test/unit/Vault/Deposit.t.sol | 1 + test/unit/Vault/DepositBrokenToken.t.sol | 1 + test/unit/Vault/Liquidate.feature | 2 +- test/unit/Vault/Liquidate.t.sol | 79 ++++++++++++++++++++++-- 8 files changed, 140 insertions(+), 34 deletions(-) diff --git a/lib/pt-v5-liquidator-interfaces b/lib/pt-v5-liquidator-interfaces index da1ee9f..13bda46 160000 --- a/lib/pt-v5-liquidator-interfaces +++ b/lib/pt-v5-liquidator-interfaces @@ -1 +1 @@ -Subproject commit da1ee9f243057246b776437a76b450731cb9c618 +Subproject commit 13bda46baedf2ffd1c87ac5a0fd4a7229e0884f2 diff --git a/src/Vault.sol b/src/Vault.sol index 543b2a3..2a9c727 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -114,6 +114,13 @@ error LiquidationAmountOutZero(); */ error LiquidationAmountOutGTYield(uint256 amountOut, uint256 availableYield); +/** + * @notice Emitted during the liquidation process if the amount out is greater than the maximum amount of Vault shares mintable. + * @param amountOut The amount out + * @param vaultMaxMint The maxmimum amount of Vault shares mintable + */ +error LiquidationAmountOutGTVaultMaxMint(uint256 amountOut, uint256 vaultMaxMint); + /// @notice Emitted when the Vault is under-collateralized. error VaultUnderCollateralized(); @@ -149,10 +156,17 @@ error YieldFeeGTAvailableShares(uint256 shares, uint256 yieldFeeShares); /** * @notice Emitted when the minted yield exceeds the amount of available yield in the YieldVault. - * @param assets The amount of yield assets requested + * @param shares The amount of yield shares to mint * @param availableYield The amount of yield available */ -error YieldFeeGTAvailableYield(uint256 assets, uint256 availableYield); +error YieldFeeGTAvailableYield(uint256 shares, uint256 availableYield); + +/** + * @notice Emitted when the minted yield exceeds the maximum amount of Vault shares mintable. + * @param shares The amount of yield shares to mint + * @param vaultMaxMint The maxmimum amount of Vault shares mintable + */ +error YieldFeeGTVaultMaxMint(uint256 shares, uint256 vaultMaxMint); /// @notice Emitted when the Liquidation Pair being set is the zero address. error LPZeroAddress(); @@ -485,7 +499,7 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { /** * @inheritdoc IERC4626 - * @dev We use type(uint96).max cause this is the type used to store balances in TwabController. + * @dev We use type(uint112).max cause this is the type used to store balances in TwabController. */ function maxDeposit(address) public view virtual override returns (uint256) { uint256 _depositedAssets = _totalSupply(); @@ -503,7 +517,7 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { /** * @inheritdoc IERC4626 - * @dev We use type(uint96).max cause this is the type used to store balances in TwabController. + * @dev We use type(uint112).max cause this is the type used to store balances in TwabController. */ function maxMint(address) public view virtual override returns (uint256) { uint256 _depositedAssets = _totalSupply(); @@ -704,6 +718,9 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_shares > _availableYield) revert YieldFeeGTAvailableYield(_shares, _availableYield); if (_shares > _yieldFeeShares) revert YieldFeeGTAvailableShares(_shares, _yieldFeeShares); + uint256 _vaultMaxMint = _getVaultMaxMint(_depositedAssets); + if (_shares > _vaultMaxMint) revert YieldFeeGTVaultMaxMint(_shares, _vaultMaxMint); + _yieldFeeShares -= _shares; _mint(_yieldFeeRecipient, _shares); @@ -740,6 +757,10 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_amountOut > _liquidatableYield) revert LiquidationAmountOutGTYield(_amountOut, _liquidatableYield); + uint256 _vaultMaxMint = _getVaultMaxMint(_totalSupply()); + if (_amountOut > _vaultMaxMint) + revert LiquidationAmountOutGTVaultMaxMint(_amountOut, _vaultMaxMint); + // Distributes the specified yield fee percentage. // For instance, with a yield fee percentage of 20% and 8e18 Vault shares being liquidated, // this calculation assigns 2e18 Vault shares to the yield fee recipient. @@ -1155,6 +1176,9 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_withdrawableAssetsAfter < _expectedWithdrawableAssets) revert YVWithdrawableAssetsLTExpected(_withdrawableAssetsAfter, _expectedWithdrawableAssets); + if (_assets > maxMint(_receiver)) + revert MintMoreThanMax(_receiver, _assets, maxMint(_receiver)); + _mint(_receiver, _assets); emit Deposit(_caller, _receiver, _assets, _assets); @@ -1361,27 +1385,27 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { /** * @notice Creates `_shares` tokens and assigns them to `_receiver`, increasing the total supply. - * @param _receiver Address that will receive the minted shares - * @param _shares Shares to mint * @dev Emits a {Transfer} event with `from` set to the zero address. * @dev `_receiver` cannot be the zero address. + * @param _receiver Address that will receive the minted shares + * @param _shares Shares to mint */ function _mint(address _receiver, uint256 _shares) internal virtual override { if (_shares > maxMint(_receiver)) revert MintMoreThanMax(_receiver, _shares, maxMint(_receiver)); - _twabController.mint(_receiver, SafeCast.toUint112(_shares)); + _twabController.mint(_receiver, SafeCast.toUint96(_shares)); emit Transfer(address(0), _receiver, _shares); } /** * @notice Destroys `_shares` tokens from `_owner`, reducing the total supply. - * @param _owner The owner of the shares - * @param _shares The shares to burn * @dev Emits a {Transfer} event with `to` set to the zero address. * @dev `_owner` cannot be the zero address. * @dev `_owner` must have at least `_shares` tokens. + * @param _owner The owner of the shares + * @param _shares The shares to burn */ function _burn(address _owner, uint256 _shares) internal virtual override { _twabController.burn(_owner, SafeCast.toUint112(_shares)); @@ -1391,12 +1415,12 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { /** * @notice Updates `_from` and `_to` TWAB balance for a transfer. - * @param _from Address to transfer from - * @param _to Address to transfer to - * @param _shares Shares to transfer * @dev `_from` cannot be the zero address. * @dev `_to` cannot be the zero address. * @dev `_from` must have a balance of at least `_shares`. + * @param _from Address to transfer from + * @param _to Address to transfer to + * @param _shares Shares to transfer */ function _transfer(address _from, address _to, uint256 _shares) internal virtual override { _twabController.transfer(_from, _to, SafeCast.toUint112(_shares)); @@ -1445,6 +1469,17 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { return _withdrawableAssets >= _depositedAssets; } + /** + * @notice Returns the maximum amount of Vault shares that can be minted. + * @dev It would overflow if minting more shares than the TwabController uint112 max type. + * @param _depositedAssets Assets deposited into the YieldVault + */ + function _getVaultMaxMint(uint256 _depositedAssets) internal pure returns (uint256) { + return type(uint112).max - _depositedAssets; + } + + /* ============ Modifiers ============ */ + /// @notice Require reverting if the vault is under-collateralized. modifier onlyVaultCollateralized() { if (!_isVaultCollateralized(_totalSupply(), _totalAssets())) revert VaultUnderCollateralized(); diff --git a/test/fuzz/Vault.t.sol b/test/fuzz/Vault.t.sol index c1d9eff..a5ca899 100644 --- a/test/fuzz/Vault.t.sol +++ b/test/fuzz/Vault.t.sol @@ -335,8 +335,8 @@ contract VaultFuzzTest is ERC4626Test, Helpers { } function test_liquidate(Init memory init, uint256 shares) public virtual { - // We set the higher bound to uint88 to avoid overflowing above uint96 - init.yield = int(bound(shares, 10e18, type(uint88).max)); + // We set the higher bound to uint104 to avoid overflowing above uint112 + init.yield = int(bound(shares, 10e18, type(uint104).max)); setUpVault(init); vault.setLiquidationPair(ILiquidationPair(address(liquidationPair))); @@ -367,7 +367,7 @@ contract VaultFuzzTest is ERC4626Test, Helpers { } } - // We set the higher bound to uint88 to avoid overflowing above uint96 + // We set the higher bound to uint104 to avoid overflowing above uint112 function setUpVault(Init memory init) public virtual override { for (uint i = 0; i < N; i++) { init.user[i] = makeAddr(Strings.toString(i)); @@ -375,7 +375,7 @@ contract VaultFuzzTest is ERC4626Test, Helpers { vm.assume(_isEOA(user)); - uint shares = bound(init.share[i], 0, type(uint88).max); + uint shares = bound(init.share[i], 0, type(uint104).max); try IMockERC20(_underlying_).mint(user, shares) {} catch { vm.assume(false); } @@ -387,7 +387,7 @@ contract VaultFuzzTest is ERC4626Test, Helpers { vm.assume(false); } - uint assets = bound(init.asset[i], 0, type(uint88).max); + uint assets = bound(init.asset[i], 0, type(uint104).max); try IMockERC20(_underlying_).mint(user, assets) {} catch { vm.assume(false); } @@ -397,22 +397,22 @@ contract VaultFuzzTest is ERC4626Test, Helpers { } function _max_deposit(address from) internal virtual override returns (uint) { - if (_unlimitedAmount) return type(uint88).max; - return uint88(IERC20(_underlying_).balanceOf(from)); + if (_unlimitedAmount) return type(uint104).max; + return uint104(IERC20(_underlying_).balanceOf(from)); } function _max_mint(address from) internal virtual override returns (uint) { - if (_unlimitedAmount) return type(uint96).max; - return uint88(vault_convertToShares(IERC20(_underlying_).balanceOf(from))); + if (_unlimitedAmount) return type(uint112).max; + return uint104(vault_convertToShares(IERC20(_underlying_).balanceOf(from))); } function _max_withdraw(address from) internal virtual override returns (uint) { - if (_unlimitedAmount) return type(uint88).max; - return uint88(vault_convertToAssets(IERC20(_vault_).balanceOf(from))); + if (_unlimitedAmount) return type(uint104).max; + return uint104(vault_convertToAssets(IERC20(_vault_).balanceOf(from))); } function _max_redeem(address from) internal virtual override returns (uint) { - if (_unlimitedAmount) return type(uint88).max; - return uint88(IERC20(_vault_).balanceOf(from)); + if (_unlimitedAmount) return type(uint104).max; + return uint104(IERC20(_vault_).balanceOf(from)); } } diff --git a/test/unit/Vault/Deposit.feature b/test/unit/Vault/Deposit.feature index 37daf4d..2a146c3 100644 --- a/test/unit/Vault/Deposit.feature +++ b/test/unit/Vault/Deposit.feature @@ -57,7 +57,7 @@ Feature: Deposit # Deposit - Errors Scenario: Alice deposits into the Vault Given Alice owns 0 Vault shares - When Alice deposits type(uint96).max + 1 underlying assets + When Alice deposits type(uint112).max + 1 underlying assets Then the transaction reverts with the custom error `DepositMoreThanMax` Scenario: Alice deposits into the Vault @@ -115,7 +115,7 @@ Feature: Deposit # Mint - Errors Scenario: Alice mints shares from the Vault Given Alice owns 0 Vault shares - When Alice mints type(uint96).max + 1 shares + When Alice mints type(uint112).max + 1 shares Then the transaction reverts with the custom error MintMoreThanMax Scenario: Alice mints shares from the Vault diff --git a/test/unit/Vault/Deposit.t.sol b/test/unit/Vault/Deposit.t.sol index 52d973c..acb3fd8 100644 --- a/test/unit/Vault/Deposit.t.sol +++ b/test/unit/Vault/Deposit.t.sol @@ -193,6 +193,7 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { vm.expectRevert( abi.encodeWithSelector(DepositMoreThanMax.selector, alice, _amount, type(uint112).max) ); + vault.deposit(_amount, alice); vm.stopPrank(); diff --git a/test/unit/Vault/DepositBrokenToken.t.sol b/test/unit/Vault/DepositBrokenToken.t.sol index 4cf50f8..1585233 100644 --- a/test/unit/Vault/DepositBrokenToken.t.sol +++ b/test/unit/Vault/DepositBrokenToken.t.sol @@ -45,6 +45,7 @@ contract VaultDepositBrokenTokenTest is UnitBrokenTokenBaseSetup { vm.expectRevert( abi.encodeWithSelector(DepositMoreThanMax.selector, alice, _amount, type(uint112).max) ); + vault.deposit(_amount, alice); vm.stopPrank(); diff --git a/test/unit/Vault/Liquidate.feature b/test/unit/Vault/Liquidate.feature index 35a4880..6387dc5 100644 --- a/test/unit/Vault/Liquidate.feature +++ b/test/unit/Vault/Liquidate.feature @@ -98,6 +98,6 @@ Feature: Liquidate Then the transaction reverts with the custom error `YieldFeeGTAvailable` Scenario: Bob mints 1e18 yield fee shares - Given Bob owns type(uint96).max Vault shares and 10e18 of yield fee shares have accrued + Given Bob owns type(uint112).max Vault shares and 10e18 of yield fee shares have accrued When Bob mints 1e18 yield fee shares Then the transaction reverts with the custom error `MintMoreThanMax` diff --git a/test/unit/Vault/Liquidate.t.sol b/test/unit/Vault/Liquidate.t.sol index e06f5dc..4a1786e 100644 --- a/test/unit/Vault/Liquidate.t.sol +++ b/test/unit/Vault/Liquidate.t.sol @@ -317,6 +317,76 @@ contract VaultLiquidateTest is UnitBaseSetup { assertEq(vault.yieldFeeShares(), 0); } + function testTransferTokensOut_AndMintFees_YieldVaultMaxMintReached() external { + _setLiquidationPair(); + + vault.setYieldFeePercentage(YIELD_FEE_PERCENTAGE); + vault.setYieldFeeRecipient(bob); + + uint256 _yield = 10e18; + uint256 _amount = type(uint112).max - _yield; + + underlyingAsset.mint(address(this), _amount); + _sponsor(underlyingAsset, vault, _amount); + + _accrueYield(underlyingAsset, yieldVault, _yield); + + vm.startPrank(alice); + + prizeToken.mint(alice, 1000e18); + + uint256 _liquidatedYield = vault.liquidatableBalanceOf(address(vault)); + + // Yield Vault has reached its max mint limit + vm.mockCall( + address(yieldVault), + abi.encodeWithSelector(IERC4626.maxMint.selector, address(vault)), + abi.encode(type(uint104).max) + ); + + // Yield has accrued, so despite the max mint limit reached, + // we should still be able to liquidate prize tokens in exchange of Vault shares + _liquidate(liquidationRouter, liquidationPair, prizeToken, _liquidatedYield, alice); + + assertEq(vault.balanceOf(alice), _liquidatedYield); + + vault.withdraw(_liquidatedYield, alice, alice); + + assertEq(vault.balanceOf(alice), 0); + assertEq(underlyingAsset.balanceOf(alice), _liquidatedYield); + + vm.stopPrank(); + + uint256 _yieldFeeShares = _getYieldFeeShares(_liquidatedYield, YIELD_FEE_PERCENTAGE); + + assertEq(vault.balanceOf(bob), 0); + + // _yieldFeeShares has not been minted yet, so totatSupply is type(uint112).max - _yield + assertEq(vault.totalSupply(), _amount); + assertEq(vault.yieldFeeShares(), _yieldFeeShares); + + vm.expectEmit(); + emit MintYieldFee(address(this), bob, _yieldFeeShares); + + vault.mintYieldFee(_yieldFeeShares); + + assertEq(vault.balanceOf(bob), _yieldFeeShares); + + assertEq(vault.totalSupply(), _amount + _yieldFeeShares); + assertEq(vault.yieldFeeShares(), 0); + + vm.startPrank(bob); + + vault.withdraw(_yieldFeeShares, bob, bob); + + assertEq(vault.totalSupply(), _amount); + + assertEq(vault.balanceOf(bob), 0); + assertEq(underlyingAsset.balanceOf(bob), _yieldFeeShares); + + vm.stopPrank(); + } + /* ============ Liquidate - Errors ============ */ function testTransferTokensOut_YieldVaultUndercollateralized() public { _setLiquidationPair(); @@ -433,7 +503,7 @@ contract VaultLiquidateTest is UnitBaseSetup { vm.stopPrank(); } - function testTransferTokensOut_AmountOutGTMaxMint() public { + function testTransferTokensOut_AmountOutGTUint112() public { _setLiquidationPair(); uint256 _amount = 1000e18; @@ -459,10 +529,9 @@ contract VaultLiquidateTest is UnitBaseSetup { vm.expectRevert( abi.encodeWithSelector( - MintMoreThanMax.selector, - alice, + LiquidationAmountOutGTVaultMaxMint.selector, _amountOut, - type(uint112).max - _amount + type(uint112).max - vault.totalSupply() ) ); @@ -515,7 +584,7 @@ contract VaultLiquidateTest is UnitBaseSetup { vault.deposit(vault.maxDeposit(bob), bob); - vm.expectRevert(abi.encodeWithSelector(MintMoreThanMax.selector, bob, 1e18, 0)); + vm.expectRevert(abi.encodeWithSelector(YieldFeeGTVaultMaxMint.selector, 1e18, 0)); vault.mintYieldFee(1e18); From 987567986faa587f53379a82026df951e1bfb866 Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Wed, 30 Aug 2023 11:44:19 -0500 Subject: [PATCH 2/6] fix(Vault): remove _getVaultMaxMint --- src/Vault.sol | 30 ------------------------------ test/unit/Vault/Liquidate.t.sol | 10 ++-------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index 2a9c727..d78db11 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -114,13 +114,6 @@ error LiquidationAmountOutZero(); */ error LiquidationAmountOutGTYield(uint256 amountOut, uint256 availableYield); -/** - * @notice Emitted during the liquidation process if the amount out is greater than the maximum amount of Vault shares mintable. - * @param amountOut The amount out - * @param vaultMaxMint The maxmimum amount of Vault shares mintable - */ -error LiquidationAmountOutGTVaultMaxMint(uint256 amountOut, uint256 vaultMaxMint); - /// @notice Emitted when the Vault is under-collateralized. error VaultUnderCollateralized(); @@ -161,13 +154,6 @@ error YieldFeeGTAvailableShares(uint256 shares, uint256 yieldFeeShares); */ error YieldFeeGTAvailableYield(uint256 shares, uint256 availableYield); -/** - * @notice Emitted when the minted yield exceeds the maximum amount of Vault shares mintable. - * @param shares The amount of yield shares to mint - * @param vaultMaxMint The maxmimum amount of Vault shares mintable - */ -error YieldFeeGTVaultMaxMint(uint256 shares, uint256 vaultMaxMint); - /// @notice Emitted when the Liquidation Pair being set is the zero address. error LPZeroAddress(); @@ -718,9 +704,6 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_shares > _availableYield) revert YieldFeeGTAvailableYield(_shares, _availableYield); if (_shares > _yieldFeeShares) revert YieldFeeGTAvailableShares(_shares, _yieldFeeShares); - uint256 _vaultMaxMint = _getVaultMaxMint(_depositedAssets); - if (_shares > _vaultMaxMint) revert YieldFeeGTVaultMaxMint(_shares, _vaultMaxMint); - _yieldFeeShares -= _shares; _mint(_yieldFeeRecipient, _shares); @@ -757,10 +740,6 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_amountOut > _liquidatableYield) revert LiquidationAmountOutGTYield(_amountOut, _liquidatableYield); - uint256 _vaultMaxMint = _getVaultMaxMint(_totalSupply()); - if (_amountOut > _vaultMaxMint) - revert LiquidationAmountOutGTVaultMaxMint(_amountOut, _vaultMaxMint); - // Distributes the specified yield fee percentage. // For instance, with a yield fee percentage of 20% and 8e18 Vault shares being liquidated, // this calculation assigns 2e18 Vault shares to the yield fee recipient. @@ -1469,15 +1448,6 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { return _withdrawableAssets >= _depositedAssets; } - /** - * @notice Returns the maximum amount of Vault shares that can be minted. - * @dev It would overflow if minting more shares than the TwabController uint112 max type. - * @param _depositedAssets Assets deposited into the YieldVault - */ - function _getVaultMaxMint(uint256 _depositedAssets) internal pure returns (uint256) { - return type(uint112).max - _depositedAssets; - } - /* ============ Modifiers ============ */ /// @notice Require reverting if the vault is under-collateralized. diff --git a/test/unit/Vault/Liquidate.t.sol b/test/unit/Vault/Liquidate.t.sol index 4a1786e..1940d50 100644 --- a/test/unit/Vault/Liquidate.t.sol +++ b/test/unit/Vault/Liquidate.t.sol @@ -527,13 +527,7 @@ contract VaultLiquidateTest is UnitBaseSetup { vm.startPrank(address(liquidationPair)); - vm.expectRevert( - abi.encodeWithSelector( - LiquidationAmountOutGTVaultMaxMint.selector, - _amountOut, - type(uint112).max - vault.totalSupply() - ) - ); + vm.expectRevert(bytes("SafeCast: value doesn't fit in 112 bits")); vault.transferTokensOut(address(this), alice, address(vault), _amountOut); @@ -584,7 +578,7 @@ contract VaultLiquidateTest is UnitBaseSetup { vault.deposit(vault.maxDeposit(bob), bob); - vm.expectRevert(abi.encodeWithSelector(YieldFeeGTVaultMaxMint.selector, 1e18, 0)); + vm.expectRevert(); vault.mintYieldFee(1e18); From 499baba81fe2983207f8094bb4d4b3729ee895f3 Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Wed, 30 Aug 2023 11:52:57 -0500 Subject: [PATCH 3/6] fix(maxMint): use yieldVault.maxDeposit() --- src/Vault.sol | 4 ++-- test/unit/Vault/Deposit.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index d78db11..a6819fe 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -511,9 +511,9 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (!_isVaultCollateralized(_depositedAssets, _totalAssets())) return 0; uint256 _vaultMaxMint = UINT112_MAX - _depositedAssets; - uint256 _yieldVaultMaxMint = _yieldVault.maxMint(address(this)); + uint256 _yieldVaultMaxDeposit = _yieldVault.maxDeposit(address(this)); - return _yieldVaultMaxMint < _vaultMaxMint ? _yieldVaultMaxMint : _vaultMaxMint; + return _yieldVaultMaxDeposit < _vaultMaxMint ? _yieldVaultMaxDeposit : _vaultMaxMint; } /// @inheritdoc IERC4626 diff --git a/test/unit/Vault/Deposit.t.sol b/test/unit/Vault/Deposit.t.sol index acb3fd8..1c60a6c 100644 --- a/test/unit/Vault/Deposit.t.sol +++ b/test/unit/Vault/Deposit.t.sol @@ -442,7 +442,7 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { vm.mockCall( address(yieldVault), - abi.encodeWithSelector(IERC4626.maxMint.selector, address(vault)), + abi.encodeWithSelector(IERC4626.maxDeposit.selector, address(vault)), abi.encode(type(uint88).max) ); From 36bc15f8c5eed22993713eb65cce43217908bf40 Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Wed, 30 Aug 2023 12:18:28 -0500 Subject: [PATCH 4/6] chore(submodules): update deps --- lib/pt-v5-liquidator-interfaces | 2 +- lib/pt-v5-prize-pool | 2 +- lib/pt-v5-twab-controller | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pt-v5-liquidator-interfaces b/lib/pt-v5-liquidator-interfaces index 13bda46..de53bdb 160000 --- a/lib/pt-v5-liquidator-interfaces +++ b/lib/pt-v5-liquidator-interfaces @@ -1 +1 @@ -Subproject commit 13bda46baedf2ffd1c87ac5a0fd4a7229e0884f2 +Subproject commit de53bdbdd3dccc77d4ea3201f1ed391fa8a51b74 diff --git a/lib/pt-v5-prize-pool b/lib/pt-v5-prize-pool index f8a388c..8764651 160000 --- a/lib/pt-v5-prize-pool +++ b/lib/pt-v5-prize-pool @@ -1 +1 @@ -Subproject commit f8a388c9fbf6b9bcd61b71c2abb1e146e5191ff4 +Subproject commit 87646510ed085592e58059b3004445fbd8b26bef diff --git a/lib/pt-v5-twab-controller b/lib/pt-v5-twab-controller index 3985dbf..1d47dac 160000 --- a/lib/pt-v5-twab-controller +++ b/lib/pt-v5-twab-controller @@ -1 +1 @@ -Subproject commit 3985dbfdaab219e15e80125175db5ddd6c4598e0 +Subproject commit 1d47dac4bc26ec92b001bc4d398c4944d3e0092b From d79c6f27f974e89d3b3257923bc3d1c2c1f4d33c Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Wed, 30 Aug 2023 12:21:22 -0500 Subject: [PATCH 5/6] fix(mint): cast to uint112 --- src/Vault.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Vault.sol b/src/Vault.sol index a6819fe..d32c361 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -1373,7 +1373,7 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_shares > maxMint(_receiver)) revert MintMoreThanMax(_receiver, _shares, maxMint(_receiver)); - _twabController.mint(_receiver, SafeCast.toUint96(_shares)); + _twabController.mint(_receiver, SafeCast.toUint112(_shares)); emit Transfer(address(0), _receiver, _shares); } From 4c66e13232771db9dea005a05996d19571144186 Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Wed, 30 Aug 2023 12:37:25 -0500 Subject: [PATCH 6/6] fix(mint): remove MintMoreThanMax --- src/Vault.sol | 15 --------------- test/unit/Vault/Deposit.feature | 4 ++-- test/unit/Vault/Deposit.t.sol | 16 +++++++--------- test/unit/Vault/Liquidate.feature | 4 ++-- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index d32c361..536a2bb 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -58,14 +58,6 @@ error WithdrawMoreThanMax(address owner, uint256 amount, uint256 max); */ error RedeemMoreThanMax(address owner, uint256 amount, uint256 max); -/** - * @notice Emitted when the amount of shares being minted to the receiver is greater than the max amount allowed. - * @param receiver The receiver address - * @param shares The shares being minted - * @param max The max amount of shares that can be minted to the receiver - */ -error MintMoreThanMax(address receiver, uint256 shares, uint256 max); - /// @notice Emitted when `_deposit` is called but no shares are minted back to the receiver. error MintZeroShares(); @@ -1155,9 +1147,6 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { if (_withdrawableAssetsAfter < _expectedWithdrawableAssets) revert YVWithdrawableAssetsLTExpected(_withdrawableAssetsAfter, _expectedWithdrawableAssets); - if (_assets > maxMint(_receiver)) - revert MintMoreThanMax(_receiver, _assets, maxMint(_receiver)); - _mint(_receiver, _assets); emit Deposit(_caller, _receiver, _assets, _assets); @@ -1370,11 +1359,7 @@ contract Vault is IERC4626, ERC20Permit, ILiquidationSource, Ownable { * @param _shares Shares to mint */ function _mint(address _receiver, uint256 _shares) internal virtual override { - if (_shares > maxMint(_receiver)) - revert MintMoreThanMax(_receiver, _shares, maxMint(_receiver)); - _twabController.mint(_receiver, SafeCast.toUint112(_shares)); - emit Transfer(address(0), _receiver, _shares); } diff --git a/test/unit/Vault/Deposit.feature b/test/unit/Vault/Deposit.feature index 2a146c3..a574d5b 100644 --- a/test/unit/Vault/Deposit.feature +++ b/test/unit/Vault/Deposit.feature @@ -116,12 +116,12 @@ Feature: Deposit Scenario: Alice mints shares from the Vault Given Alice owns 0 Vault shares When Alice mints type(uint112).max + 1 shares - Then the transaction reverts with the custom error MintMoreThanMax + Then the transaction reverts with the error SafeCast: value doesn't fit in 112 bits Scenario: Alice mints shares from the Vault Given Alice owns 0 Vault shares and YieldVault's maxMint function returns type(uint88).max When Alice mints type(uint88).max + 1 shares - Then the transaction reverts with the custom error MintMoreThanMax + Then the transaction reverts with the error ERC4626: deposit more than max Scenario: Alice mints 0 shares from the Vault Given Alice owns 0 Vault shares diff --git a/test/unit/Vault/Deposit.t.sol b/test/unit/Vault/Deposit.t.sol index 1c60a6c..a17a6f2 100644 --- a/test/unit/Vault/Deposit.t.sol +++ b/test/unit/Vault/Deposit.t.sol @@ -423,9 +423,7 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { underlyingAsset.mint(alice, _amount); underlyingAsset.approve(address(vault), type(uint256).max); - vm.expectRevert( - abi.encodeWithSelector(MintMoreThanMax.selector, alice, _amount, type(uint112).max) - ); + vm.expectRevert(bytes("SafeCast: value doesn't fit in 112 bits")); vault.mint(_amount, alice); @@ -440,15 +438,15 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { underlyingAsset.mint(alice, _amount); underlyingAsset.approve(address(vault), type(uint256).max); - vm.mockCall( + bytes memory _errorMessage = bytes("ERC4626: deposit more than max"); + + vm.mockCallRevert( address(yieldVault), - abi.encodeWithSelector(IERC4626.maxDeposit.selector, address(vault)), - abi.encode(type(uint88).max) + abi.encodeWithSelector(IERC4626.deposit.selector, _amount, address(vault)), + _errorMessage ); - vm.expectRevert( - abi.encodeWithSelector(MintMoreThanMax.selector, alice, _amount, type(uint88).max) - ); + vm.expectRevert(_errorMessage); vault.mint(_amount, alice); diff --git a/test/unit/Vault/Liquidate.feature b/test/unit/Vault/Liquidate.feature index 6387dc5..9739038 100644 --- a/test/unit/Vault/Liquidate.feature +++ b/test/unit/Vault/Liquidate.feature @@ -88,7 +88,7 @@ Feature: Liquidate Scenario: Alice swaps prize tokens in exchange of Vault shares Given type(uint104).max underlying assets have accrued in the YieldVault When Alice swaps type(uint104).max prize tokens for type(uint104).max Vault shares - Then the transaction reverts with the custom error `MintMoreThanMax` + Then the transaction reverts with the error SafeCast: value doesn't fit in 112 bits # MintYieldFee - Errors @@ -100,4 +100,4 @@ Feature: Liquidate Scenario: Bob mints 1e18 yield fee shares Given Bob owns type(uint112).max Vault shares and 10e18 of yield fee shares have accrued When Bob mints 1e18 yield fee shares - Then the transaction reverts with the custom error `MintMoreThanMax` + Then the transaction reverts with the error SafeCast: value doesn't fit in 112 bits