// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/access/Ownable.sol"; import "../oracle/IAggregator.sol"; /** * @title MockPriceFeed * @notice Mock price feed for testing and development * @dev Implements IAggregator interface for Chainlink compatibility */ contract MockPriceFeed is IAggregator, Ownable { int256 private _latestAnswer; uint256 private _latestTimestamp; uint8 private _decimals; event PriceUpdated(int256 newPrice, uint256 timestamp); constructor(int256 initialPrice, uint8 decimals_) Ownable(msg.sender) { _latestAnswer = initialPrice; _latestTimestamp = block.timestamp; _decimals = decimals_; } /** * @notice Update price (owner only) * @param newPrice New price value */ function updatePrice(int256 newPrice) external onlyOwner { require(newPrice > 0, "MockPriceFeed: price must be positive"); _latestAnswer = newPrice; _latestTimestamp = block.timestamp; emit PriceUpdated(newPrice, block.timestamp); } /** * @notice Update price with custom timestamp * @param newPrice New price value * @param timestamp Custom timestamp */ function updatePriceWithTimestamp(int256 newPrice, uint256 timestamp) external onlyOwner { require(newPrice > 0, "MockPriceFeed: price must be positive"); require(timestamp <= block.timestamp, "MockPriceFeed: future timestamp"); _latestAnswer = newPrice; _latestTimestamp = timestamp; emit PriceUpdated(newPrice, timestamp); } /** * @notice Get latest answer * @return answer Latest price answer */ function latestAnswer() external view override returns (int256) { return _latestAnswer; } /** * @notice Get latest timestamp * @return timestamp Latest price timestamp */ function latestTimestamp() external view returns (uint256) { return _latestTimestamp; } /** * @notice Get decimals * @return decimals Number of decimals */ function decimals() external view override returns (uint8) { return _decimals; } /** * @notice Get latest round data * @return roundId Round ID (mock: always 1) * @return answer Latest price answer * @return startedAt Start timestamp (mock: same as latestTimestamp) * @return updatedAt Latest timestamp * @return answeredInRound Round ID (mock: always 1) */ function latestRoundData() external view override returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return ( 1, _latestAnswer, _latestTimestamp, _latestTimestamp, 1 ); } /** * @notice Get round data (mock: always returns latest) * @param roundId Round ID (ignored in mock) * @return roundId Round ID * @return answer Latest price answer * @return startedAt Start timestamp * @return updatedAt Latest timestamp * @return answeredInRound Round ID */ function getRoundData(uint80 roundId) external view override returns ( uint80, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return ( roundId, _latestAnswer, _latestTimestamp, _latestTimestamp, roundId ); } /** * @notice Get description * @return description Price feed description */ function description() external pure override returns (string memory) { return "Mock Price Feed"; } /** * @notice Update answer (for testing) * @param answer New answer value */ function updateAnswer(uint256 answer) external override onlyOwner { require(answer > 0, "MockPriceFeed: answer must be positive"); _latestAnswer = int256(answer); _latestTimestamp = block.timestamp; emit PriceUpdated(int256(answer), block.timestamp); } /** * @notice Get version * @return version Version number */ function version() external pure override returns (uint256) { return 1; } }