Core Contracts

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

Emission rate is updated inaccurately

Summary

The calculated utilization rate is incorrect in term of decimals, which can cause emission rate update incorrect

Vulnerability Details

The function RAACMinter::getUtilizationRate() computes system utilization rate by using normalized debt from Lending Pool and total deposits in Stability Pool.

Here, the function lendingPool.getNormalizedDebt() returns value in term of RAY (1e27), when stabilityPool.getTotalDeposits() returns value in term of 1e18 (RToken's decimals). This can cause the returned value of getUtilizationRate() to be in term of 1e11. So, the function calculateNewEmissionRate() always goes into the if branch if (utilizationRate > utilizationTarget) because utilizationTarget's value range is (1, 100].

As a result, the emission rate will always increase until reached maximum emission rate.

function getUtilizationRate() internal view returns (uint256) {
@> uint256 totalBorrowed = lendingPool.getNormalizedDebt();
@> uint256 totalDeposits = stabilityPool.getTotalDeposits();
if (totalDeposits == 0) return 0;
@> return (totalBorrowed * 100) / totalDeposits;
}
function calculateNewEmissionRate() internal view returns (uint256) {
uint256 utilizationRate = getUtilizationRate();
uint256 adjustment = (emissionRate * adjustmentFactor) / 100;
@> if (utilizationRate > utilizationTarget) {
uint256 increasedRate = emissionRate + adjustment;
uint256 maxRate = increasedRate > benchmarkRate ? increasedRate : benchmarkRate;
return maxRate < maxEmissionRate ? maxRate : maxEmissionRate;
} else if (utilizationRate < utilizationTarget) {
uint256 decreasedRate = emissionRate > adjustment ? emissionRate - adjustment : 0;
uint256 minRate = decreasedRate < benchmarkRate ? decreasedRate : benchmarkRate;
return minRate > minEmissionRate ? minRate : minEmissionRate;
}
return emissionRate;
}

PoC

Add the test to file test/unit/core/pools/StabilityPool/StabilityPool.test.js

describe("Deposits", function () {
/// ...
it.only("incorrect utilization rate", async function(){
// @audit POC incorrect utilization rate
// Help function
async function utilizationRate() {
let borrowed = await lendingPool.getNormalizedDebt();
let deposits = await stabilityPool.getTotalDeposits();
return borrowed * 100n / deposits
}
// assume that there is deposits in Stability Pool
const depositAmount = ethers.parseEther("50");
await stabilityPool.connect(user2).deposit(depositAmount);
// emission interval passed
await ethers.provider.send("evm_increaseTime", [86400 + 1]);
await ethers.provider.send("evm_mine");
// borrow
const tokenId = 1;
const price = ethers.parseEther("100");
await raacHousePrices.setOracle(owner.address);
await raacHousePrices.setHousePrice(tokenId, price);
await crvusd.mint(user1.address, price)
await crvusd.connect(user1).approve(raacNFT.target, price)
await raacNFT.connect(user1).mint(tokenId, price);
await raacNFT.connect(user1).approve(lendingPool.target, tokenId);
await lendingPool.connect(user1).depositNFT(tokenId);
await lendingPool.connect(user1).borrow(price / 2n)
// update state
await lendingPool.connect(user1).updateState();
console.log(`UR ${await utilizationRate()}`)
})

Run the test and console shows:

StabilityPool
Core Functionality
Deposits
UR 2000136992
✔ incorrect utilization rate
1 passing (2s)

Impact

  • Emission rate is updated inaccurately such that it always increase until reached maximum rate

Tools Used

Manual

Recommendations

Scale the utilization rate accordingly

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

trtrth Submitter
4 months ago
inallhonesty Lead Judge
4 months ago
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACMinter::getUtilizationRate incorrectly mixes stability pool deposits with lending pool debt index instead of using proper lending pool metrics

Support

FAQs

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