Incorrect calculation of RAACMinter's utilization rate will push its emission rate to maximum rate for most of the cases.
Liquidity index is a ray, so it will have 27 decimals.
So unless total deposits is greater than 1B (i.e. 1e9) USD, utilization rate will always be greater than 100%
According to POC test, the protocol will reach to max emission rate within 15 days. And emissionrate will stay there until deposit amount is greater than 1.42B ( = 1B / 0.7)
pragma solidity ^0.8.19;
import "../lib/forge-std/src/Test.sol";
import {RToken} from "../contracts/core/tokens/RToken.sol";
import {DebtToken} from "../contracts/core/tokens/DebtToken.sol";
import {DEToken} from "../contracts/core/tokens/DEToken.sol";
import {RAACToken} from "../contracts/core/tokens/RAACToken.sol";
import {veRAACToken} from "../contracts/core/tokens/veRAACToken.sol";
import {RAACNFT} from "../contracts/core/tokens/RAACNFT.sol";
import {LendingPool} from "../contracts/core/pools/LendingPool/LendingPool.sol";
import {StabilityPool} from "../contracts/core/pools/StabilityPool/StabilityPool.sol";
import {RAACMinter} from "../contracts/core/minters/RAACMinter/RAACMinter.sol";
import {crvUSDToken} from "../contracts/mocks/core/tokens/crvUSDToken.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract RAACHousePricesMock {
mapping(uint256 => uint256) public prices;
function getLatestPrice(uint256 tokenId) external view returns (uint256, uint256) {
return (prices[tokenId], block.timestamp);
}
function setTokenPrice(uint256 tokenId, uint256 price) external {
prices[tokenId] = price;
}
function tokenToHousePrice(uint256 tokenId) external view returns (uint256) {
return prices[tokenId];
}
}
contract RAACMinterTest is Test {
RToken rtoken;
DebtToken debtToken;
RAACToken raacToken;
DEToken deToken;
veRAACToken veToken;
RAACNFT raacNft;
RAACMinter raacMinter;
crvUSDToken asset;
LendingPool lendingPool;
StabilityPool stabilityPool;
RAACHousePricesMock housePrice;
address depositor = makeAddr("depositor");
address borrower = makeAddr("borrower");
address user = makeAddr("user");
uint256 userAssetAmount = 10_000e18;
uint256 tokenId = 1;
uint256 initialBurnTaxRate = 50;
uint256 initialSwapTaxRate = 100;
uint256 initialPrimeRate = 0.1e27;
function setUp() external {
vm.warp(1e9);
asset = new crvUSDToken(address(this));
housePrice = new RAACHousePricesMock();
debtToken = new DebtToken("DebtToken", "DTK", address(this));
rtoken = new RToken("RToken", "RTK", address(this), address(asset));
raacNft = new RAACNFT(address(asset), address(housePrice), address(this));
lendingPool = new LendingPool(
address(asset), address(rtoken), address(debtToken), address(raacNft), address(housePrice), 0.1e27
);
rtoken.setReservePool(address(lendingPool));
debtToken.setReservePool(address(lendingPool));
deToken = new DEToken("DEToken", "DET", address(this), address(rtoken));
raacToken = new RAACToken(address(this), initialSwapTaxRate, initialBurnTaxRate);
stabilityPool = new StabilityPool(address(this));
stabilityPool.initialize(
address(rtoken), address(deToken), address(raacToken), address(this), address(asset), address(lendingPool)
);
lendingPool.setStabilityPool(address(stabilityPool));
raacMinter = new RAACMinter(address(raacToken), address(stabilityPool), address(lendingPool), address(this));
stabilityPool.setRAACMinter(address(raacMinter));
raacToken.setMinter(address(raacMinter));
veToken = new veRAACToken(address(raacToken));
raacToken.manageWhitelist(address(veToken), true);
deToken.setStabilityPool(address(stabilityPool));
uint256 depositorAmount = userAssetAmount * 10;
deal(address(asset), depositor, depositorAmount);
vm.startPrank(depositor);
asset.approve(address(lendingPool), depositorAmount);
lendingPool.deposit(depositorAmount);
uint256 rtokenBalance = rtoken.balanceOf(depositor);
rtoken.approve(address(stabilityPool), rtokenBalance);
stabilityPool.deposit(rtokenBalance);
vm.stopPrank();
}
function testEmissionRate() public {
_createDebt(userAssetAmount);
for (uint256 i; i < 16; i++) {
skip(1 days);
vm.roll(7200);
lendingPool.updateState();
raacMinter.updateEmissionRate();
}
assertEq(raacMinter.getEmissionRate(), raacMinter.maxEmissionRate());
for (uint256 i = 16; i < 100; i++) {
skip(1 days);
vm.roll(7200);
lendingPool.updateState();
raacMinter.updateEmissionRate();
assertEq(raacMinter.getEmissionRate(), raacMinter.maxEmissionRate());
}
}
function _createDebt(uint256 amount) internal {
uint256 price = amount * 8 / 10;
deal(address(asset), borrower, price);
housePrice.setTokenPrice(tokenId, price);
vm.startPrank(borrower);
asset.approve(address(raacNft), price);
raacNft.mint(tokenId, price);
raacNft.approve(address(lendingPool), tokenId);
lendingPool.depositNFT(tokenId);
tokenId++;
lendingPool.borrow(amount);
vm.stopPrank();
}
}
RAACToken will be minted faster than expected.
RAACMinter's utilization rate should use either one of the values.