The dust calculation attempts to compare the contract's CRVUSD balance with the total RToken supply to determine excess funds. However, the implementation contains two critical scaling issues:
High - Dust accumulation is completely irretrievable.
High - Current implementation means it will always happen.
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "contracts/core/tokens/DebtToken.sol";
import "contracts/core/tokens/RToken.sol";
import "contracts/core/tokens/RAACNFT.sol";
import "contracts/core/pools/LendingPool/LendingPool.sol";
import "contracts/core/primitives/RAACHousePrices.sol";
import "contracts/core/pools/StabilityPool/StabilityPool.sol";
import "contracts/core/tokens/RAACToken.sol";
import "contracts/core/tokens/DEToken.sol";
import "contracts/core/minters/RAACMinter/RAACMinter.sol";
import "contracts/libraries/math/PercentageMath.sol";
import "contracts/libraries/math/WadRayMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MockERC20 is ERC20 {
constructor() ERC20("Mock", "MCK") {}
function mint(address to, uint256 amount) public {
_mint(to, amount);
}
}
contract MasterTest is Test {
using PercentageMath for uint256;
using WadRayMath for uint256;
DebtToken public debtToken;
RToken public rToken;
RAACNFT public nft;
RAACToken public raacToken;
DEToken public deToken;
RAACHousePrices public priceOracle;
LendingPool public lendingPool;
StabilityPool public stabilityPool;
RAACMinter public raacMinter;
MockERC20 public mockCrvUSD;
address borrower = address(0x1);
address lender = address(0x2);
address lender2 = address(0x3);
address treasury = address(0x4);
address repairFund = address(0x5);
address protocolOwner = address(0x999);
function setUp() public {
vm.warp(1000 days);
vm.startPrank(protocolOwner);
mockCrvUSD = new MockERC20();
priceOracle = new RAACHousePrices(protocolOwner);
raacToken = new RAACToken(
protocolOwner,
100,
50
);
rToken = new RToken(
"RToken",
"RTKN",
protocolOwner,
address(mockCrvUSD)
);
debtToken = new DebtToken(
"DebtToken",
"DEBT",
protocolOwner
);
deToken = new DEToken(
"DEToken",
"DETKN",
protocolOwner,
address(rToken)
);
nft = new RAACNFT(
address(mockCrvUSD),
address(priceOracle),
protocolOwner
);
lendingPool = new LendingPool(
address(mockCrvUSD),
address(rToken),
address(debtToken),
address(nft),
address(priceOracle),
1e27
);
stabilityPool = new StabilityPool(protocolOwner);
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
protocolOwner
);
stabilityPool.initialize(
address(rToken),
address(deToken),
address(raacToken),
address(raacMinter),
address(mockCrvUSD),
address(lendingPool)
);
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
protocolOwner
);
debtToken.setReservePool(address(lendingPool));
rToken.setReservePool(address(lendingPool));
deToken.setStabilityPool(address(stabilityPool));
raacToken.setMinter(address(raacMinter));
lendingPool.setStabilityPool(address(stabilityPool));
mockCrvUSD.mint(borrower, 100_000e18);
mockCrvUSD.mint(lender, 500_000e18);
mockCrvUSD.mint(address(stabilityPool), 1_000_000e18);
vm.stopPrank();
vm.startPrank(borrower);
mockCrvUSD.approve(address(lendingPool), type(uint256).max);
mockCrvUSD.approve(address(nft), type(uint256).max);
nft.setApprovalForAll(address(lendingPool), true);
vm.stopPrank();
vm.startPrank(protocolOwner);
priceOracle.setOracle(protocolOwner);
priceOracle.setHousePrice(1, 100_000e18);
vm.stopPrank();
vm.startPrank(lender);
mockCrvUSD.approve(address(lendingPool), type(uint256).max);
lendingPool.deposit(500_000e18);
vm.stopPrank();
}
function testIncorrectDustCalculation() public {
uint256 depositAmount = 1000e18;
vm.startPrank(lender2);
mockCrvUSD.mint(lender2, depositAmount * 2);
mockCrvUSD.approve(address(lendingPool), depositAmount);
lendingPool.deposit(depositAmount);
vm.stopPrank();
uint256 initialLiquidityIndex = lendingPool.getNormalizedIncome();
console.log("Initial Liquidity Index:", initialLiquidityIndex);
vm.startPrank(borrower);
nft.mint(1, 100_000e18);
lendingPool.depositNFT(1);
lendingPool.borrow(500e18);
vm.stopPrank();
uint256 dustAmount = 100e18;
vm.startPrank(lender2);
mockCrvUSD.transfer(address(rToken), dustAmount);
vm.stopPrank();
vm.warp(block.timestamp + 365 days);
lendingPool.updateState();
uint256 currentLiquidityIndex = lendingPool.getNormalizedIncome();
console.log("Current Liquidity Index:", currentLiquidityIndex);
uint256 actualBalance = mockCrvUSD.balanceOf(address(rToken));
console.log("Actual CRVUSD Balance:", actualBalance);
uint256 rTokenSupply = rToken.totalSupply();
console.log("rToken Total Supply:", rTokenSupply);
uint256 calculatedDust = rToken.calculateDustAmount();
console.log("Calculated Dust:", calculatedDust);
console.log("Actual Dust Should Be:", dustAmount);
}