On-chain price oracle · real-world asset price feeds
Price oracle for smart contracts: forex, gold, indices and stocks on-chain
Fetch a signed price report from our API and pass it to your contract. Our verifier contract checks the signature on-chain, so your contract can trust the price without trusting the messenger.
How it works
- Fetch a signed report:
GET /oracle/reportwith your API key. - Submit it in your transaction: our verifier contract checks the signature with
ecrecover. - Use the verified price, reject it if it is too old for your use case.
Reports are EIP-712 typed data PriceReport(bytes32 feedId,int256 price,uint64 timestamp,uint64 validUntil,uint8 marketStatus,uint32 spreadBps), bound to one chain and one verifier contract (no replay elsewhere) and accepted only until validUntil, about a minute after signing (no hoarding of favourable reports). Prices are the mid of bid and ask with 8 decimals; spreadBps is the bid-ask spread. When you submit a report in your own transaction, you pay that gas. Feeds in demand are also stored on-chain by our relayer from time to time (free to read), but always check a stored price's age.
Market hours
Every report states the market status: OPEN (a live price, at most 60 seconds old), CLOSED (the official last price before the close, the same for everyone, signed at any time while the market is closed) or HALTED (trading stopped). getPriceNoOlderThan only returns OPEN prices, so a closing price can never pass as live. Asset-backed tokens should settle orders placed while the market is closed at the next OPEN price (forward pricing, as funds and ETFs do).
Circuit breaker
Each feed can have a maximum allowed jump. A larger move is not stored: the feed pauses until we have checked it and re-opened it, so a wrong or manipulated price never reaches your contract.
Feeds
| Symbol | feedId |
|---|---|
| AUDUSD | 0x5577fa17b1be8ae0f1517fe1864ea534d6992f3721000cdb6585b11e7046d5ec |
| EURGBP | 0xeaff3420d70d388655273db9b4f3000962445a6f0ea23ec1f04d843e4dbae4b0 |
| EURUSD | 0x0730076c3c0f5da594eb635c5d5a22116233bdb2a9c2977279cc348b8b8ce917 |
| GBPUSD | 0xd30842147b60fe333c2700bcae5eee6815b8529847955d19522858806bca8217 |
| IEF | 0x309dab183458dd939568dca0d68bea45d34aa25d31fa47fc5f12030e8f227f4e |
| OILCash# | 0xe21b90a237aa0663c02092eca221a0098c6c477054a61d05ecd3692f6631f6ff |
| SHY | 0x78f1083af8e72ddefc2e66fad95680c38a953b3136f413c61ab40f99b72991bc |
| SPY | 0xcea23e8f56b5ac447ac08d57e0808e35f468a65f29bb659b3d48934481e7c67a |
| TLT | 0x4abd434857e07567d49487ccf888348e55d5cfc15498c09a9dbcba2820e40ee4 |
| USDCAD | 0xa36f6cf3195abb4e93bf276660acee423f78878e6cfdbad0eab36e455afca97e |
| USDCHF | 0xd85af9ebd6fec6b317a7ac07f6d6455c4fc915cc8e1cfe19471faca002582907 |
| USDJPY | 0x3d6c40e9d821c10e442cb3fc6cb1c589ffd8b906e734a1ab3b3f7d213ac395d6 |
| USDSGD | 0x8a1375bd16e33aff9be7037f83e076defe9df6efea6d39a51d0b08f17ac9bc6d |
| XAGUSD | 0x67107df7f0e95594d9bf8ad8840bbb2ff95617123c227e10aab4d161dd6959b6 |
| XAUUSD | 0xe21c86d8b6a127bfef214d88fdb0c279e55d27dd8c443733e46c8d3de3c98cd6 |
Networks
| Network | Chain ID | Verifier contract |
|---|---|---|
| Optimism | 10 | 0xaeF7f701acFB42267015D3F593ce17CF536Afd6B |
| Optimism Sepolia testnet | 11155420 | 0xBBf36E7Cd75494f28E02C5698EaD35fF4960a943 |
Signer address: 0x83fa2d8836525251FAd214A78A72038D0864f877. Machine-readable list: /oracle/feeds.
Integrate
1. Fetch a signed report
curl -H "Authorization: Bearer $API_KEY" \
"https://api.marketapi.app/v1/oracle/report?symbols=EURUSD&chain_id=10"
Returns report (the ABI-encoded tuple) and signature (65 bytes) per symbol. Your key needs the read:oracle scope.
2. Verify it in your contract
interface IMarketApiOracle {
struct PriceReport { bytes32 feedId; int256 price; uint64 timestamp; uint64 validUntil;
uint8 marketStatus; uint32 spreadBps; }
function submit(PriceReport calldata r, bytes calldata sig) external returns (bool); // verifies, stores if newer
function getPriceNoOlderThan(bytes32 feedId, uint256 maxAge)
external view returns (int256 price, uint64 timestamp); // OPEN and fresh only, else reverts
function getPrice(bytes32 feedId) external view
returns (int256 price, uint64 timestamp, uint8 marketStatus, uint32 spreadBps); // incl. CLOSED
}
contract MyVault {
IMarketApiOracle immutable oracle;
bytes32 constant EURUSD = keccak256("EURUSD");
constructor(IMarketApiOracle o) { oracle = o; }
function act(IMarketApiOracle.PriceReport calldata r, bytes calldata sig) external {
oracle.submit(r, sig); // reverts on a bad signature
(int256 price, ) = oracle.getPriceNoOlderThan(EURUSD, 60); // 8 decimals
// ... use price ...
}
}
Chainlink-style consumers can read a feed through the MarketApiFeedAdapter (latestRoundData(), AggregatorV3Interface).
Pricing
5 credits per signed report, paid from your normal API balance. Reading prices already stored on-chain is free.
Use cases for our on-chain price feeds
- Tokenized assets and RWA: price tokenized stocks, gold-backed tokens and other real-world assets with market-hours aware price data.
- DeFi lending and collateral: value forex- and commodity-backed collateral with fresh, verifiable prices and liquidation-safe staleness checks.
- Synthetic assets and on-chain FX: settle synthetic forex, index and commodity positions against signed reference prices.
- Stablecoins and payments: convert between currencies on-chain with live EUR/USD, GBP/USD and other forex rates.
Why a signed pull oracle?
Push oracles update on a schedule and someone pays gas for every update. With a pull oracle your contract fetches a fresh, cryptographically signed price exactly when it needs it: lower cost, fresher data and the same on-chain verification. Market status, bid-ask spread and expiry are part of every signed report.
Price oracle FAQ
What is a blockchain price oracle?
A price oracle brings off-chain market prices to smart contracts. Our oracle signs each price with an EIP-712 signature; your contract verifies it on-chain with ecrecover before using it, so the price cannot be forged in transit.
Which assets do the price feeds cover?
Forex pairs such as EUR/USD, precious metals such as gold (XAU/USD) and silver, stock indices and individual stocks, as published in the feed list on this page. Every feed has a fixed feedId (keccak256 of the symbol).
Is it suitable for RWA and tokenized assets?
Yes. Reports carry the market status (open, closed, halted), so tokenized stocks, commodities and other real-world assets can apply forward pricing outside market hours instead of trading on a stale price.
Is the oracle Chainlink-compatible?
Each feed can be read through an adapter that implements the Chainlink AggregatorV3Interface (latestRoundData), so existing DeFi code that reads Chainlink-style price feeds works with minimal changes.
How is manipulation prevented?
Reports expire within about a minute and must be newer than the stored price, a per-feed circuit breaker pauses a feed after an abnormal jump, and with several brokers the price is the median of independent sources with outliers removed.
Who pays the gas fees?
It is a pull oracle: your application submits the signed report in its own transaction and pays that gas. Our relayer also stores feeds that are in demand on-chain from time to time; reading stored prices is free, but check their age before use.
Which blockchains are supported?
Any EVM chain where the verifier contract is deployed, for example Ethereum, Base, Arbitrum, Optimism, BNB Smart Chain and Polygon and their testnets; the current list is shown above.
Recommended: the pull pattern (always-fresh prices)
Do not rely on the price already stored on-chain. Fetch a freshly signed report and submit it in the same transaction that uses it: your contract always sees the current price, and you pay only for your own updates.
// 1) off-chain: GET https://api.marketapi.app/v1/oracle/report?symbols=EURUSD&chain_id=CHAIN_ID (header: Authorization: Bearer KEY)
// 2) on-chain: verify, store and use the report atomically
interface IMarketApiOracle {
struct PriceReport { bytes32 feedId; int256 price; uint64 timestamp; uint64 validUntil; uint8 marketStatus; uint32 spreadBps; }
function submit(PriceReport calldata r, bytes calldata sig) external returns (bool);
function getPriceNoOlderThan(bytes32 feedId, uint256 maxAge) external view returns (int256 price, uint64 timestamp);
}
contract MyVault {
IMarketApiOracle immutable oracle;
bytes32 constant EURUSD = keccak256("EURUSD");
constructor(IMarketApiOracle o) { oracle = o; }
function settle(IMarketApiOracle.PriceReport calldata r, bytes calldata sig) external {
oracle.submit(r, sig); // ignored if an equal/newer price is stored
(int256 price, ) = oracle.getPriceNoOlderThan(EURUSD, 60); // reverts unless OPEN and fresh
// ... use price (8 decimals)
}
}
The report's report and signature fields from the API are exactly the two arguments of submit. Reports are valid for about a minute, so fetch them right before sending the transaction.
Risk notice. Prices are indicative market data from our source, not an execution venue. Smart contracts that move funds based on any oracle must check freshness, handle missing updates and cap the impact of a wrong price. Read the terms before relying on the oracle in production.