Core Contracts

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

A missing mapping between requestId and houseId may lead to incorrect house price settings.

Summary

The current implementation doesn't link the house of which price is requested to the requestId. This can lead to setting the house price to a value received from a different requestId.

Vulnerability Details

Owner can call BaseChainlinkFunctionsOracle::sendRequest to request the price of a houseId.

function sendRequest(
...
) external onlyOwner {
...
if (bytesArgs.length > 0) {
req.setBytesArgs(bytesArgs);
}
@> s_lastRequestId = _sendRequest(
req.encodeCBOR(),
subscriptionId,
callbackGasLimit,
donId
);
@> _beforeFulfill(args);
}

The s_lastRequestId is set to the Chainlink returned requestId. The _beforeFulfill hook sets the lastHouseId to the houseId for which the price is requested.

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

When the Chainlink fulfills the request the fulfillRequest is called:

function fulfillRequest(
@> bytes32 requestId,// @audit requestId not checked
bytes memory response,
bytes memory err
) internal override {
s_lastResponse = response;
s_lastError = err;
if (err.length == 0) {
if (response.length == 0) {
revert FulfillmentFailed();
}
_processResponse(response);
}
}

The _processResponse sets the price for the lastHouseId :

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

There's no 1:1 link between the requestId and the house of which price is updated.
The lastHouseId price can be set to the value returned from a different request.

Consider the following example:

-> the price of houseA is requested:

  • lastHouseId is set to houseA

  • lastRequestId is set to requestA

-> the price of houseB is requested:

  • lastHouseId is set to houseB

  • lastRequestId is set to requestB

The Chainlink FunctionRouter calls fulfillRequest to fulfill requestB first then the requestA :

  • the price of lastHouseId == houseB is set to priceB

  • the price of lastHouseId == houseB is set to priceA

    The houseB price is set to the wrong value.
    The houseA price is not updated.

According to Chainlink Functions Service docs, the maximum duration of a request is 5 minutes, increasing the chances of this type of scenario happening.

The maximum duration of a request is the time allowed for processing a
Chainlink Functions request from start to finish, covering all steps such
as reading from the chain, executing code, and posting back on chain.
If processing takes longer, the request times out,

Impact

The houses prices may be incorrectly set or even not updated.

Tools Used

Recommendations

Add a new mapping in BaseChainlinkFunctionsOracle.

mapping(uint256 houseId => bytes32 requestId) public requestToHouseId

Populate this mapping in sendRequest and use it in fulfillRequest to set the response price to the correct houseId.

Updates

Lead Judging Commences

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

Oracle Race Condition in RAACHousePriceOracle causes price misassignment between NFTs

inallhonesty Lead Judge 3 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.