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
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