Vulnerability Details
getNFTPrice()function is used in LendingPoolcontract to get price of NFT:
function getNFTPrice(uint256 tokenId) public view returns (uint256) {
(uint256 price, uint256 lastUpdateTimestamp) = priceOracle.getLatestPrice(tokenId);
if (price == 0) revert InvalidNFTPrice();
return price;
}
In RAACHousePricescontract, ít return data that updated from oracle, that updated from oracle response that fulfilled by chainlink functions:
@> 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;
emit PriceUpdated(_tokenId, _amount);
}
RAACHousePriceOracle contract:
function _processResponse(bytes memory response) internal override {
uint256 price = abi.decode(response, (uint256));
@> housePrices.setHousePrice(lastHouseId, price);
emit HousePriceUpdated(lastHouseId, price);
}
But in chainlink functions, fulfill request can be paused in an emergency state link, lead to price cant be updated:
function fulfill(
bytes memory response,
bytes memory err,
uint96 juelsPerGas,
uint96 costWithoutFulfillment,
address transmitter,
FunctionsResponse.Commitment memory commitment
) external override returns (FunctionsResponse.FulfillResult resultCode, uint96) {
@> _whenNotPaused();
. . . . . .
}
In that case, old price will be used, instead of revert because there is no check on lastUpdateTimestamp. User can be unfairly liquidated due to that reason.
Impact
In some cases, user will be unfairly liquidated due to using stale price.
Recommendations
Add checking condition about lastUpdateTimestamp to make sure protocol do not use stale price.