The current implementation uses a single global timestamp to track the freshness of price data for all tokens, instead of maintaining individual timestamps per token. This design flaw can lead to situations where outdated price information is mistakenly considered current, potentially resulting in incorrect collateral valuations and financial imbalances. Implementing individual timestamps for each token would provide a more accurate and secure way to verify price recency.
The current implementation of the price oracle uses a single global timestamp (lastUpdateTimestamp) to track when any token's price is updated, rather than maintaining a separate timestamp for each token. This design has several drawbacks:
Lack of Granularity:
When prices for different tokens are updated at different times, a global timestamp fails to reflect the actual freshness of each token's price. A token with an outdated price may still appear current if another token’s price was updated recently.
Risk of Stale Data:
This setup can lead to scenarios where collateral valuations are based on stale price data, potentially causing borrowers to receive loans based on inflated collateral values or triggering unjust liquidations.
Collateral Misvaluation:
Outdated price data for individual tokens may lead to incorrect collateral valuations. This misvaluation can cause borrowers to secure loans that exceed the true value of their collateral, putting the protocol at risk.
Financial Imbalance:
Relying on stale data can result in over-borrowing or unjust liquidations. Inaccurate collateral assessments can destabilize the lending protocol, especially in volatile market conditions.
Exploitation Risk:
Malicious actors might exploit the global timestamp mechanism by manipulating update timings, thereby triggering scenarios where stale prices are used. This can lead to economically advantageous situations for attackers at the expense of the protocol.
Systemic Risk:
Widespread mispricing across tokens can accumulate into a significant systemic risk, undermining the overall stability and trustworthiness of the financial system built on the protocol
manual review
Implement Individual Timestamps:
Modify the price oracle to maintain a separate timestamp for each token rather than using a single global timestamp. This can be achieved by using a mapping (e.g., mapping(uint256 => uint256) tokenToLastUpdateTimestamp;) to store the last update time for each token's price.
Update Price Retrieval Logic:
Adjust the getLatestPrice function to return the token-specific timestamp along with the price, and ensure that any contract consuming this data (like the LendingPool) checks that the token's price is not stale based on its individual timestamp.
Enhanced Monitoring and Alerts:
Introduce events that log both the price update and its individual timestamp, allowing off-chain monitoring tools to detect any delays or inconsistencies in price updates.
Fallback Mechanisms:
Consider implementing fallback logic that either uses alternative data sources or temporarily restricts protocol operations for tokens with stale price data, minimizing the risk of misvaluation during market volatility.
`// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
@title RAACHousePrices
@dev Contract for managing house prices associated with RAAC tokens.
This contract allows an oracle to update prices and provides functions to retrieve the latest prices.
*/
contract RAACHousePrices is Ownable {
/// @notice Mapping from RAAC tokenId to house price in USD
mapping(uint256 => uint256) public tokenToHousePrice;
/// @notice Mapping from RAAC tokenId to the timestamp of the last price update
mapping(uint256 => uint256) public tokenToLastUpdateTimestamp;
address public oracle;
/// @notice Emitted when a price is updated
event PriceUpdated(uint256 tokenId, uint256 newPrice);
modifier onlyOracle() {
require(msg.sender == oracle, "RAACHousePrices: caller is not the oracle");
_;
}
/**
@notice Retrieves the latest price and update timestamp for a given token
@param _tokenId The ID of the RAAC token
@return The latest price and the timestamp of the last update
*/
function getLatestPrice(
uint256 _tokenId
) external view returns (uint256, uint256) {
return (tokenToHousePrice[_tokenId], tokenToLastUpdateTimestamp[_tokenId]);
}
constructor(address initialOwner) Ownable(initialOwner) {}
/**
@notice Allows the oracle to set the house price for a token
@param _tokenId The ID of the RAAC token
@param _amount The price to set for the house in USD
*/
function setHousePrice(
uint256 _tokenId,
uint256 _amount
) external onlyOracle {
tokenToHousePrice[_tokenId] = _amount;
tokenToLastUpdateTimestamp[_tokenId] = block.timestamp;
emit PriceUpdated(_tokenId, _amount);
}
/**
@notice Allows the owner to set the oracle address
@param _oracle The address of the oracle
*/
function setOracle(address _oracle) external onlyOwner {
oracle = _oracle;
}
}`
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.