Platform: Code4rena
Start Date: 04/03/2024
Pot Size: $88,500 USDC
Total HM: 31
Participants: 105
Period: 11 days
Judge: ronnyx2017
Total Solo HM: 7
Id: 342
League: ETH
Rank: 53/105
Findings: 3
Award: $112.78
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xjuan
Also found by: 0rpse, 0x175, 0xAlix2, 0xBugSlayer, 0xloscar01, Ali-_-Y, Arz, CaeraDenoir, JohnSmith, Ocean_Sky, SpicyMeatball, alix40, ayden, falconhoof, givn, iamandreiski, kinda_very_good, nmirchev8, nnez, novamanbg, stackachu, wangxx2026
17.3162 USDC - $17.32
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L744 https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L1083
Can't liquidate a loan if the loan owner doesn't want to be liquidated.
At the end of the liquidate
function, the protocol transfers disarmed
NFT to the loan owner.
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L744
// cleans up loan when it is closed because of replacement, repayment or liquidation // send the position in its current state to owner function _cleanupLoan(uint256 tokenId, uint256 debtExchangeRateX96, uint256 lendExchangeRateX96, address owner) internal { _removeTokenFromOwner(owner, tokenId); _updateAndCheckCollateral(tokenId, debtExchangeRateX96, lendExchangeRateX96, loans[tokenId].debtShares, 0); delete loans[tokenId]; >> nonfungiblePositionManager.safeTransferFrom(address(this), owner, tokenId); emit Remove(tokenId, owner); }
If the loan owner is a contract, it can revert the transaction inside the onERC721Received
callback.
Manual review
Let liquidated users to claim their NFTs instead of transferring them.
DoS
#0 - c4-pre-sort
2024-03-18T18:41:21Z
0xEVom marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-03-18T18:42:13Z
0xEVom marked the issue as duplicate of #54
#2 - c4-judge
2024-03-31T15:27:34Z
jhsagd76 marked the issue as satisfactory
🌟 Selected for report: JecikPo
Also found by: KupiaSec, SpicyMeatball, kennedy1030, kfx, linmiaomiao, t4sk
92.1136 USDC - $92.11
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Oracle.sol#L304-L305
Oracles where reference token has 18 decimals will not work in chainlink modes.
Let's check V3Oracle's _getReferenceTokenPriceX96
function.
function _getReferenceTokenPriceX96(address token, uint256 cachedChainlinkReferencePriceX96) internal view returns (uint256 priceX96, uint256 chainlinkReferencePriceX96) { ... if (usesChainlink) { uint256 chainlinkPriceX96 = _getChainlinkPriceX96(token); chainlinkReferencePriceX96 = cachedChainlinkReferencePriceX96 == 0 ? _getChainlinkPriceX96(referenceToken) : cachedChainlinkReferencePriceX96; >> chainlinkPriceX96 = (10 ** referenceTokenDecimals) * chainlinkPriceX96 * Q96 / chainlinkReferencePriceX96 / (10 ** feedConfig.tokenDecimals); ...
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Oracle.sol#L304
Line 304 will most likely overflow if referenceTokenDecimals = 18
, for example if the referenceToken
is DAI and the token
is WETH, the dividend will be 10 ** 18 * 3500 * 2 ** 96 * 2 ** 96 = 2,2 * 10 ** 79
which is greater than uint256 can handle.
Manual review
Use FullMath.mulDiv function to prevent intermediate value overflows.
Oracle
#0 - c4-pre-sort
2024-03-22T07:25:08Z
0xEVom marked the issue as duplicate of #409
#1 - c4-pre-sort
2024-03-22T07:25:16Z
0xEVom marked the issue as sufficient quality report
#2 - c4-judge
2024-03-31T14:51:10Z
jhsagd76 changed the severity to 2 (Med Risk)
#3 - c4-judge
2024-03-31T14:51:15Z
jhsagd76 marked the issue as satisfactory
🌟 Selected for report: kfx
Also found by: 0x175, 0xAlix2, 0xjuan, AMOW, Aymen0909, CaeraDenoir, Giorgio, JCN, JecikPo, JohnSmith, Norah, SpicyMeatball, alexander_orjustalex, atoko, erosjohn, falconhoof, givn, grearlake, jnforja, kinda_very_good, lanrebayode77, nmirchev8, shaka, web3Tycoon, zxriptor
3.3501 USDC - $3.35
https://github.com/code-423n4/2024-03-revert-lend/blob/main/src/V3Vault.sol#L696-L698
The user can frontrun liquidation transaction with one wei loan repayment, making it to revert with debtChanged
error.
When the liquidator calls liquidate
function he specifies loan debtShares
in parameters,
function liquidate(LiquidateParams calldata params) external override returns (uint256 amount0, uint256 amount1) { // liquidation is not allowed during transformer mode if (transformedTokenId > 0) { revert TransformNotAllowed(); } LiquidateState memory state; (state.newDebtExchangeRateX96, state.newLendExchangeRateX96) = _updateGlobalInterest(); uint256 debtShares = loans[params.tokenId].debtShares; >> if (debtShares != params.debtShares) { revert DebtChanged(); }
this value is then compared to actual debtShares
, if these values are not equal the liquidation fails. This allows the loan owner to evade liquidation by repaying dust amounts just before the liquidation call.
V3Vault.t.sol _testLiquidation https://github.com/code-423n4/2024-03-revert-lend/blob/main/test/integration/V3Vault.t.sol#L731-L747
function _testLiquidation(LiquidationType lType) internal { ... vm.prank(WHALE_ACCOUNT); USDC.approve(address(vault), liquidationCost - 1); (uint256 debtShares) = vault.loans(TEST_NFT); vm.prank(WHALE_ACCOUNT); vm.expectRevert("ERC20: transfer amount exceeds allowance"); vault.liquidate(IVault.LiquidateParams(TEST_NFT, debtShares, 0, 0, WHALE_ACCOUNT, "")); vm.prank(WHALE_ACCOUNT); USDC.approve(address(vault), liquidationCost); uint256 daiBalance = DAI.balanceOf(WHALE_ACCOUNT); uint256 usdcBalance = USDC.balanceOf(WHALE_ACCOUNT); + vm.startPrank(TEST_NFT_ACCOUNT); + USDC.approve(address(vault), 1); + vault.repay(TEST_NFT, 1, true); + vm.stopPrank(); vm.prank(WHALE_ACCOUNT); vault.liquidate(IVault.LiquidateParams(TEST_NFT, debtShares, 0, 0, WHALE_ACCOUNT, "")); ... }
Foundry
Consider adding a time cooldown between repayments.
DoS
#0 - c4-pre-sort
2024-03-18T18:13:53Z
0xEVom marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-03-18T18:14:36Z
0xEVom marked the issue as duplicate of #231
#2 - c4-pre-sort
2024-03-22T12:02:45Z
0xEVom marked the issue as duplicate of #222
#3 - c4-judge
2024-03-31T14:47:29Z
jhsagd76 changed the severity to 2 (Med Risk)
#4 - c4-judge
2024-03-31T16:05:58Z
jhsagd76 marked the issue as satisfactory