OracleRouter
0xacbf0cdd399229479a10d6aaabb5bc9d2db9fa41
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
2,913 bytes
ABI entries
27
9 read · 6 write
License
none
Contract information
- Address
- 0xacbf0cdd399229479a10d6aaabb5bc9d2db9fa41
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0x61b442d74A…6DE645fF53
- Creation tx
- 0xd7b28f48ae…c7dc76431e
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (9)
CL_STALENESS() → uint256
markStalenessSec() → uint64
marketOracles(bytes32) → address, uint16, uint16, bool
owner() → address
pendingOwner() → address
priceFeed() → address
readPrice(bytes32) → uint64
rthCloseMinutesUtc() → uint16
rthOpenMinutesUtc() → uint16
Events (3)
MarketConfiguredOwnershipTransferStartedOwnershipTransferred
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "priceFeed_",
"type": "address"
},
{
"internalType": "uint64",
"name": "markStalenessSec_",
"type": "uint64"
},
{
"internalType": "uint16",
"name": "openM",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "closeM",
"type": "uint16"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BadAggregatorDecimals",
"type": "error"
},
{
"inputs": [],
"name": "BadRthWindow",
"type": "error"
},
{
"inputs": [],
"name": "MarkOutOfBound",
"type": "error"
},
{
"inputs": [],
"name": "MarketNotConfigured",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "StaleMark",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "marketId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "address",
"name": "clAggregator",
"type": "address"
},
{
"indexed": false,
"internalType": "uint16",
"name": "boundBpsRth",
"type": "uint16"
},
{
"indexed": false,
"internalType": "uint16",
"name": "boundBpsOffHours",
"type": "uint16"
}
],
"name": "MarketConfigured",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [],
"name": "CL_STALENESS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "marketId",
"type": "bytes32"
},
{
"internalType": "address",
"name": "clAggregator",
"type": "address"
},
{
"internalType": "uint16",
"name": "boundBpsRth",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "boundBpsOffHours",
"type": "uint16"
}
],
"name": "configureMarket",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "markStalenessSec",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "marketOracles",
"outputs": [
{
"internalType": "address",
"name": "clAggregator",
"type": "address"
},
{
"internalType": "uint16",
"name": "boundBpsRth",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "boundBpsOffHours",
"type": "uint16"
},
{
"internalType": "bool",
"name": "configured",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "priceFeed",
"outputs": [
{
"internalType": "contract PriceFeed",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "marketId",
"type": "bytes32"
}
],
"name": "readPrice",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "rthCloseMinutesUtc",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rthOpenMinutesUtc",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint64",
"name": "s",
"type": "uint64"
}
],
"name": "setMarkStaleness",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "openM",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "closeM",
"type": "uint16"
}
],
"name": "setRthWindow",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IAggregatorV3} from "./interfaces/IAggregatorV3.sol";
import {PriceFeed} from "./PriceFeed.sol";
import {SessionLib} from "./libs/SessionLib.sol";
contract OracleRouter is Ownable2Step {
struct MarketOracle {
address clAggregator;
uint16 boundBpsRth;
uint16 boundBpsOffHours;
bool configured;
}
error StaleMark();
error MarkOutOfBound();
error MarketNotConfigured();
error ZeroAddress();
error BadAggregatorDecimals();
error BadRthWindow();
event MarketConfigured(bytes32 indexed marketId, address clAggregator, uint16 boundBpsRth, uint16 boundBpsOffHours);
uint256 public constant CL_STALENESS = 26 hours;
PriceFeed public immutable priceFeed;
uint64 public markStalenessSec;
uint16 public rthOpenMinutesUtc;
uint16 public rthCloseMinutesUtc;
mapping(bytes32 => MarketOracle) public marketOracles;
constructor(address priceFeed_, uint64 markStalenessSec_, uint16 openM, uint16 closeM) Ownable(msg.sender) {
if (priceFeed_ == address(0)) revert ZeroAddress();
priceFeed = PriceFeed(priceFeed_);
markStalenessSec = markStalenessSec_;
_setRthWindow(openM, closeM);
}
function configureMarket(bytes32 marketId, address clAggregator, uint16 boundBpsRth, uint16 boundBpsOffHours)
external
onlyOwner
{
if (clAggregator != address(0) && IAggregatorV3(clAggregator).decimals() != 8) {
revert BadAggregatorDecimals();
}
marketOracles[marketId] = MarketOracle(clAggregator, boundBpsRth, boundBpsOffHours, true);
emit MarketConfigured(marketId, clAggregator, boundBpsRth, boundBpsOffHours);
}
function setMarkStaleness(uint64 s) external onlyOwner {
markStalenessSec = s;
}
function setRthWindow(uint16 openM, uint16 closeM) external onlyOwner {
_setRthWindow(openM, closeM);
}
function _setRthWindow(uint16 openM, uint16 closeM) private {
if (openM >= closeM || closeM >= 1440) revert BadRthWindow();
rthOpenMinutesUtc = openM;
rthCloseMinutesUtc = closeM;
}
function readPrice(bytes32 marketId) external view returns (uint64) {
MarketOracle memory mo = marketOracles[marketId];
if (!mo.configured) revert MarketNotConfigured();
(uint64 mark, uint64 ts,) = priceFeed.prices(marketId);
if (mark == 0 || block.timestamp > uint256(ts) + markStalenessSec) revert StaleMark();
if (mo.clAggregator != address(0)) {
bool rth = SessionLib.isRth(block.timestamp, rthOpenMinutesUtc, rthCloseMinutesUtc)
&& !SessionLib.isBlend(block.timestamp, rthOpenMinutesUtc, rthCloseMinutesUtc);
uint16 boundBps = rth ? mo.boundBpsRth : mo.boundBpsOffHours;
if (boundBps != 0) {
try IAggregatorV3(mo.clAggregator).latestRoundData() returns (
uint80, int256 clAnswer, uint256, uint256 clUpdatedAt, uint80
) {
if (clAnswer > 0 && block.timestamp <= clUpdatedAt + CL_STALENESS) {
uint256 cl = uint256(clAnswer);
uint256 diff = mark > cl ? mark - cl : cl - uint256(mark);
if (diff * 10_000 > cl * boundBps) revert MarkOutOfBound();
}
} catch {
}
}
}
return mark;
}
}
Chain explorer7344msChain node90ms