Core Contracts

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

Race Condition in Chainlink Functions Requests

Summary

The contract can assign house prices to the wrong property because it doesn’t tie Chainlink request IDs to house IDs. If two requests are sent within 5 minutes, the second response might arrive first and overwrite the price for the wrong house.

Vulnerability Details

The RAACHousePriceOracle uses Chainlink Functions to fetch house prices. The process works in two steps:

  1. sendRequest sends a request with a houseId in args[0], stored in lastHouseId via _beforeFulfill.

  2. fulfillRequest processes the response and assigns the price to lastHouseId.

Chainlink Functions can take up to 5 minutes to respond (per docs). If two requests are sent before the first response arrives, lastHouseId gets overwritten by the second request. When responses come back, they’re processed in whatever order they arrive, not the order they were sent. For example:

  • Request 1: houseId = 1, sets lastHouseId = 1.

  • Request 2: houseId = 2, overwrites lastHouseId = 2.

  • Response 2 arrives first (price = 200), sets price of house 2.

  • Response 1 arrives second (price = 100), overwrites price of house 2 instead of house 1.

This mixes up prices because fulfillRequest uses lastHouseId without checking which request it belongs to.

https://docs.chain.link/chainlink-functions/resources/service-limits

RAACHousePriceOracle.sol

Impact

Wrong prices get assigned to houses. This messes up NFT valuations tied to those prices, leading to incorrect loan amounts. A borrower could get too much or too little based on a swapped price, costing money or breaking the system’s trust.

Tools Used

Manual review of RAACHousePriceOracle and BaseChainlinkFunctionsOracle code, plus Chainlink Functions docs on service limits.

Recommendations

  1. Map Request ID to House ID: Store houseId with each requestId in a mapping. Use it in fulfillRequest to set the right price.

    mapping(bytes32 => uint256) public requestToHouseId;
    function _beforeFulfill(string[] calldata args) internal override {
    lastHouseId = args[0].stringToUint();
    requestToHouseId[s_lastRequestId] = lastHouseId;
    }
    function _processResponse(bytes memory response) internal override {
    uint256 price = abi.decode(response, (uint256));
    uint256 houseId = requestToHouseId[s_lastRequestId];
    housePrices.setHousePrice(houseId, price);
    emit HousePriceUpdated(houseId, price);
    }
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!