Core Contracts

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

User can’t withdraw his full balance in lending pool

Summary

We deposit, time has passed, liq.index growth, we withdraw but due to higher value, the amountUnderlying can't be deducted. It means that last user to withdraw will not be able to withdraw his full amount.

Vulnerability Details

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Test, console} from "lib/forge-std/src/Test.sol";
//Import which is neccessery for the LendingPool.sol
import "contracts/core/pools/LendingPool/LendingPool.sol";
import "contracts/core/tokens/RToken.sol";
import "contracts/core/tokens/DebtToken.sol";
import "contracts/core/tokens/RAACNFT.sol";
import "contracts/core/tokens/RAACToken.sol";
import "contracts/core/minters/RAACMinter/RAACMinter.sol";
import "contracts/core/pools/StabilityPool/StabilityPool.sol";
import "contracts/core/pools/StabilityPool/StabilityPool.sol";
interface ITokenERC20{
function approve(address spender, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract ArsenTest is Test{
//Actual, non-modifier contracts
RToken public rToken;
DebtToken public dToken;
RAACNFT public raacNFT;
RAACToken public raacToken;
//deploy minters
RAACMinter public raacMinter;
//Mock contracts
HousePrices public housePrices;
PriceOracle public priceOracle;
//deploy the Lending Pool
LendingPool public lendingPool;
//deploy Stability Pool
StabilityPool public stabilityPool;
address public attacker = address(8888);
address public user_1 = address(1111);
address public user_2 = address(2222);
address CRV_USD = address(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
string ETH_RPC_URL = "https://mainnet.infura.io/v3/2cef90f9f3f44a1fb5201d8547183d42";
function setUp() public{
uint256 ethFork = vm.createFork(ETH_RPC_URL);
vm.selectFork(ethFork);
housePrices = new HousePrices();
priceOracle = new PriceOracle();
rToken = new RToken(
"rToken",
"RTOKEN",
address(this), //init owner
CRV_USD //crvUsd addr
);
dToken = new DebtToken(
"Debt Token",
"DBTTOKEN",
address(this) //init owner
);
raacNFT = new RAACNFT(
CRV_USD, //token
address(housePrices), //house prices
address(this) //init owner
);
raacToken = new RAACToken(
address(this),
500, //initialSwapTaxRate
500 //initialBurnTaxRate
);
lendingPool = new LendingPool(
CRV_USD, //crvUsd addr
address(rToken), //rToken
address(dToken), //debtToken
address(raacNFT), //raacNFT
address(priceOracle),//priceOracle addr
100000000000000000000000000 //init prime rate
);
stabilityPool = new StabilityPool(address(this));
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
address(this)
);
stabilityPool.initialize(
address(rToken),
address(dToken),
address(raacToken),
address(raacMinter), //raacMinter
CRV_USD,
address(lendingPool)
);
//@audit TO DO - set the setParameter , like threshold, e.t.c for the Lending Pool
//set the reserve pool for RToken to be able to mint stuff
rToken.setReservePool(address(lendingPool));
//set the reserve pool for the DToken to be able to mint stuff
dToken.setReservePool(address(lendingPool));
}
//forge test --match-test test_deposit -vvvv
function test_deposit() public{
deal(CRV_USD, attacker, 1_000_000e18); //deal 200 crvUSD to the attacker
vm.startPrank(attacker);//start
ITokenERC20(CRV_USD).approve(address(lendingPool), 1_000_000e18);
lendingPool.deposit(1_000_000e18); //deposit 1_000_000e18 crvUSD into the pool
//check the balances after the deposit
assertEq(ITokenERC20(CRV_USD).balanceOf(address(rToken)), 1_000_000e18);
assertEq(rToken.balanceOf(attacker), 1_000_000e18);
vm.stopPrank();//stop
//---------------------------------
//check the liquidityIndex at the beggining.
console.log("The liquidity index after the first ever deposit is", lendingPool.getNormalizedIncome());
for(uint i = 1; i < 10; i++){ //activity spike in the protocol happens, to the liquidityIndex has increased.
address user = vm.addr(i);
deal(CRV_USD, user, 10_000e18);
vm.startPrank(user); //start
ITokenERC20(CRV_USD).approve(address(raacNFT), 10_000e18);
raacNFT.mint(i, 10_000e18);
raacNFT.approve(address(lendingPool), i);
lendingPool.depositNFT(i);
lendingPool.borrow(10_000e18);
//lendingPool.repay(10_000e18);
vm.stopPrank(); //stop
}
vm.warp(block.timestamp + 365 days); //some time has passed
lendingPool.updateState();
console.log("The liquidity index right before the withdrawal is", lendingPool.getNormalizedIncome());
console.log("The balanceOf user in the rToken contract is,", rToken.balanceOf(attacker));
console.log("User tries to withdraw it, but without success.");
vm.startPrank(attacker);//start
(uint256 userBalance) = rToken.balanceOf(attacker);
vm.expectRevert();
lendingPool.withdraw(2_000_000e18); //here we can provide any number, it will be still capped to balanceOf
vm.stopPrank();//stop
}
}
//@note MOCK CONTRACTS FOR THE POC PURPOSES ONLY
contract HousePrices{
function tokenToHousePrice(uint256 _tokenId) external view returns (uint256){
return 10_000e18;
}
}
contract PriceOracle{
function getLatestPrice(uint256 _tokenId) external view returns (uint256, uint256){
return(10_000e18, block.timestamp);
}
}

Impact

Funds stuck

Tools Used

Foundry

Recommendations

Ensure user can withdraw his funds

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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