Summary
The RAACHousePrices
contract uses a single global lastUpdateTimestamp
variable to track price updates across all tokens, rather than maintaining individual timestamps for each token. This causes tokens to appear fresher than they actually are when prices are queried.
Vulnerability Details
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/primitives/RAACHousePrices.sol#L34-L37)
[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/primitives/RAACHousePrices.sol#L49-L55)
The contract tracks price updates using a single global timestamp:
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;
emit PriceUpdated(_tokenId, _amount);
}
When the oracle updates any token's price, it updates this global timestamp. This means if Token A is updated at t=100 and Token B at t=200, Token A will appear to have been updated at t=200 when queried via RAACHousePrices::getLatestPrice
.
Impact
Price staleness cannot be accurately tracked per token
LendingPool::getNFTPrice
could use stale prices thinking they're fresh
Direct financial impact through lending decisions based on stale data
Tools Used
Recommendations
Implement per-token timestamps:
+ mapping(uint256 => uint256) public tokenLastUpdateTimestamp;
function getLatestPrice(
uint256 _tokenId
) external view returns (uint256, uint256) {
- return (tokenToHousePrice[_tokenId], lastUpdateTimestamp);
+ return (tokenToHousePrice[_tokenId], tokenLastUpdateTimestamp[_tokenId]); // Return token-specific timestamp
}
function setHousePrice(uint256 _tokenId, uint256 _amount) external onlyOracle {
tokenToHousePrice[_tokenId] = _amount;
- lastUpdateTimestamp = block.timestamp;
+ tokenLastUpdateTimestamp[_tokenId] = block.timestamp;
emit PriceUpdated(_tokenId, _amount);
}