diff --git a/lcov.info b/lcov.info index ccdb0ee..288585e 100644 --- a/lcov.info +++ b/lcov.info @@ -1,316 +1,327 @@ TN: SF:src/Vault.sol -FN:214,Vault.availableBalanceOf -FN:223,Vault.availableYieldBalance -FN:231,Vault.availableYieldFeeBalance -FN:244,Vault.balanceOf -FN:251,Vault.decimals -FN:256,Vault.totalAssets -FN:261,Vault.totalSupply -FN:270,Vault.exchangeRate -FN:278,Vault.isVaultCollateralized -FN:286,Vault.maxDeposit -FN:294,Vault.maxMint -FN:301,Vault.deposit -FN:320,Vault.depositWithPermit -FN:333,Vault.mint -FN:351,Vault.mintWithPermit -FN:373,Vault.sponsor -FN:387,Vault.sponsorWithPermit -FN:402,Vault.withdraw -FN:416,Vault.redeem -FN:437,Vault.liquidate -FN:473,Vault.targetOf -FN:493,Vault.claimPrize -FN:520,Vault.mintYieldFee -FN:538,Vault.disableAutoClaim -FN:550,Vault.setClaimer -FN:564,Vault.setLiquidationPair -FN:590,Vault.setYieldFeePercentage -FN:603,Vault.setYieldFeeRecipient -FN:618,Vault.yieldFeeRecipient -FN:627,Vault.yieldFeePercentage -FN:636,Vault.yieldFeeTotalSupply -FN:644,Vault.twabController -FN:652,Vault.yieldVault -FN:660,Vault.liquidationPair -FN:668,Vault.prizePool -FN:676,Vault.claimer -FN:687,Vault._totalAssets -FN:695,Vault._totalSupply -FN:705,Vault._totalShares -FN:716,Vault._availableBalanceOf -FN:736,Vault._availableYieldBalance -FN:751,Vault._availableYieldFeeBalance -FN:759,Vault._increaseYieldFeeBalance -FN:766,Vault._convertToShares -FN:792,Vault._convertToAssets -FN:818,Vault._deposit -FN:864,Vault._beforeMint -FN:875,Vault._sponsor -FN:892,Vault._withdraw -FN:930,Vault._permit -FN:950,Vault._mint -FN:963,Vault._burn -FN:977,Vault._transfer -FN:992,Vault._exchangeRate -FN:1027,Vault._isVaultCollateralized -FN:1032,Vault._requireVaultCollateralized -FN:1042,Vault._setClaimer -FN:1051,Vault._setYieldFeePercentage -FN:1060,Vault._setYieldFeeRecipient -FNDA:1581,Vault._totalShares -FNDA:33066,Vault._mint -FNDA:2,Vault.sponsorWithPermit -FNDA:1305,Vault.availableBalanceOf -FNDA:2,Vault.mintYieldFee -FNDA:2,Vault.depositWithPermit -FNDA:276,Vault.setLiquidationPair -FNDA:9,Vault._setYieldFeePercentage -FNDA:1800,Vault.withdraw -FNDA:1581,Vault._availableYieldBalance -FNDA:2058,Vault.totalSupply -FNDA:2,Vault._setClaimer -FNDA:2,Vault.twabController -FNDA:3,Vault.setClaimer -FNDA:6,Vault.availableYieldFeeBalance -FNDA:1538,Vault.mint -FNDA:256,Vault.maxMint -FNDA:256,Vault.maxDeposit -FNDA:9,Vault.sponsor -FNDA:32802,Vault._deposit -FNDA:2,Vault.prizePool -FNDA:3,Vault.disableAutoClaim -FNDA:270,Vault._requireVaultCollateralized +FN:1016,Vault._withdraw +FN:1052,Vault._claimPrize +FN:1103,Vault._permit +FN:1119,Vault._updateExchangeRate +FN:1132,Vault._mint +FN:1148,Vault._burn +FN:1164,Vault._transfer +FN:1178,Vault._currentExchangeRate +FN:1206,Vault._isVaultCollateralized +FN:1211,Vault._requireVaultCollateralized +FN:1221,Vault._setClaimer +FN:1230,Vault._setYieldFeePercentage +FN:1241,Vault._setYieldFeeRecipient +FN:313,Vault.availableYieldBalance +FN:324,Vault.availableYieldFeeBalance +FN:335,Vault.balanceOf +FN:342,Vault.decimals +FN:347,Vault.totalAssets +FN:352,Vault.totalSupply +FN:361,Vault.exchangeRate +FN:369,Vault.isVaultCollateralized +FN:377,Vault.maxDeposit +FN:388,Vault.maxMint +FN:398,Vault.mintYieldFee +FN:411,Vault.deposit +FN:431,Vault.depositWithPermit +FN:444,Vault.mint +FN:462,Vault.mintWithPermit +FN:484,Vault.sponsor +FN:498,Vault.sponsorWithPermit +FN:513,Vault.withdraw +FN:528,Vault.redeem +FN:544,Vault.liquidatableBalanceOf +FN:554,Vault.liquidate +FN:600,Vault.targetOf +FN:616,Vault.claimPrizes +FN:650,Vault.setClaimer +FN:662,Vault.setHooks +FN:674,Vault.setLiquidationPair +FN:700,Vault.setYieldFeePercentage +FN:713,Vault.setYieldFeeRecipient +FN:728,Vault.yieldFeeRecipient +FN:737,Vault.yieldFeePercentage +FN:746,Vault.yieldFeeTotalSupply +FN:754,Vault.twabController +FN:762,Vault.yieldVault +FN:770,Vault.liquidationPair +FN:778,Vault.prizePool +FN:786,Vault.claimer +FN:795,Vault.getHooks +FN:809,Vault._totalAssets +FN:817,Vault._totalSupply +FN:827,Vault._totalShares +FN:838,Vault._liquidatableBalanceOf +FN:853,Vault._availableYieldFeeBalance +FN:861,Vault._increaseYieldFeeBalance +FN:873,Vault._convertToShares +FN:891,Vault._convertToAssets +FN:934,Vault._deposit +FN:980,Vault._beforeMint +FN:991,Vault._sponsor +FNDA:3360,Vault._withdraw +FNDA:3,Vault._claimPrize FNDA:6,Vault._permit -FNDA:10,Vault.setYieldFeePercentage -FNDA:3084,Vault._burn -FNDA:5,Vault._increaseYieldFeeBalance +FNDA:36215,Vault._updateExchangeRate +FNDA:33110,Vault._mint +FNDA:3106,Vault._burn +FNDA:258,Vault._transfer +FNDA:112231,Vault._currentExchangeRate +FNDA:33644,Vault._isVaultCollateralized +FNDA:274,Vault._requireVaultCollateralized +FNDA:2,Vault._setClaimer +FNDA:11,Vault._setYieldFeePercentage +FNDA:3,Vault._setYieldFeeRecipient +FNDA:7,Vault.availableYieldBalance +FNDA:7,Vault.availableYieldFeeBalance +FNDA:3668,Vault.balanceOf FNDA:1,Vault.decimals -FNDA:2,Vault.yieldFeePercentage -FNDA:33588,Vault._isVaultCollateralized -FNDA:3628,Vault.balanceOf -FNDA:31249,Vault.deposit -FNDA:268,Vault.liquidate -FNDA:145126,Vault._exchangeRate -FNDA:6,Vault.availableYieldBalance -FNDA:1570,Vault._availableYieldFeeBalance -FNDA:2,Vault.liquidationPair +FNDA:1024,Vault.totalAssets +FNDA:2083,Vault.totalSupply +FNDA:2,Vault.exchangeRate +FNDA:8,Vault.isVaultCollateralized +FNDA:256,Vault.maxDeposit +FNDA:256,Vault.maxMint +FNDA:3,Vault.mintYieldFee +FNDA:31291,Vault.deposit +FNDA:2,Vault.depositWithPermit +FNDA:1539,Vault.mint FNDA:2,Vault.mintWithPermit +FNDA:11,Vault.sponsor +FNDA:2,Vault.sponsorWithPermit +FNDA:1823,Vault.withdraw +FNDA:1797,Vault.redeem +FNDA:1311,Vault.liquidatableBalanceOf +FNDA:271,Vault.liquidate +FNDA:1,Vault.targetOf +FNDA:5,Vault.claimPrizes +FNDA:3,Vault.setClaimer +FNDA:3,Vault.setHooks +FNDA:278,Vault.setLiquidationPair +FNDA:12,Vault.setYieldFeePercentage +FNDA:4,Vault.setYieldFeeRecipient FNDA:2,Vault.yieldFeeRecipient +FNDA:2,Vault.yieldFeePercentage +FNDA:6,Vault.yieldFeeTotalSupply +FNDA:2,Vault.twabController FNDA:2,Vault.yieldVault -FNDA:298096,Vault._convertToAssets -FNDA:33955,Vault._convertToShares -FNDA:4,Vault.isVaultCollateralized -FNDA:1,Vault.exchangeRate -FNDA:1540,Vault._beforeMint -FNDA:11,Vault._sponsor -FNDA:3,Vault.setYieldFeeRecipient -FNDA:2,Vault._setYieldFeeRecipient -FNDA:1569,Vault._availableBalanceOf -FNDA:1024,Vault.totalAssets -FNDA:5,Vault.claimPrize -FNDA:1796,Vault.redeem -FNDA:148765,Vault._totalSupply -FNDA:258,Vault._transfer +FNDA:2,Vault.liquidationPair +FNDA:2,Vault.prizePool FNDA:3,Vault.claimer -FNDA:4,Vault.yieldFeeTotalSupply -FNDA:2605,Vault._totalAssets -FNDA:2,Vault.targetOf -FNDA:3299,Vault._withdraw -FNF:59 -FNH:59 -DA:215,1305 -DA:224,1581 -DA:232,6 -DA:234,6 -DA:236,6 -DA:237,1 -DA:240,5 -DA:247,7750 -DA:252,1 -DA:257,1024 -DA:262,2058 -DA:271,1 -DA:279,4 -DA:287,31518 -DA:295,1796 -DA:302,31262 -DA:304,31262 -DA:305,31262 -DA:307,31262 -DA:328,2 -DA:329,2 -DA:334,1538 -DA:336,1538 -DA:338,1538 -DA:359,2 -DA:361,2 +FNDA:1,Vault.getHooks +FNDA:2615,Vault._totalAssets +FNDA:115905,Vault._totalSupply +FNDA:1591,Vault._totalShares +FNDA:1577,Vault._liquidatableBalanceOf +FNDA:1578,Vault._availableYieldFeeBalance +FNDA:7,Vault._increaseYieldFeeBalance +FNDA:34012,Vault._convertToShares +FNDA:128947,Vault._convertToAssets +FNDA:32844,Vault._deposit +FNDA:1541,Vault._beforeMint +FNDA:13,Vault._sponsor +FNF:61 +FNH:61 +DA:314,1591 +DA:315,1591 +DA:317,1591 +DA:325,7 +DA:327,7 +DA:328,1 +DA:331,6 +DA:338,8109 +DA:343,1 +DA:348,1024 +DA:353,2083 DA:362,2 -DA:364,2 -DA:374,9 -DA:395,2 -DA:396,2 -DA:407,1800 -DA:409,1669 -DA:410,1669 -DA:412,1544 -DA:421,1796 -DA:423,1630 -DA:424,1630 -DA:426,1540 -DA:444,268 -DA:445,268 -DA:446,267 -DA:447,266 -DA:448,265 -DA:450,264 -DA:451,264 -DA:453,263 -DA:455,263 -DA:456,5 -DA:461,263 -DA:463,263 -DA:464,263 -DA:467,263 -DA:469,263 -DA:474,2 -DA:475,1 -DA:500,5 -DA:502,5 -DA:503,4 -DA:504,2 +DA:370,8 +DA:378,31564 +DA:380,31563 +DA:381,31563 +DA:389,1798 +DA:399,3 +DA:400,2 +DA:402,1 +DA:403,1 +DA:405,1 +DA:412,31306 +DA:413,2 +DA:415,31304 +DA:416,31304 +DA:418,31304 +DA:439,2 +DA:440,2 +DA:445,1539 +DA:447,1538 +DA:449,1538 +DA:470,2 +DA:472,2 +DA:473,2 +DA:475,2 +DA:485,11 DA:506,2 -DA:510,3 -DA:521,2 -DA:522,2 -DA:524,1 -DA:525,1 -DA:527,1 -DA:539,3 -DA:541,3 -DA:542,3 -DA:551,2 -DA:552,2 -DA:554,2 -DA:555,2 -DA:567,275 -DA:569,274 -DA:570,274 -DA:572,274 -DA:573,1 -DA:576,274 -DA:578,274 -DA:580,274 -DA:581,274 -DA:591,9 -DA:592,9 -DA:594,8 -DA:595,8 -DA:604,2 -DA:605,2 -DA:607,2 -DA:608,2 -DA:619,2 -DA:628,2 -DA:637,4 -DA:645,2 -DA:653,2 -DA:661,2 -DA:669,2 -DA:677,3 -DA:688,2605 -DA:696,148765 -DA:706,1581 -DA:717,1569 -DA:719,1569 -DA:722,1569 -DA:740,1581 -DA:741,1581 -DA:743,1581 -DA:752,1570 -DA:760,5 -DA:770,33955 -DA:772,33955 -DA:783,7844 -DA:784,7844 -DA:786,7844 -DA:796,290252 -DA:797,290252 -DA:799,290252 -DA:824,32802 -DA:825,32802 -DA:835,32802 -DA:836,26362 -DA:839,26362 -DA:840,392 -DA:844,26362 -DA:852,32802 -DA:853,32802 -DA:855,32802 -DA:865,1540 -DA:866,1540 -DA:876,11 -DA:879,11 -DA:881,11 -DA:884,11 -DA:886,11 -DA:899,3299 -DA:900,1241 -DA:909,3084 -DA:911,3084 -DA:912,3084 -DA:914,3084 -DA:940,6 -DA:951,33066 -DA:952,33066 -DA:954,33066 -DA:964,3084 -DA:965,3084 -DA:968,3084 -DA:978,258 -DA:980,258 -DA:993,145126 -DA:994,145126 -DA:995,145126 -DA:996,145126 -DA:997,145126 -DA:998,145126 -DA:1000,145126 -DA:1001,145126 -DA:1002,145126 -DA:1003,145126 -DA:1004,145126 -DA:1008,145126 -DA:1009,2397 -DA:1012,145126 -DA:1015,145126 -DA:1016,118319 -DA:1019,26807 -DA:1028,33588 -DA:1029,33588 -DA:1033,270 -DA:1043,2 -DA:1052,9 -DA:1053,8 -DA:1061,2 -LF:174 -LH:174 +DA:507,2 +DA:518,1823 +DA:519,139 +DA:521,1684 +DA:522,1684 +DA:524,1565 +DA:533,1797 +DA:535,1676 +DA:536,1676 +DA:538,1540 +DA:545,1311 +DA:561,271 +DA:563,270 +DA:564,1 +DA:566,269 +DA:567,1 +DA:569,268 +DA:570,1 +DA:572,267 +DA:574,266 +DA:575,266 +DA:577,266 +DA:578,1 +DA:580,265 +DA:582,265 +DA:583,7 +DA:588,265 +DA:590,265 +DA:591,256 +DA:594,265 +DA:596,265 +DA:601,1 +DA:623,5 +DA:625,3 +DA:627,3 +DA:628,3 +DA:629,3 +DA:630,3 +DA:640,3 +DA:651,2 +DA:652,2 +DA:654,2 +DA:655,2 +DA:663,3 +DA:665,3 +DA:677,277 +DA:679,276 +DA:680,276 +DA:682,276 +DA:683,1 +DA:686,276 +DA:688,276 +DA:690,276 +DA:691,276 +DA:701,11 +DA:702,11 +DA:704,10 +DA:705,10 +DA:714,3 +DA:715,3 +DA:717,3 +DA:718,3 +DA:729,2 +DA:738,2 +DA:747,6 +DA:755,2 +DA:763,2 +DA:771,2 +DA:779,2 +DA:787,3 +DA:796,1 +DA:810,2615 +DA:818,115905 +DA:828,1591 +DA:839,1577 +DA:841,1577 +DA:844,1577 +DA:854,1578 +DA:862,7 +DA:877,34012 +DA:879,34012 +DA:880,34012 +DA:895,8358 +DA:910,120589 +DA:911,120589 +DA:940,32844 +DA:941,32844 +DA:951,32844 +DA:952,28148 +DA:955,28148 +DA:956,462 +DA:960,28148 +DA:968,32844 +DA:969,32844 +DA:971,32844 +DA:981,1541 +DA:982,1540 +DA:992,13 +DA:995,13 +DA:997,12 +DA:1000,13 +DA:1002,13 +DA:1023,3360 +DA:1024,1280 +DA:1033,3106 +DA:1035,3105 +DA:1036,3105 +DA:1038,3105 +DA:1059,3 +DA:1060,3 +DA:1062,3 +DA:1063,2 +DA:1065,1 +DA:1068,3 +DA:1077,3 +DA:1078,1 +DA:1087,3 +DA:1113,6 +DA:1120,36215 +DA:1121,36215 +DA:1133,33110 +DA:1134,33110 +DA:1136,33110 +DA:1149,3106 +DA:1150,3105 +DA:1152,3105 +DA:1165,258 +DA:1167,258 +DA:1179,112231 +DA:1180,112231 +DA:1186,112231 +DA:1190,112231 +DA:1191,2742 +DA:1194,112231 +DA:1195,95491 +DA:1198,16740 +DA:1207,33644 +DA:1212,274 +DA:1222,2 +DA:1231,11 +DA:1232,1 +DA:1234,10 +DA:1242,3 +LF:181 +LH:181 end_of_record TN: SF:src/VaultFactory.sol -FN:51,VaultFactory.deployVault -FN:88,VaultFactory.totalVaults -FNDA:1,VaultFactory.totalVaults +FN:55,VaultFactory.deployVault +FN:92,VaultFactory.totalVaults FNDA:1,VaultFactory.deployVault +FNDA:1,VaultFactory.totalVaults FNF:2 FNH:2 -DA:63,1 -DA:76,1 -DA:77,1 -DA:79,1 +DA:67,1 +DA:80,1 DA:81,1 -DA:89,1 +DA:83,1 +DA:85,1 +DA:93,1 LF:6 LH:6 end_of_record diff --git a/src/Vault.sol b/src/Vault.sol index 75f2a1a..1ef231b 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -374,16 +374,26 @@ contract Vault is ERC4626, ERC20Permit, ILiquidationSource, Ownable { * @inheritdoc ERC4626 * @dev We use type(uint96).max cause this is the type used to store balances in TwabController. */ - function maxDeposit(address) public view virtual override returns (uint256) { - return _isVaultCollateralized() ? type(uint96).max : 0; + function maxDeposit(address recipient) public view virtual override returns (uint256) { + if (!_isVaultCollateralized()) return 0; + + uint256 _vaultMaxDeposit = type(uint96).max - _convertToAssets(balanceOf(recipient), Math.Rounding.Down); + uint256 _yieldVaultMaxDeposit = _yieldVault.maxDeposit(address(this)); + + return _yieldVaultMaxDeposit < _vaultMaxDeposit ? _yieldVaultMaxDeposit : _vaultMaxDeposit; } /** * @inheritdoc ERC4626 * @dev We use type(uint96).max cause this is the type used to store balances in TwabController. */ - function maxMint(address) public view virtual override returns (uint256) { - return _isVaultCollateralized() ? type(uint96).max : 0; + function maxMint(address recipient) public view virtual override returns (uint256) { + if (!_isVaultCollateralized()) return 0; + + uint256 _vaultMaxMint = type(uint96).max - balanceOf(recipient); + uint256 _yieldVaultMaxMint = _yieldVault.maxMint(address(this)); + + return _yieldVaultMaxMint < _vaultMaxMint ? _yieldVaultMaxMint : _vaultMaxMint; } /** @@ -439,7 +449,7 @@ contract Vault is ERC4626, ERC20Permit, ILiquidationSource, Ownable { /// @inheritdoc ERC4626 function mint(uint256 _shares, address _receiver) public virtual override returns (uint256) { - uint256 _assets = _beforeMint(_shares, _receiver); + uint256 _assets = _convertToAssets(_shares, Math.Rounding.Up); _deposit(msg.sender, _receiver, _assets, _shares); @@ -464,7 +474,7 @@ contract Vault is ERC4626, ERC20Permit, ILiquidationSource, Ownable { bytes32 _r, bytes32 _s ) external returns (uint256) { - uint256 _assets = _beforeMint(_shares, _receiver); + uint256 _assets = _convertToAssets(_shares, Math.Rounding.Up); _permit(IERC20Permit(asset()), msg.sender, address(this), _assets, _deadline, _v, _r, _s); _deposit(msg.sender, _receiver, _assets, _shares); @@ -968,17 +978,6 @@ contract Vault is ERC4626, ERC20Permit, ILiquidationSource, Ownable { emit Deposit(_caller, _receiver, _assets, _shares); } - /** - * @notice Compute the amount of assets to deposit before minting `_shares`. - * @param _shares Amount of shares to mint - * @param _receiver Address of the receiver of the vault shares - * @return uint256 Amount of assets to deposit. - */ - function _beforeMint(uint256 _shares, address _receiver) internal view returns (uint256) { - if (_shares > maxMint(_receiver)) revert MintMoreThanMax(_receiver, _shares, maxMint(_receiver)); - return _convertToAssets(_shares, Math.Rounding.Up); - } - /** * @notice Deposit assets into the Vault and delegate to the sponsorship address. * @param _assets Amount of assets to deposit @@ -1127,6 +1126,8 @@ contract Vault is ERC4626, ERC20Permit, ILiquidationSource, Ownable { * @dev Updates the exchange rate. */ function _mint(address _receiver, uint256 _shares) internal virtual override { + if (_shares > maxMint(_receiver)) revert MintMoreThanMax(_receiver, _shares, maxMint(_receiver)); + _twabController.mint(_receiver, SafeCast.toUint96(_shares)); _updateExchangeRate(); diff --git a/test/unit/Vault/Deposit.feature b/test/unit/Vault/Deposit.feature index 261cb9f..5affed2 100644 --- a/test/unit/Vault/Deposit.feature +++ b/test/unit/Vault/Deposit.feature @@ -57,6 +57,17 @@ Feature: Deposit Then the YieldVault must mint to the Vault an amount of shares equivalent to the amount of underlying assets deposited Then the Vault `totalSupply` must be equal to 1,000 + # Deposit - Errors + Scenario: Alice deposits into the Vault + Given Alice owns 0 Vault shares + When Alice deposits type(uint96).max + 1 underlying assets + Then the transaction reverts with the custom error DepositMoreThanMax + + Scenario: Alice deposits into the Vault + Given Alice owns 0 Vault shares and YieldVault's maxDeposit function returns type(uint88).max + When Alice deposits type(uint88).max + 1 underlying assets + Then the transaction reverts with the custom error DepositMoreThanMax + # Deposit - Attacks # Inflation attack Scenario: Bob front runs Alice deposits into the Vault @@ -118,6 +129,17 @@ Feature: Deposit Then the YieldVault must mint to the Vault an amount of shares equivalent to the amount of underlying assets deposited Then the Vault `totalSupply` must be equal to 1,000 + # Mint - Errors + Scenario: Alice mints shares from the Vault + Given Alice owns 0 Vault shares + When Alice mints type(uint96).max + 1 shares + Then the transaction reverts with the custom error MintMoreThanMax + + 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 + # Sponsor Scenario: Alice sponsors the Vault Given Alice owns 0 Vault shares and has not sponsored the Vault diff --git a/test/unit/Vault/Deposit.t.sol b/test/unit/Vault/Deposit.t.sol index f9cda23..a8c7e54 100644 --- a/test/unit/Vault/Deposit.t.sol +++ b/test/unit/Vault/Deposit.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import { MintMoreThanMax, DepositMoreThanMax } from "../../../src/Vault.sol"; - import { BrokenToken } from "brokentoken/BrokenToken.sol"; +import { IERC4626 } from "openzeppelin/token/ERC20/extensions/ERC4626.sol"; import { IERC20, UnitBaseSetup } from "../../utils/UnitBaseSetup.t.sol"; -import { console2 } from "forge-std/Test.sol"; +import { MintMoreThanMax, DepositMoreThanMax } from "../../../src/Vault.sol"; contract VaultDepositTest is UnitBaseSetup, BrokenToken { /* ============ Events ============ */ @@ -51,20 +50,6 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { vm.stopPrank(); } - function testDepositMoreThanMax() external { - vm.startPrank(alice); - - uint256 _moreThanMax = uint256(type(uint96).max) + 1; - uint256 _amount = _moreThanMax; - underlyingAsset.mint(alice, _amount); - underlyingAsset.approve(address(vault), type(uint256).max); - - vm.expectRevert(abi.encodeWithSelector(DepositMoreThanMax.selector, alice, _amount, type(uint96).max)); - vault.deposit(_amount, alice); - - vm.stopPrank(); - } - function testDepositAssetsLivingInVault() external { uint256 _vaultAmount = 500e18; underlyingAsset.mint(address(vault), _vaultAmount); @@ -187,6 +172,42 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { vm.stopPrank(); } + /* ============ Deposit - Errors ============ */ + function testDepositMoreThanMax() external { + vm.startPrank(alice); + + uint256 _amount = uint256(type(uint96).max) + 1; + + underlyingAsset.mint(alice, _amount); + underlyingAsset.approve(address(vault), type(uint256).max); + + vm.expectRevert(abi.encodeWithSelector(DepositMoreThanMax.selector, alice, _amount, type(uint96).max)); + vault.deposit(_amount, alice); + + vm.stopPrank(); + } + + function testDepositMoreThanYieldVaultMax() external { + vm.startPrank(alice); + + + uint256 _amount = uint256(type(uint88).max) + 1; + + underlyingAsset.mint(alice, _amount); + underlyingAsset.approve(address(vault), type(uint256).max); + + vm.mockCall( + address(yieldVault), + abi.encodeWithSelector(IERC4626.maxDeposit.selector, address(vault)), + abi.encode(type(uint88).max) + ); + + vm.expectRevert(abi.encodeWithSelector(DepositMoreThanMax.selector, alice, _amount, type(uint88).max)); + vault.deposit(_amount, alice); + + vm.stopPrank(); + } + /* ============ Deposit - Attacks ============ */ function testFailDepositInflationAttack() external { vm.startPrank(bob); @@ -385,10 +406,12 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { vm.stopPrank(); } + /* ============ Mint - Errors ============ */ function testMintMoreThanMax() external { vm.startPrank(alice); uint256 _amount = uint256(type(uint96).max) + 1; + underlyingAsset.mint(alice, _amount); underlyingAsset.approve(address(vault), type(uint256).max); @@ -399,6 +422,27 @@ contract VaultDepositTest is UnitBaseSetup, BrokenToken { vm.stopPrank(); } + function testMintMoreThanYieldVaultMax() external { + vm.startPrank(alice); + + uint256 _amount = uint256(type(uint88).max) + 1; + + underlyingAsset.mint(alice, _amount); + underlyingAsset.approve(address(vault), type(uint256).max); + + vm.mockCall( + address(yieldVault), + abi.encodeWithSelector(IERC4626.maxMint.selector, address(vault)), + abi.encode(type(uint88).max) + ); + + vm.expectRevert(abi.encodeWithSelector(MintMoreThanMax.selector, alice, _amount, type(uint88).max)); + + vault.mint(_amount, alice); + + vm.stopPrank(); + } + /* ============ Sponsor ============ */ function testSponsor() external { vm.startPrank(alice); diff --git a/test/unit/Vault/Liquidate.feature b/test/unit/Vault/Liquidate.feature index 1f36f07..35a4880 100644 --- a/test/unit/Vault/Liquidate.feature +++ b/test/unit/Vault/Liquidate.feature @@ -54,7 +54,7 @@ Feature: Liquidate Then the Vault total supply must increase by 1 - # Failure + # Liquidate - Errors Scenario: Bob swaps prize tokens in exchange of Vault shares Given the YieldVault is now undercollateralized When `liquidate` is called @@ -85,7 +85,19 @@ Feature: Liquidate When Bob swaps 0 prize tokens for uint256.max Vault shares through the LiquidationRouter Then the transaction reverts with the custom error `LiquidationAmountOutGTYield` - Scenario: Bob mints an arbitrary amount of yield fee + 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` + + + # MintYieldFee - Errors + Scenario: Bob mints an arbitrary amount of yield fee shares Given no yield fee has accrued When Bob mints 10 yield fee shares 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 + 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 203c9bf..6698fd0 100644 --- a/test/unit/Vault/Liquidate.t.sol +++ b/test/unit/Vault/Liquidate.t.sol @@ -317,7 +317,7 @@ contract VaultLiquidateTest is UnitBaseSetup { assertEq(vault.yieldFeeTotalSupply(), 0); } - /* ============ Errors ============ */ + /* ============ Liquidate - Errors ============ */ function testLiquidateYieldVaultUndercollateralized() public { _setLiquidationPair(); @@ -412,8 +412,92 @@ contract VaultLiquidateTest is UnitBaseSetup { vm.stopPrank(); } + function testLiquidateAmountOutGTMaxMint() public { + _setLiquidationPair(); + + uint256 _amount = 1000e18; + + underlyingAsset.mint(address(this), _amount); + _sponsor(underlyingAsset, vault, _amount, address(this)); + + uint256 _amountOut = type(uint104).max; + _accrueYield(underlyingAsset, yieldVault, _amountOut); + + vm.startPrank(address(alice)); + + prizeToken.mint(alice, type(uint256).max); + prizeToken.approve(address(this), type(uint256).max); + + vm.stopPrank(); + + uint256 _amountIn = liquidationPair.computeExactAmountIn(_amountOut); + + IERC20(address(prizeToken)).transferFrom( + alice, + address(prizePool), + _amountIn + ); + + vm.startPrank(address(liquidationPair)); + + vm.expectRevert( + abi.encodeWithSelector(MintMoreThanMax.selector, alice, _amountOut, type(uint96).max) + ); + + vault.liquidate(alice, address(prizeToken), _amountIn, address(vault), _amountOut); + + vm.stopPrank(); + } + + /* ============ MintYieldFee - Errors ============ */ function testMintYieldFeeGTYieldFeeSupply() public { vm.expectRevert(abi.encodeWithSelector(YieldFeeGTAvailable.selector, 10e18, 0)); vault.mintYieldFee(10e18); } + + function testMintYieldFeeMoreThanMax() public { + _setLiquidationPair(); + + vault.setYieldFeePercentage(YIELD_FEE_PERCENTAGE); + vault.setYieldFeeRecipient(bob); + + uint256 _amount = 1000e18; + + underlyingAsset.mint(address(this), _amount); + _sponsor(underlyingAsset, vault, _amount, address(this)); + + uint256 _yield = 10e18; + _accrueYield(underlyingAsset, yieldVault, _yield); + + vm.startPrank(alice); + + prizeToken.mint(alice, 1000e18); + + uint256 _liquidatableYield = vault.liquidatableBalanceOf(address(vault)); + + _liquidate( + liquidationRouter, + liquidationPair, + prizeToken, + _liquidatableYield, + alice + ); + + vm.stopPrank(); + + vm.startPrank(bob); + + underlyingAsset.mint(bob, vault.maxDeposit(bob)); + underlyingAsset.approve(address(vault), vault.maxDeposit(bob)); + + vault.deposit(vault.maxDeposit(bob), bob); + + vm.expectRevert( + abi.encodeWithSelector(MintMoreThanMax.selector, bob, 1e18, 0) + ); + + vault.mintYieldFee(1e18); + + vm.stopPrank(); + } } diff --git a/test/unit/Vault/Withdraw.feature b/test/unit/Vault/Withdraw.feature index cab13da..7983da2 100644 --- a/test/unit/Vault/Withdraw.feature +++ b/test/unit/Vault/Withdraw.feature @@ -43,12 +43,6 @@ Feature: Withdraw Then the YieldVault balance of underlying assets must be equal to 0 Then the Vault `totalSupply` must be equal to 0 - # Withdraw - Attacks - Scenario: Bob tries to withdraw more than uint96 to exploit a rounding down error - Given Bob owns double the max amount that fits in uint96 Vault shares - When Bob `withdraw` his full deposit - Then it reverts with the error "SafeCast: value doesn't fit in 96 bits" - # Redeem Scenario: Alice redeems her full deposit Given Alice owns 1,000 Vault shares diff --git a/test/unit/Vault/Withdraw.t.sol b/test/unit/Vault/Withdraw.t.sol index 5ff3147..74d4160 100644 --- a/test/unit/Vault/Withdraw.t.sol +++ b/test/unit/Vault/Withdraw.t.sol @@ -246,37 +246,6 @@ contract VaultWithdrawTest is UnitBaseSetup { vm.stopPrank(); } - /* ============ Withdraw - Attacks ============ */ - function testWithdrawOverflow() external { - uint256 _amount = type(uint96).max; - uint256 _doubleAmount = _amount * 2; - - vm.startPrank(bob); - - underlyingAsset.mint(bob, _doubleAmount); - underlyingAsset.approve(address(vault), _doubleAmount); - - // Need to deposit in two steps cause it would overflow over the maxDeposit limit of uint96 otherwise - // NOTE: this is only possible cause balances are stored in uint112 in the TwabController, it would revert if it was uint96 - vault.deposit(_amount, bob); - vault.deposit(_amount, bob); - - assertEq(vault.balanceOf(bob), _doubleAmount); - - assertEq(twabController.balanceOf(address(vault), bob), _doubleAmount); - assertEq(twabController.delegateBalanceOf(address(vault), bob), _doubleAmount); - - assertEq(underlyingAsset.balanceOf(address(yieldVault)), _doubleAmount); - assertEq(yieldVault.balanceOf(address(vault)), _doubleAmount); - assertEq(yieldVault.totalSupply(), _doubleAmount); - - uint256 _bobMaxWithdraw = vault.maxWithdraw(bob); - assertEq(_bobMaxWithdraw, _doubleAmount); - - vm.expectRevert(bytes("SafeCast: value doesn't fit in 96 bits")); - vault.withdraw(_bobMaxWithdraw, bob, bob); - } - /* ============ Redeem ============ */ function testRedeemFullAmount() external { vm.startPrank(alice);