Core Contracts

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

`lastHouseId` Can Be Manipulated to Store Incorrect Data in `RAACHousePriceOracle` Contract

Summary

The contract RAACHousePriceOracle has a vulnerability where the variable lastHouseId is used to track house price updates but can be overwritten if multiple requests are made before responses are received. This creates a risk where incorrect prices may be assigned to houses, leading to data corruption.

Vulnerability Details

The lastHouseId variable is updated in the _beforeFulfill() function based on the requests:

function _beforeFulfill(string[] calldata args) internal override {
lastHouseId = args[0].stringToUint();
}

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/oracles/RAACHousePriceOracle.sol#L34C1-L36C6

However, if multiple requests are made before the corresponding responses arrive, lastHouseId gets overwritten, causing _processResponse() to update the wrong house:

function _processResponse(bytes memory response) internal override {
uint256 price = abi.decode(response, (uint256));
housePrices.setHousePrice(lastHouseId, price);
emit HousePriceUpdated(lastHouseId, price);
}

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/oracles/RAACHousePriceOracle.sol#L42C1-L46C6

Failure Scenario

  1. Sends Request A:

    • _beforeFulfill() sets lastHouseId = 101

    • Oracle request is made

  2. Sends Request B before Response A arrives:

    • _beforeFulfill() overwrites lastHouseId = 202

  3. Oracle sends Response A (for House ID 101):

    • _processResponse() updates House 202 instead of 101, storing the wrong price.

  4. Oracle sends Response B (for House ID 202):

    • _processResponse() updates House 202 correctly.

    • House 101 never gets updated.

Impact

  • Incorrect price updates: Houses may be assigned incorrect price data.

  • Data loss: Some houses may never receive the correct price update.

Tools Used

Manual Review

Recommendations

Instead of using a single global variable (lastHouseId), track house IDs per oracle request using a mapping:

- uint256 public lastHouseId;
+ mapping(bytes32 => uint256) private requestHouseIds;
- function _beforeFulfill(string[] calldata args) internal override {
- lastHouseId = args[0].stringToUint();
+ function _beforeFulfill(string[] calldata args, bytes32 requestId) internal override {
+ requestHouseIds[requestId] = args[0].stringToUint();
}
- function _processResponse(bytes memory response) internal override {
- uint256 price = abi.decode(response, (uint256));
- housePrices.setHousePrice(lastHouseId, price);
+ function _processResponse(bytes memory response, bytes32 requestId) internal override {
+ uint256 houseId = requestHouseIds[requestId];
+ require(houseId != 0, "Invalid request ID");
+ uint256 price = abi.decode(response, (uint256));
+ require(price > 0, "Invalid price data");
+ housePrices.setHousePrice(houseId, price);
emit HousePriceUpdated(houseId, price);
+ delete requestHouseIds[requestId];
}
Updates

Lead Judging Commences

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

Oracle Race Condition in RAACHousePriceOracle causes price misassignment between NFTs

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

Oracle Race Condition in RAACHousePriceOracle causes price misassignment between NFTs

Support

FAQs

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

Give us feedback!