If an user owns a large amount of NFTs/houses and deposits all his NFTs in the LendingPool
, calling LendingPool.initiateLiquidation
will revert due to OOG, and the user will be able to maintain an undercollateralized position in the system.
LendingPool.initiateLiquidation
will call LendingPool.calculateHealthFactor
which will call LendingPool.getUserCollateralValue
.
LendingPool.getUserCollateralValue
will iterate all the user NFTs. The issue is that if the user has a large amount of NFTs, the transaction will consume more gas than the block gas limit.
We can prove this by creating a POC where a user has 4000 NFTs, and when the value of each NFT drops to 1 wei, the user won't be able to get liquidated.
This POC can be pasted inside the "Liquidation" tests of test/unit/core/pools/LendingPool/LendingPool.test.js
. It should work out of the box without the need of any additional setup when running: npx hardhat test --grep "POC: user with a lot of NFTs won't be able to get liquidated"
. But please note it may take ~60s to run.
Logs:
What is the POC doing?
Before going into our POC, it's important to underestand what the the existing tests setup are doing
The price of the NFT token id 1 gets configurated with the value 100e18:
https://github.com/Cyfrin/2025-02-raac/blob/main/test/unit/core/pools/LendingPool/LendingPool.test.js#L121
user1 mints the NFT with token id 1:
https://github.com/Cyfrin/2025-02-raac/blob/main/test/unit/core/pools/LendingPool/LendingPool.test.js#L138
user2 deposits 1000e18 of liquidity in the pool:
https://github.com/Cyfrin/2025-02-raac/blob/main/test/unit/core/pools/LendingPool/LendingPool.test.js#L621
user1 deposits the NFT with token id 1:
https://github.com/Cyfrin/2025-02-raac/blob/main/test/unit/core/pools/LendingPool/LendingPool.test.js#L626
user1 borrows 80e18:
https://github.com/Cyfrin/2025-02-raac/blob/main/test/unit/core/pools/LendingPool/LendingPool.test.js#L628-L629
Our POC:
We are setting the price of tokens ids from 2 to 4000 to have the value 1e18.
user1 mints these NFTs and deposits in the pool.
user2 deposits 4000e18 in the pool.
user1 borrows 80% of ~4000e18, (4000 - 1) * 1e18 * 0.8
.
The price of all NFTs falls to 1 wei.
user1 won't be able to be liquidated, since calling initiateLiquidation
will consume 23M gas, which would exceed the block gas limit in the ethereum mainnet which is 21M.
If an account can't get liquidated it means the protocol can own assets which are worth less than the debt hold by the user. Even if the houses/NFTs go to zero, the protocol would never recover the user debt since any liquidation attempt during price decay would fail.
Manual review.
Limit the maximum number of NFTs/houses a single account can own. For example limiting to 1000 NFTs would prevent OOG and still leave a buffer for safety.
LightChaser L-36 and M-02 covers it.
LightChaser L-36 and M-02 covers it.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.