Files
smom-dbis-138/contracts/wrapped-lp-public/WLPNAVOracle.sol

53 lines
1.8 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title WLPNAVOracle
* @notice Chainlink-style read interface for wLP / vault share price in USD (8 decimals) for lending adapters.
* @dev Keeper updates `latestAnswer` + `updatedAt`. Consumers must check `isStale()` before liquidations.
*/
contract WLPNAVOracle is AccessControl {
bytes32 public constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
/// @notice Price with 8 decimals (Chainlink USD convention).
int256 public latestAnswer;
uint256 public updatedAt;
uint256 public heartbeat;
bool public circuitBreaker;
event AnswerUpdated(int256 indexed answer, uint256 updatedAt, address indexed keeper);
event HeartbeatUpdated(uint256 heartbeat);
event CircuitBreakerSet(bool tripped);
constructor(address admin, uint256 heartbeat_) {
require(admin != address(0), "NAV: zero admin");
heartbeat = heartbeat_;
_grantRole(DEFAULT_ADMIN_ROLE, admin);
}
function setHeartbeat(uint256 h) external onlyRole(DEFAULT_ADMIN_ROLE) {
heartbeat = h;
emit HeartbeatUpdated(h);
}
function setCircuitBreaker(bool on) external onlyRole(DEFAULT_ADMIN_ROLE) {
circuitBreaker = on;
emit CircuitBreakerSet(on);
}
function submitAnswer(int256 answer) external onlyRole(KEEPER_ROLE) {
require(!circuitBreaker, "NAV: breaker");
latestAnswer = answer;
updatedAt = block.timestamp;
emit AnswerUpdated(answer, updatedAt, msg.sender);
}
function isStale() external view returns (bool) {
if (circuitBreaker) return true;
if (heartbeat == 0) return false;
return block.timestamp > updatedAt + heartbeat;
}
}