Integration

Integrate

Quotes are signed off-chain and posted on demand. Anyone may relay a quote; only the signature is trusted. The contract is live on Robinhood Chain mainnet, an Arbitrum Orbit L2, so the same Solidity deploys to Arbitrum unchanged.

1 · Read the consumer interface

The function a liquidation path should call is getPriceIfTraded. It refuses to return a modelled weekend price at all, rather than returning one and hoping the caller checks.

interface IHoodOracle {
    struct Quote {
        uint128 price;           // 8 decimals
        uint64  confidenceBps;
        uint8   session;         // 0 REG 1 PRE 2 POST 3 CLOSED 4 HOLIDAY
        uint8   provenance;      // 0 TRADED 1 DERIVED 2 STALE
        uint8   sourceCount;
        uint64  maxDeviationBps;
        uint64  lastTradeTime;
        uint64  publishTime;
    }

    function getQuote(string calldata t) external view returns (Quote memory);
    function getPriceIfTraded(string calldata t, uint64 maxBps) external view returns (uint128);
    function getBandedPrice(string calldata t, bool lower) external view returns (uint128);
    function isLive(string calldata t, uint64 maxBps) external view returns (bool);
}

2 · Write policy that was previously impossible

contract LendingMarket {
    IHoodOracle public oracle;

    // Liquidation demands a live print inside 50bps. A weekend
    // quote reverts here rather than liquidating on a model.
    function liquidate(address user, string calldata ticker) external {
        uint128 px = oracle.getPriceIfTraded(ticker, 50);
        _liquidate(user, px);
    }

    // Collateral is always valued at the pessimistic edge of the
    // band, so a wide weekend band automatically reduces borrowing
    // power instead of being ignored.
    function collateralValue(string calldata ticker, uint256 qty)
        public view returns (uint256)
    {
        uint128 conservative = oracle.getBandedPrice(ticker, true);
        return (uint256(conservative) * qty) / 1e8;
    }

    // New borrows pause while the tape is shut.
    function borrow(string calldata ticker, uint256 amount) external {
        require(oracle.isLive(ticker, 100), "market shut");
        _borrow(msg.sender, amount);
    }
}

3 · Relay a quote on-chain

import { createWalletClient, http } from "viem";
import { arbitrumSepolia } from "viem/chains";

const r = await fetch("https://your-host/api/quote/HOOD").then((x) => x.json());

await wallet.writeContract({
  address: HOOD_ORACLE,
  abi: hoodOracleAbi,
  functionName: "postQuote",
  args: [
    r.quote.ticker,
    {
      price:           BigInt(Math.round(r.quote.price * 1e8)),
      confidenceBps:   BigInt(r.quote.confidenceBps),
      session:         r.quote.session,
      provenance:      r.quote.provenance,
      sourceCount:     r.quote.sourceCount,
      maxDeviationBps: BigInt(Math.round(r.quote.maxDeviationBps)),
      lastTradeTime:   BigInt(r.quote.lastTradeTime),
      publishTime:     BigInt(r.quote.publishTime),
    },
    r.signature,
  ],
});

Guards the contract enforces

GuardWhy
UnknownSignerOnly allow-listed signers are accepted.
NotNewerBlocks replay of an older quote, including re-posting a stale favourable price.
QuoteTooOld / QuoteFromFutureBounds clock skew in both directions. 60s of forward tolerance, configurable age limit backwards.
BandTooWideA quote past the publish ceiling is rejected rather than stored.
ZeroPriceUpstream returns Price: 0 for unsupported tickers. That must never reach storage.
InvalidEnumOut-of-range session or provenance bytes are refused, so a consumer's switch cannot fall through.
Not audited. This is an evaluation build. The contract has not been audited. The confidence model is calibrated, on two years of realised gaps with observed coverage between 93.4% and 96.4%, but a fit on ordinary days does not anticipate a structural break. Do not settle real money against it yet.

Contract source: contracts/HoodOracle.sol