Skip to content

Oracle and observations ​

An observation is one oracle price of MON or ETH that a market accepts. Each accepted observation settles one funding interval. This page gives the acceptance rules and the limits of the test oracle.

Source: contracts/src/Market.sol (_readOracle, _observe), contracts/src/TestOracle.sol, and packages/shared/src/oracle.ts.

Test oracle

One operator controls the oracle prices. The prices are not live MON or ETH prices. The oracle does not verify Pyth signatures, a confidence value, or the source sequence. Market.testOracle() always returns true. A production adapter and an independent review are necessary before a funded deployment.

The observation model is for testnet only

On testnet, one operator signs the price of each observation and sends it with a maker quote, at a fixed interval. This model keeps the markets active without an external feed. It is not the design for a market with real funds, where an independent price feed must supply the observations.

The observation schedule ​

A market has a fixed observation interval. The boundaries are multiples of that interval in Unix time. nextBoundary() returns the first boundary after the last accepted observation.

DeploymentIntervalSet by
Local chain, default60 sLOCAL_OBSERVATION_SECONDS, default 60
Local chain, Hedging demo10 sLOCAL_OBSERVATION_SECONDS=10
Monad testnet300 sA constant in scripts/monad-testnet.ts

The interval is a constructor input of the market. A deployed market cannot change it. The contract accepts an interval from 1 to 600 seconds.

How a price reaches the market ​

The market accepts update data in two forms:

FormDataUse
Signed pricePrice, timestamp, and a signature of the oracle signerAll current scripts and the keeper. No publish transaction is necessary.
Stored recordA timestamp of 32 bytes. The market reads the price that the owner stored with TestOracle.publish.An older path. The contract still supports it.

The oracle signer is the owner of the TestOracle contract, which is the deployer. The signature covers the chain ID, the oracle address, the feed ID, the price, and the timestamp.

Any address can submit a signed observation with submitObservation(updateData). The quoter can also attach it to a quote call. The demo loops use the second method, so the observation and the new quote arrive in one transaction.

Acceptance rules ​

The market accepts an observation only if all of these conditions are true:

  • The signature is from the oracle signer, or a stored record exists.
  • The price is from 1e6 to 1e30 in 18-decimal units.
  • The timestamp is later than the last accepted timestamp.
  • The timestamp is at or after the next boundary.
  • The timestamp is not in the future, and it is at most 5 seconds before the block time.

Data that fails a rule reverts without a state change.

What an accepted observation does ​

  1. The contract updates the mark from the book.
  2. It calculates the squared log return between the new price and the last price, and applies the cap of 0.0025.
  3. It adds the net payment for one contract to the funding index. See Funding.
  4. It saves the new price and time, and sets the next boundary.
  5. It saves the current mark as the charge of the next interval.
  6. It emits an Observation event with the raw return, the capped return, the payment, the old charge, and the new charge.

An observation does not loop over accounts. Each account takes its payment from the funding index when it is next used.

Late and missing observations ​

Gap since the last accepted observationResult
Up to the gap limit (3 intervals)One capped return between the two prices, and the fixed charge for the actual elapsed time
Above the gap limitBoth funding legs are skipped. The contract rebases to the new price. The mark stays frozen until the book gives a valid mark.

The oracle halts when the block time is later than the last accepted time plus the gap limit plus 5 seconds. During a halt, new orders, withdrawals, and liquidations wait. Cancellation stays available. A new observation clears the halt.

A permitted gap does not recover the missing price path. For two log returns, the square of the sum is not the sum of the squares. Thus an omitted observation can increase or decrease the payment.

Recovery is a test tool

The recover commands accept a current price after a long pause. They do not restore missing returns. A long gap skips funding. Do not treat a recovered test path as evidence of continuous hedging.

Price moves in the demo loops ​

LoopPrice at each observation
Local pnpm demo serveThe last price times a pending shock factor, or else calm noise
Testnet pnpm testnet serveThe last price, unchanged
Testnet pnpm testnet shock and reboundThe last price times exp(-0.04) or exp(0.03)

The calm noise of the local loop is a random log return with an annual volatility of 110% for MON and 60% for ETH. The loop limits it to 1% for each observation, so calm noise stays far below a shock. See Run the local exchange.

Open problems ​

  • The test oracle does not prove that the selected price is the first eligible source update. A caller could omit a price.
  • The source time and the receipt time have different cut-off rules.
  • The target design uses Pyth with one valid price for each boundary. This is not implemented, and its availability on Monad is not confirmed. See Target architecture.

Deployed on Monad testnet.