During Liquidation, Raac NFT will be sent to the StabilityPool contract and it will be permanently stuck
During liquidation, the owner or the manager of the StabilityPool contract can call the liquidateBorrower function to liquidate a borrower's debt.
The issue is that after NFT has been transferred, there is no way to withdraw it from the StabilityPool contract.
pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";
import {LendingPool} from "../contracts/core/pools/LendingPool/LendingPool.sol";
import {StabilityPool} from "../contracts/core/pools/StabilityPool/StabilityPool.sol";
import {RAACNFT} from "../contracts/core/tokens/RAACNFT.sol";
import {RAACHousePrices} from "contracts/core/primitives/RAACHousePrices.sol";
import {RToken} from "../contracts/core/tokens/RToken.sol";
import {DebtToken} from "../contracts/core/tokens/DebtToken.sol";
import {crvUSDToken} from "contracts/mocks/core/tokens/crvUSDToken.sol";
import {ILendingPool} from "contracts/interfaces/core/pools/LendingPool/ILendingPool.sol";
import "../contracts/core/tokens/DEToken.sol";
import "../contracts/core/tokens/RAACToken.sol";
import {RAACMinter} from "../contracts/core/minters/RAACMinter/RAACMinter.sol";
contract Audit_Test is Test {
LendingPool public lendingPool;
StabilityPool public stabilityPool;
RAACNFT public raacNFT;
RAACHousePrices public priceOracle;
RToken public rToken;
DebtToken public debtToken;
crvUSDToken public crvusd;
DEToken public deToken;
RAACToken public raacToken;
RAACMinter public raacMinter;
address owner = makeAddr("owner");
uint256 constant INITIAL_PRIME_RATE = 1e27;
function setUp() public {
vm.startPrank(owner);
crvusd = new crvUSDToken(owner);
crvusd.setMinter(owner);
priceOracle = new RAACHousePrices(owner);
priceOracle.setOracle(owner);
raacNFT = new RAACNFT(address(crvusd), address(priceOracle), owner);
rToken = new RToken("RToken", "RT", owner, address(crvusd));
debtToken = new DebtToken("DebtToken", "DT", owner);
deToken = new DEToken("DEToken", "DET", owner, address(rToken));
uint256 initialPrimeRate = 0.1e27;
lendingPool = new LendingPool(
address(crvusd),
address(rToken),
address(debtToken),
address(raacNFT),
address(priceOracle),
initialPrimeRate
);
raacToken = new RAACToken(owner, 1000, 1000);
stabilityPool = new StabilityPool(owner);
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
owner
);
stabilityPool.initialize(
address(rToken),
address(deToken),
address(raacToken),
address(raacMinter),
address(crvusd),
address(lendingPool)
);
rToken.setReservePool(address(lendingPool));
debtToken.setReservePool(address(lendingPool));
rToken.transferOwnership(address(lendingPool));
debtToken.transferOwnership(address(lendingPool));
deToken.setStabilityPool(address(stabilityPool));
deToken.transferOwnership(address(stabilityPool));
lendingPool.setStabilityPool(address(stabilityPool));
raacToken.setMinter(address(raacMinter));
raacToken.manageWhitelist(address(stabilityPool), true);
vm.stopPrank();
}
function test_POC() public {
uint256 HOUSE_PRICE = 50;
address alice = makeAddr("alice");
address bob = makeAddr("bob");
uint256 depositAmount = 100;
uint256 BORROW_AMOUNT = 50;
uint8 HOUSE_TOKEN_ID = 1;
vm.prank(owner);
priceOracle.setHousePrice(HOUSE_TOKEN_ID, HOUSE_PRICE);
crvusd.mint(alice, depositAmount);
crvusd.mint(bob, HOUSE_PRICE);
crvusd.mint(address(this), 1000 ether);
crvusd.transfer(address(stabilityPool), 1000 ether);
vm.startPrank(alice);
crvusd.approve(address(lendingPool), depositAmount);
lendingPool.deposit(depositAmount);
vm.stopPrank();
vm.startPrank(bob);
crvusd.approve(address(raacNFT), HOUSE_PRICE);
raacNFT.mint(HOUSE_TOKEN_ID, HOUSE_PRICE);
raacNFT.approve(address(lendingPool), HOUSE_TOKEN_ID);
lendingPool.depositNFT(HOUSE_TOKEN_ID);
assertEq(raacNFT.ownerOf(HOUSE_TOKEN_ID), address(lendingPool));
lendingPool.borrow(BORROW_AMOUNT);
lendingPool.initiateLiquidation(bob);
vm.stopPrank();
vm.warp(block.timestamp + 4 days);
vm.prank(owner);
stabilityPool.liquidateBorrower(bob);
assertEq(raacNFT.ownerOf(HOUSE_TOKEN_ID), address(stabilityPool));
}
}