From c61ff6880bf0cecbabfb8ea1325c6a42d6cfbbc4 Mon Sep 17 00:00:00 2001 From: OxMarco <> Date: Mon, 18 Dec 2023 17:16:53 +0100 Subject: [PATCH 1/4] fix l10 --- src/Vault.sol | 14 ++++++++++++-- test/Vault.test.sol | 16 ++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index 63483a84..aee8a612 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -147,6 +147,16 @@ contract Vault is IVault, ERC4626, ERC20Permit { return ERC4626.deposit(_assets, receiver); } + function maxDeposit(address) public view override(IERC4626, ERC4626) returns (uint256) { + if (isLocked) return 0; + return type(uint256).max; + } + + function maxMint(address) public view override(IERC4626, ERC4626) returns (uint256) { + if (isLocked) return 0; + return type(uint256).max; + } + function depositWithPermit( uint256 assets, address receiver, @@ -170,7 +180,7 @@ contract Vault is IVault, ERC4626, ERC20Permit { ) public override(ERC4626, IERC4626) returns (uint256) { // assets cannot be more than the current free liquidity uint256 freeLiq = freeLiquidity(); - if (assets >= freeLiq) revert InsufficientLiquidity(); + if (assets > freeLiq) revert InsufficientLiquidity(); // super.withdraw but we leverage the fact of having already computed freeLiq uint256 supply = totalSupply(); @@ -194,7 +204,7 @@ contract Vault is IVault, ERC4626, ERC20Permit { uint256 supply = totalSupply(); uint256 assets = shares.mulDiv(totalAssetsCache, supply); - if (assets >= freeLiq) revert InsufficientLiquidity(); + if (assets > freeLiq) revert InsufficientLiquidity(); // redeem, now all data have been computed _withdraw(msg.sender, receiver, owner, assets, shares); diff --git a/test/Vault.test.sol b/test/Vault.test.sol index fc1d7635..07ccab74 100644 --- a/test/Vault.test.sol +++ b/test/Vault.test.sol @@ -85,7 +85,7 @@ contract VaultTest is Test { assertTrue(token.balanceOf(tokenSink) == type(uint256).max); } - function testAccess(uint256 shares, uint256 assets, uint256 debt) public { + function testAccess(uint256 assets, uint256 debt) public { vm.startPrank(notOwner); vm.expectRevert(bytes4(keccak256(abi.encodePacked("RestrictedToOwner()")))); vault.setFeeUnlockTime(1000); @@ -422,7 +422,7 @@ contract VaultTest is Test { uint256 shares = 0; vm.startPrank(receiver); - if (withdrawn >= vault.freeLiquidity()) { + if (withdrawn > vault.freeLiquidity()) { vm.expectRevert(bytes4(keccak256(abi.encodePacked("InsufficientLiquidity()")))); vault.withdraw(withdrawn, receiver, receiver); withdrawn = 0; @@ -597,25 +597,29 @@ contract VaultTest is Test { } function testCannotWithdrawMoreThanFreeLiquidity(uint256 amount) public { + vm.assume(amount < type(uint256).max); + vm.startPrank(tokenSink); - token.approve(address(vault), amount); + token.approve(address(vault), type(uint256).max); vault.deposit(amount, receiver); vm.stopPrank(); // withdraw without leaving 1 token unit uint256 vaultBalance = token.balanceOf(address(vault)); vm.expectRevert(IVault.InsufficientLiquidity.selector); - vault.withdraw(vaultBalance, tokenSink, receiver); + vault.withdraw(vaultBalance + 1, tokenSink, receiver); } function testCannotBorrowMoreThanFreeLiquidity(uint256 amount) public { + vm.assume(amount < type(uint256).max); + vm.startPrank(tokenSink); - token.approve(address(vault), amount); + token.approve(address(vault), type(uint256).max); vault.deposit(amount, receiver); vm.stopPrank(); uint256 vaultBalance = token.balanceOf(address(vault)); vm.expectRevert(IVault.InsufficientFreeLiquidity.selector); - vault.borrow(vaultBalance, vaultBalance, address(this)); + vault.borrow(vaultBalance + 1, vaultBalance, address(this)); } } From 602e6f8a9d3a07ffd316a115a87c5224efc55482 Mon Sep 17 00:00:00 2001 From: OxMarco <> Date: Mon, 18 Dec 2023 17:49:02 +0100 Subject: [PATCH 2/4] fix l10 --- src/Vault.sol | 4 ++-- test/Vault.test.sol | 14 +++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index aee8a612..45dbc471 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -180,7 +180,7 @@ contract Vault is IVault, ERC4626, ERC20Permit { ) public override(ERC4626, IERC4626) returns (uint256) { // assets cannot be more than the current free liquidity uint256 freeLiq = freeLiquidity(); - if (assets > freeLiq) revert InsufficientLiquidity(); + if (assets >= freeLiq) revert InsufficientLiquidity(); // super.withdraw but we leverage the fact of having already computed freeLiq uint256 supply = totalSupply(); @@ -204,7 +204,7 @@ contract Vault is IVault, ERC4626, ERC20Permit { uint256 supply = totalSupply(); uint256 assets = shares.mulDiv(totalAssetsCache, supply); - if (assets > freeLiq) revert InsufficientLiquidity(); + if (assets >= freeLiq) revert InsufficientLiquidity(); // redeem, now all data have been computed _withdraw(msg.sender, receiver, owner, assets, shares); diff --git a/test/Vault.test.sol b/test/Vault.test.sol index 07ccab74..4195cf5d 100644 --- a/test/Vault.test.sol +++ b/test/Vault.test.sol @@ -422,7 +422,7 @@ contract VaultTest is Test { uint256 shares = 0; vm.startPrank(receiver); - if (withdrawn > vault.freeLiquidity()) { + if (withdrawn >= vault.freeLiquidity()) { vm.expectRevert(bytes4(keccak256(abi.encodePacked("InsufficientLiquidity()")))); vault.withdraw(withdrawn, receiver, receiver); withdrawn = 0; @@ -597,29 +597,25 @@ contract VaultTest is Test { } function testCannotWithdrawMoreThanFreeLiquidity(uint256 amount) public { - vm.assume(amount < type(uint256).max); - vm.startPrank(tokenSink); - token.approve(address(vault), type(uint256).max); + token.approve(address(vault), amount); vault.deposit(amount, receiver); vm.stopPrank(); // withdraw without leaving 1 token unit uint256 vaultBalance = token.balanceOf(address(vault)); vm.expectRevert(IVault.InsufficientLiquidity.selector); - vault.withdraw(vaultBalance + 1, tokenSink, receiver); + vault.withdraw(vaultBalance, tokenSink, receiver); } function testCannotBorrowMoreThanFreeLiquidity(uint256 amount) public { - vm.assume(amount < type(uint256).max); - vm.startPrank(tokenSink); - token.approve(address(vault), type(uint256).max); + token.approve(address(vault), amount); vault.deposit(amount, receiver); vm.stopPrank(); uint256 vaultBalance = token.balanceOf(address(vault)); vm.expectRevert(IVault.InsufficientFreeLiquidity.selector); - vault.borrow(vaultBalance + 1, vaultBalance, address(this)); + vault.borrow(vaultBalance, vaultBalance, address(this)); } } From 689bd72a6f0f87004607abf81e54b9c1ce2dbb90 Mon Sep 17 00:00:00 2001 From: lsqrl Date: Tue, 19 Dec 2023 11:53:15 +0100 Subject: [PATCH 3/4] maxDeposit and maxMint added in case of locks, maxWithdraw and maxRedeem adjusted to consider the fact that freeLiquidity cannot be withdrawn --- src/Vault.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index 45dbc471..a21ca537 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -101,7 +101,6 @@ contract Vault is IVault, ERC4626, ERC20Permit { // Free liquidity available to withdraw or borrow // Locked profits are locked for every operation // We do not consider negative profits since they are not true liquidity - // We subtract 1 because there is always 1 token deposited at the beginning: this will never be taken function freeLiquidity() public view override returns (uint256) { return super.totalAssets() - _calculateLockedProfits(); } @@ -113,8 +112,8 @@ contract Vault is IVault, ERC4626, ERC20Permit { uint256 supply = totalSupply(); uint256 shares = balanceOf(owner); // super.maxWithdraw but we leverage the fact of having already computed freeLiq which contains balanceOf() - // notice that shares are always at least 1 - return freeLiq.min(shares.mulDiv(freeLiq + netLoans + _calculateLockedLosses(), supply)); + // notice that shares and free liquidity are always at least 1, and freeLiq cannot be withdrawn + return (freeLiq - 1).min(shares.mulDiv(freeLiq + netLoans + _calculateLockedLosses(), supply)); } // Assets include netLoans but they are not available for withdraw @@ -131,9 +130,9 @@ contract Vault is IVault, ERC4626, ERC20Permit { // convertToShares using the already computed variables // if the assets the owner can theoretically withdraw are higher than the free liquidity - // we cap them with the convertToShares of the free liquidity + // we cap them with the convertToShares of the free liquidity - 1, which is always available if (assets >= freeLiquidityCache && assets > 0) { - maxRedeemCache = freeLiquidityCache.mulDiv(supply, totalAssetsCache); + maxRedeemCache = (freeLiquidityCache - 1).mulDiv(supply, totalAssetsCache); } return maxRedeemCache; @@ -147,9 +146,9 @@ contract Vault is IVault, ERC4626, ERC20Permit { return ERC4626.deposit(_assets, receiver); } - function maxDeposit(address) public view override(IERC4626, ERC4626) returns (uint256) { + function maxDeposit(address dummy) public view override(IERC4626, ERC4626) returns (uint256) { if (isLocked) return 0; - return type(uint256).max; + return super.maxDeposit(dummy); } function maxMint(address) public view override(IERC4626, ERC4626) returns (uint256) { From c91759430b53deb4d539070579fcbc8f530c2ad1 Mon Sep 17 00:00:00 2001 From: lsqrl Date: Tue, 19 Dec 2023 11:54:20 +0100 Subject: [PATCH 4/4] changed name of maxDeposit argument --- src/Vault.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vault.sol b/src/Vault.sol index a21ca537..e27bc56b 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -146,9 +146,9 @@ contract Vault is IVault, ERC4626, ERC20Permit { return ERC4626.deposit(_assets, receiver); } - function maxDeposit(address dummy) public view override(IERC4626, ERC4626) returns (uint256) { + function maxDeposit(address addr) public view override(IERC4626, ERC4626) returns (uint256) { if (isLocked) return 0; - return super.maxDeposit(dummy); + return super.maxDeposit(addr); } function maxMint(address) public view override(IERC4626, ERC4626) returns (uint256) {