Protocols that try to integrate with Revert Land, expecting V3Vault to be ERC-4626 compliant, will multiple issues that may damage Revert Land's brand and limit Revert Land's growth in the market.
Proof of Concept
All official ERC-4626 requirements are on their official page. Non-compliant methods are listed below along with why they are not compliant and coded POCs demonstrating the issues.
V3Vault::maxDeposit and V3Vault::maxMint
As specified in ERC-4626, both maxDeposit and maxMint must return the maximum amount that would allow to be used and not cause a revert. Also, if the deposits or mints are disabled at current moment, it MUST return 0.
ERC4626::maxDeposit Non-compliant Requirements
MUST return the maximum amount of assets deposit would allow to be deposited for receiver and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary).
MUST factor in both global and user-specific limits, like if deposits are entirely disabled (even temporarily) it MUST return 0.
ERC4626::maxMint Non-compliant Requirements
MUST return the maximum amount of shares mint would allow to be deposited to receiver and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary).
MUST factor in both global and user-specific limits, like if mints are entirely disabled (even temporarily) it MUST return 0.
However, in both V3Vault::maxDeposit or V3Vault::maxMint return the amount that cause a revert in deposit or mint. This is because they do not check if the amount exceeded the daily lend limit and if this is a case, it will cause a revert inside _deposit function (where it used in both deposit and mint):
Furthermore, when dailyLendIncreaseLimitLeft == 0 that means the deposits and mints are temporarily disabled, while both V3Vault::maxDeposit and V3Vault::maxMint could return amounts that is more than 0. Based on ERC4626 requirements, it MUST return 0 in this case.
Test Case (Foundry)
To run the POC, copy-paste it into V3Vault.t.sol:
function testPOC_MaxDepositDoesNotReturnZeroWhenExceedsDailyLimit() public {
uint256 dailyLendIncreaseLimitMin = vault.dailyLendIncreaseLimitMin();
uint256 depositAmount = dailyLendIncreaseLimitMin;
vm.startPrank(WHALE_ACCOUNT);
USDC.approve(address(vault), depositAmount);
vault.deposit(depositAmount, WHALE_ACCOUNT);
//Should return 0 to comply to ERC-4626.
assertNotEq(vault.maxDeposit(address(WHALE_ACCOUNT)), 0);
USDC.approve(address(vault), 1);
vm.expectRevert(IErrors.DailyLendIncreaseLimit.selector);
vault.deposit(1, WHALE_ACCOUNT);
vm.stopPrank();
}
V3Vault::maxWithdraw and V3Vault::maxRedeem
As specified in ERC-4626, both maxWithdraw and maxRedeem must return the maximum amount that would allow to be transferred from owner and not cause a revert. Also, if the withdrawals or redemption are disabled at current moment, it MUST return 0.
ERC4626::maxWithdraw Non-compliant Requirements
MUST return the maximum amount of assets that could be transferred from owner through withdraw and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary).
MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0.
ERC4626::maxRedeem Non-compliant Requirements
MUST return the maximum amount of shares that could be transferred from owner through redeem and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary).
MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0.
However, in both V3Vault::maxWithdraw or V3Vault::maxRedeem return the amount that cause a revert in withdraw or redeem. This is because they do not check if the amount exceeded the current available balance in the vault and if this is a case, it will cause a revert inside _withdraw function (where it used in both withdraw and redeem):
function testPOC_MaxWithdrawDoesNotReturnZeroWhenExceedsAvailableBalance() external {
// maximized collateral loan
_setupBasicLoan(true);
uint256 amount = vault.maxRedeem(address(WHALE_ACCOUNT));
(,,, uint256 available,,,) = vault.vaultInfo();
//Should return available balance if it is less than owner balance to comply to ERC-4626.
assertNotEq(vault.maxRedeem(address(WHALE_ACCOUNT)), available);
vm.expectRevert(IErrors.InsufficientLiquidity.selector);
vm.prank(WHALE_ACCOUNT);
vault.redeem(amount, WHALE_ACCOUNT, WHALE_ACCOUNT);
}
Tools Used
Manual review.
Recommended Mitigation Steps
To address the non-compliance issues, the following changes are recommended:
The modified maxDeposit function now correctly calculates the maximum deposit amount by considering both the global lend limit and the daily lend increase limit. If the calculated maximum global deposit exceeds the daily lend increase limit, the function returns the daily lend increase limit to comply with ERC-4626 requirements.
Similarly, the modified maxMint ensures compliance with ERC-4626 by calculating the maximum mints amount for a given owner. It considers both the global lend limit and the daily lend increase limit as mentioned in maxDeposit.
The modified maxWithdraw function now correctly calculates the maximum withdrawal amount for a given owner. It ensures that the returned value does not exceed the available balance in the vault. If the available balance is greater than the owner's balance, it returns the owner's balance, otherwise it return the available balance to prevent potential reverts during withdrawal transactions. This adjustment aligns with ERC-4626 requirements by ensuring that withdrawals do not cause unexpected reverts and accurately reflect the available funds for withdrawal.
Similarly, the modified maxRedeem function ensures compliance with ERC-4626 by calculating the maximum redemption amount for a given owner. It considers both the owner's balance and the available liquidity in the vault as mentioned in maxWithdraw.
Lines of code
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L301-L309 https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L312-L320 https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L323-L326 https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L329-L331
Vulnerability details
Impact
Protocols that try to integrate with Revert Land, expecting
V3Vault
to be ERC-4626 compliant, will multiple issues that may damage Revert Land's brand and limit Revert Land's growth in the market.Proof of Concept
All official ERC-4626 requirements are on their official page. Non-compliant methods are listed below along with why they are not compliant and coded POCs demonstrating the issues.
V3Vault::maxDeposit
andV3Vault::maxMint
As specified in ERC-4626, both
maxDeposit
andmaxMint
must return the maximum amount that would allow to be used and not cause a revert. Also, if the deposits or mints are disabled at current moment, it MUST return 0.ERC4626::maxDeposit
Non-compliant RequirementsERC4626::maxMint
Non-compliant RequirementsHowever, in both
V3Vault::maxDeposit
orV3Vault::maxMint
return the amount that cause a revert indeposit
ormint
. This is because they do not check if the amount exceeded the daily lend limit and if this is a case, it will cause a revert inside_deposit
function (where it used in bothdeposit
andmint
):Furthermore, when
dailyLendIncreaseLimitLeft == 0
that means the deposits and mints are temporarily disabled, while bothV3Vault::maxDeposit
andV3Vault::maxMint
could return amounts that is more than 0. Based on ERC4626 requirements, it MUST return 0 in this case.Test Case (Foundry)
To run the POC, copy-paste it into
V3Vault.t.sol
:V3Vault::maxWithdraw
andV3Vault::maxRedeem
As specified in ERC-4626, both
maxWithdraw
andmaxRedeem
must return the maximum amount that would allow to be transferred from owner and not cause a revert. Also, if the withdrawals or redemption are disabled at current moment, it MUST return 0.ERC4626::maxWithdraw
Non-compliant RequirementsERC4626::maxRedeem
Non-compliant RequirementsHowever, in both
V3Vault::maxWithdraw
orV3Vault::maxRedeem
return the amount that cause a revert inwithdraw
orredeem
. This is because they do not check if the amount exceeded the current available balance in the vault and if this is a case, it will cause a revert inside_withdraw
function (where it used in bothwithdraw
andredeem
):Test Case (Foundry)
To run the POC, copy-paste it into
V3Vault.t.sol
:Tools Used
Manual review.
Recommended Mitigation Steps
To address the non-compliance issues, the following changes are recommended:
The modified
maxDeposit
function now correctly calculates the maximum deposit amount by considering both the global lend limit and the daily lend increase limit. If the calculated maximum global deposit exceeds the daily lend increase limit, the function returns the daily lend increase limit to comply with ERC-4626 requirements.Similarly, the modified
maxMint
ensures compliance with ERC-4626 by calculating the maximum mints amount for a given owner. It considers both the global lend limit and the daily lend increase limit as mentioned inmaxDeposit
.The modified
maxWithdraw
function now correctly calculates the maximum withdrawal amount for a given owner. It ensures that the returned value does not exceed the available balance in the vault. If the available balance is greater than the owner's balance, it returns the owner's balance, otherwise it return the available balance to prevent potential reverts during withdrawal transactions. This adjustment aligns with ERC-4626 requirements by ensuring that withdrawals do not cause unexpected reverts and accurately reflect the available funds for withdrawal.Similarly, the modified
maxRedeem
function ensures compliance with ERC-4626 by calculating the maximum redemption amount for a given owner. It considers both the owner's balance and the available liquidity in the vault as mentioned inmaxWithdraw
.Assessed type
Other