-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bigsam - Liquidation fails to update the interest Rate when liquidation funds are sent to the treasury thus the next user uses an inflated index #401
Comments
request poc Seems related to #387 in terms of root cause |
PoC requested from @Tomiwasa0 Requests remaining: 14 |
++ address sam = address(3);
++ address dav = address(4);
function _generateLiquidationCondition() internal {
_mintAndApprove(alice, tokenA, mintAmountA, address(pool)); // alice 1000 tokenA
_mintAndApprove(sam, tokenA, mintAmountA, address(pool)); // alice 1000 tokenA
_mintAndApprove(bob, tokenB, mintAmountB, address(pool)); // bob 2000 tokenB
_mintAndApprove(dav, tokenA, mintAmountA, address(pool)); // bob 2000 tokenB
vm.startPrank(alice);
pool.supplySimple(address(tokenA), alice, supplyAmountA, 0); // 550 tokenA alice supply
vm.stopPrank();
vm.startPrank(sam);
pool.supplySimple(address(tokenA), sam, supplyAmountA, 0); // 550 tokenA alice supply
vm.stopPrank();
vm.startPrank(bob);
pool.supplySimple(address(tokenB), bob, supplyAmountB, 0); // 750 tokenB bob supply
vm.stopPrank();
vm.startPrank(alice);
pool.borrowSimple(address(tokenB), alice, borrowAmountB, 0); // 100 tokenB alice borrow
vm.stopPrank();
vm.startPrank(sam);
pool.borrowSimple(address(tokenB), sam, borrowAmountB, 0); // 100 tokenB alice borrow
vm.stopPrank();
vm.startPrank(bob);
pool.borrowSimple(address(tokenA), bob , 500e18, 0); // 100 tokenB alice borrow
vm.stopPrank();
// Get the current block timestamp
uint256 currentTime = block.timestamp;
// Set the block.timestamp to current time plus 100 seconds
vm.warp(currentTime + 1000);
assertEq(tokenB.balanceOf(alice), borrowAmountB);
oracleA.updateAnswer(0.45e8);
}
Updated Liquidation Function: function testLiquidationSimple2() external {
_generateLiquidationCondition();
(, uint256 totalDebtBase,,,,) = pool.getUserAccountData(alice, 0);
vm.startPrank(bob);
// vm.expectEmit(true, true, true, false);
// emit PoolEventsLib.LiquidationCall(address(tokenA), address(tokenB), pos, 0, 0, bob);
pool.liquidateSimple(address(tokenA), address(tokenB), pos, 100 ether);
vm.stopPrank();
(, uint256 totalDebtBaseNew,,,,) = pool.getUserAccountData(alice, 0);
assertTrue(totalDebtBase > totalDebtBaseNew);
// Get the current block timestamp
uint256 currentTime3 = block.timestamp;
// Set the block.timestamp to current time plus 100 seconds
vm.warp(currentTime3 + 500);
vm.startPrank(dav);
pool.supplySimple(address(tokenA), dav, 100e18, 0); // 550 tokenA alice supply
vm.stopPrank();
assertEq(pool.getBalanceRaw(address(tokenA), dav, 0).supplyShares, 99999784100498438999);
} Before Updating the index with Amount minted to tresury Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 67.39ms (15.32ms CPU time)
Ran 1 test suite in 2.36s (67.39ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/forge/core/pool/PoolLiquidationTests.t.sol:PoolLiquidationTest
[FAIL. Reason: assertion failed: 99999783033155331339 != 99999784100498438999] testLiquidationSimple1() (gas: 1610672)
|
This issue is low severity. It does not satisfy the criteria for medium. As shown by the POC, the difference in shares is 0.00000106% which is not large enough (0.01%) to be medium severity. |
I agree with @0xSpearmint. This issue does not meet the criteria for Medium severity:
I'm planning to invalidate the issue. |
To get the full impact of this kindly apply the appropriate fix to the bugs discovered in the liquidation function issue 473 and others, this creates a scenario also almost similar to issue 199 attacker get free funds, In evaluating the current system's functionality, issue 91 identified seven significant impacts resulting from improper handling, specifically regarding the liquidity and collateral management mechanisms:
|
@Tomiwasa0 I will agree with your comment and leave this issue as is. |
Bigsam
Medium
Liquidation fails to update the interest Rate when liquidation funds are sent to the treasury thus the next user uses an inflated index
Summary
A bug exists in the Zerolend liquidation process where the interest rate is not updated before transferring liquidation funds to the treasury. This omission leads to an inflated index being used by the next user when performing subsequent actions such as deposits, withdrawals, or borrowing, similar to the previously reported bug in the withdrawal function. As a result, the next user may receive fewer shares or incur an incorrect debt due to the artificially high liquidity rate.
Root Cause
Examples of update rate before transferring everywhere in the protocol to maintain Rate
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/SupplyLogic.sol#L69-L81
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/SupplyLogic.sol#L125-L146
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/BorrowLogic.sol#L88-L99
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/BorrowLogic.sol#L139-L158
The same process can be observed in Aave v 3.
Looking at the effect of updating rate
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/ReserveLogic.sol#L134-L182
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/periphery/ir/DefaultReserveInterestRateStrategy.sol#L98-L131
This rates are used to get the new index
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/ReserveLogic.sol#L225-L227
https://github.com/sherlock-audit/2024-06-new-scope/blob/main/zerolend-one/contracts/core/pool/logic/ReserveLogic.sol#L235-L237
Internal pre-conditions
No response
External pre-conditions
No response
Attack Path
During the liquidation process in Zerolend, when funds are transferred to the treasury as a liquidation protocol fee, the interest rate in the pool is not updated before the transfer. This failure results in the next user's interaction with the protocol (such as a deposit, withdrawal, or loan) being calculated based on an inflated liquidity rate. The inflated rate causes the user to receive fewer shares than they should or be charged an incorrect interest rate.
In contrast, Aave’s approach ensures that the interest rate is always updated when necessary and adjusted when funds are moved outside the system. Aave achieves this by transferring the funds inside the contract in the form of aTokens, which track liquidity changes, and since atokens are not burnt there is no need to update the interest rate accordingly in this case.
Zerolend, however, directly transfers funds out of the pool without recalculating the interest rate, which leads to inconsistencies in the index used by the next user.
Code Context:
In Zerolend's liquidation process, when a user is liquidated and the liquidation fee is sent to the treasury, the protocol transfers the funds directly without updating the interest rate.
As can be seen in the code, the liquidation protocol fee is transferred to the treasury, but no interest rate update takes place before the transfer. This results in an incorrect liquidity rate for the next user interaction.
Comparison with Aave:
Aave uses aTokens for transfers within the protocol, and the interest rate is updated accordingly when funds are moved, ensuring that the liquidity rate and index are always accurate. In Aave’s liquidation process, the aTokens are transferred to the treasury rather than removing liquidity directly from the pool.
In Aave’s implementation, the aToken system ensures that the liquidity and interest rates are intact based on the movement of funds and not transferring underlying assets.
Impact
PoC
No response
Mitigation
To address this issue, the interest rate must be updated before transferring any liquidation protocol fees to the treasury. This ensures that the system correctly accounts for the reduction in liquidity due to the transfer. This will be my last report here before transferring funds to the treasury also a bug was discovered before transferring. kind fix also. thank you for the great opportunity to audit your code i wish zerolend the very best in the future.
Suggested Fix:
In the liquidation logic, invoke the
updateInterestRates
function on the collateral reserve before transferring the funds to the treasury. This will ensure that the correct liquidity rate is applied to the pool before the funds are removed.Modified Code Example:
In this updated version, the interest rates are recalculated before the liquidation protocol fee is transferred to the treasury. This ensures that subsequent deposits, withdrawals, and loans use the correct liquidity rate and avoid discrepancies caused by an inflated index.
The text was updated successfully, but these errors were encountered: