Core Contracts

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

`LendingPool.sol#borrow`: calculation without considering fixed number's scaling

Summary

The calculation of value doesn't consider the scaling of the fixed number which leads to return an incorrect result.

Vulnerability Details

The collateralValue relies on the price feed from the Chainlink Functions, and its value is a fixed number scaled by 1e2. See "NFT Pricing Mechanism" below. The userTotalDebt, on the other hand, is a fixed number scaled by 1e18.

The check in L344 compares collateralValue between userTotalDebt without considering the scaling of the fixed number.

contract LendingPool is ILendingPool, Ownable, ReentrancyGuard, ERC721Holder, Pausable {
function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
// scaled by 1e2
uint256 collateralValue = getUserCollateralValue(msg.sender);
// ...
// debtBalance * index / ray, scaled by 1e18
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
/// @dev https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L344
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
}
function getUserCollateralValue(address userAddress) public view returns (uint256) {
UserData storage user = userData[userAddress];
uint256 totalValue = 0;
for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
uint256 price = getNFTPrice(tokenId);
totalValue += price;
}
return totalValue;
}
}

NFT Pricing Mechanism

According to Chainlink Functions' script, the price is a fixed number scaled by 1e2.

/// @dev https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/chainlink-api/house-price-api.js#L47-L48
// Multiply by 100 to convert the price to an integer
return Functions.encodeUint256(housePrice * 100)

The oracle fulfills the request and sets the price of NFT.

/// @dev https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/oracles/RAACHousePriceOracle.sol#L13
contract RAACHousePriceOracle is BaseChainlinkFunctionsOracle {
RAACHousePrices public housePrices;
uint256 public lastHouseId;
constructor(
address router,
bytes32 _donId,
address housePricesAddress
) BaseChainlinkFunctionsOracle(router, _donId) {
require(housePricesAddress != address(0), "HousePrices address must be set");
housePrices = RAACHousePrices(housePricesAddress);
}
function _beforeFulfill(string[] calldata args) internal override {
lastHouseId = args[0].stringToUint();
}
function _processResponse(bytes memory response) internal override {
uint256 price = abi.decode(response, (uint256));
housePrices.setHousePrice(lastHouseId, price);
emit HousePriceUpdated(lastHouseId, price);
}
}
/// @dev https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/primitives/RAACHousePrices.sol#L11
contract RAACHousePrices is Ownable {
// RAAC tokenId to house price in USD
mapping(uint256 => uint256) public tokenToHousePrice;
address public oracle;
uint256 public lastUpdateTimestamp;
function getLatestPrice(
uint256 _tokenId
) external view returns (uint256, uint256) {
return (tokenToHousePrice[_tokenId], lastUpdateTimestamp);
}
function setHousePrice(
uint256 _tokenId,
uint256 _amount
) external onlyOracle {
tokenToHousePrice[_tokenId] = _amount;
lastUpdateTimestamp = block.timestamp;
}
}

Impact

If users already holds a small amount of debt, they will be unable to borrow any additional assets.

Tools Used

Manual.

Recommendations

Before comparing fixed numbers, it is necessary to ensure that their scaling is consistent.

Updates

Lead Judging Commences

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

RAACHousePriceOracle::_processResponse doesn't account for the price scaling applied by Chainlink Functions

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

RAACHousePriceOracle::_processResponse doesn't account for the price scaling applied by Chainlink Functions

Support

FAQs

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

Give us feedback!