Core Contracts

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

Incorrect Implementation of Debt Calculations

Summary

The ReserveLibrary contains errors in its implementation of getNormalizedDebt function. This function incorrectly calculate interest rates and return wrong values, which affects the entire protocol's interest rate model and economic calculations.

Vulnerability Details

The issue exists in ReserveLibrary.sol:

  1. getNormalizedDebt incorrectly returns the usage index instead of the total normalized debt (this is based on the use here):

function getNormalizedDebt(ReserveData storage reserve, ReserveRateData storage rateData)
internal
view
returns (uint256)
{
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return reserve.usageIndex; // @audit Wrong: should return total usage in underlying units
}
// @audit Wrong: should calculate total normalized debt, not just the index
return calculateCompoundedInterest(rateData.currentUsageRate, timeDelta).rayMul(reserve.usageIndex);
}

The function should return the total debt in underlying asset units, but instead it's returning scaled values that represent the interest rate index. This causes:

  1. Incorrect debt calculations throughout the protocol

  2. Wrong utilization rate calculations

  3. Improper interest rate updates

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";
import {ReserveLibrary} from "../contracts/libraries/pools/ReserveLibrary.sol";
import "../contracts/libraries/math/WadRayMath.sol";
import {console} from "forge-std/console.sol";
contract ReserveLibraryPoc is Test {
using WadRayMath for uint256;
using ReserveLibrary for ReserveLibrary.ReserveData;
using ReserveLibrary for ReserveLibrary.ReserveRateData;
ReserveLibrary.ReserveData reserve;
ReserveLibrary.ReserveRateData rateData;
function setUp() public {
reserve.liquidityIndex = 1.1e27;
reserve.usageIndex = 1.1e27;
reserve.totalUsage = 1000e18; // 1000 tokens of debt
reserve.lastUpdateTimestamp = uint40(block.timestamp);
rateData.currentUsageRate = 0.08e27; // 8% APY
}
function test_incorrect_normalized_debt() public {
// Move time forward
vm.warp(block.timestamp + 365 days);
uint256 normalizedDebt = ReserveLibrary.getNormalizedDebt(reserve, rateData);
// In time delta = 0 case, it returns totalUsage instead of usageIndex
vm.warp(block.timestamp); // Reset time to test time delta = 0 case
uint256 zeroTimeDebt = ReserveLibrary.getNormalizedDebt(reserve, rateData);
console.log("Normalized debt with time delta = 0: %s", zeroTimeDebt);
console.log("Usage index: %s", reserve.usageIndex);
// This will fail because it returns totalUsage instead of usageIndex
assertEq(zeroTimeDebt, reserve.usageIndex, "Should return usage index when time delta = 0");
}
}

Running the test shows:

[FAIL: Should return usage index when time delta = 0: 1191615774442408228571428569 != 1100000000000000000000000000] test_incorrect_normalized_debt()

Impact

  1. Total debt is = underreported

  2. Utilization rates are calculated incorrectly

  3. Interest rates are not properly adjusted

Tools Used

  • Manual code review

Recommendations

  1. Fix getNormalizedDebt to return total debt in underlying units:

function getNormalizedDebt(ReserveData storage reserve, ReserveRateData storage rateData)
internal
view
returns (uint256)
{
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
- return reserve.usageIndex;
+ return reserve.totalUsage.rayMul(reserve.usageIndex);
}
- return calculateCompoundedInterest(rateData.currentUsageRate, timeDelta).rayMul(reserve.usageIndex);
+ uint256 currentIndex = calculateCompoundedInterest(rateData.currentUsageRate, timeDelta).rayMul(reserve.usageIndex);
+ return reserve.totalUsage.rayMul(currentIndex);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

getNormalizedDebt returns totalUsage (amount) instead of usageIndex (rate) when timeDelta < 1, breaking interest calculations across the protocol

getNormalizedDebt doesn't return total debt but only the index, causing incorrect utilization and interest rate calculations

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

getNormalizedDebt returns totalUsage (amount) instead of usageIndex (rate) when timeDelta < 1, breaking interest calculations across the protocol

getNormalizedDebt doesn't return total debt but only the index, causing incorrect utilization and interest rate calculations

Support

FAQs

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

Give us feedback!