Skip to main content
The publisher service is a Rust program that computes forward prices from Pyth spot data and submits them onchain. It runs continuously to keep forward price curves fresh for all active tenors. This page covers the publisher’s internals for operators and developers.
For a product-level overview of how forward pricing works, see Oracle & Forward Pricing.

Architecture

The publisher is a single-binary Rust service that:
  1. Connects to the Pyth Hermes API for real-time EUR/USD spot prices
  2. Computes forward prices using the interest rate parity formula
  3. Submits prices onchain via OracleModule.publishForwardRounds()
  4. Records fixing prices for matured timestamps via OracleModule.recordFixingPriceFromPyth()

Publisher Cycle

1

Fetch Spot Price

The publisher queries the Pyth Hermes REST API for the latest EUR/USD price. The raw Pyth price (int64 with exponent) is converted to 18-decimal precision.
2

Compute Forward Prices

For each enabled tenor (1D, 1W, 1M), the publisher:
  • Determines all active fixing timestamps (positions open for that tenor)
  • Computes the forward price: F = S * (1 + r * t / (365 days * 10,000))
  • Where r is the configured FORWARD_RATE_BPS (default: 0) and t is seconds to maturity
3

Submit Forward Rounds

All computed prices are batched into a single publishForwardRounds() transaction. Each price is keyed by (pairId, fixingTimestamp) and receives an incrementing round ID.
4

Record Fixing Prices

For any fixing timestamps that have passed (matured positions), the publisher submits recordFixingPriceFromPyth() with fresh Pyth update data. This records the settlement price.
5

Wait for Next Cycle

The publisher sleeps for the configured interval (default: 30 seconds) before repeating.

Safeguard Checks

Every publishForwardRound call passes through four onchain safeguard checks. All must pass for the price to be accepted.
CheckFormulaDefaultPurpose
Spacingblock.timestamp - lastPublishTime >= minForwardUpdateSpacing10sPrevent rapid-fire updates
Move Limit|newPrice - lastPrice| / lastPrice <= maxOracleMovePerUpdateBps200 bps (2%)Cap single-update price jumps
Deviation vs Prior|newPrice - lastAccepted| / lastAccepted <= maxDeviationVsPriorBps50 bps (0.5%)Limit cumulative drift
Deviation vs Close|newPrice - lastClose| / lastClose <= maxDeviationVsLastCloseBps150 bps (1.5%)Anchor to last settlement
If any safeguard check fails, the entire publishForwardRound call reverts. The publisher logs the failure and retries on the next cycle. Safeguard parameters are configured by the ORACLE_ADMIN_ROLE.

Configuration

Environment VariableDefaultDescription
RPC_URLEthereum RPC endpoint
PRIVATE_KEYPublisher wallet private key (must have PUBLISHER_ROLE)
FORWARD_RATE_BPS0Annualized forward rate in basis points
PUBLISH_INTERVAL_SECS30Seconds between publish cycles
ORACLE_MODULE_ADDRESSOracleModule contract address
PYTH_ADDRESSPyth contract address

Forward Round Clearing

Matured forward rounds can be cleared from storage to reclaim gas:
FunctionAccessBehavior
clearForwardRound(pairId, fixingTimestamp)PublicClears one round if matured and no open positions reference it
clearMaturedForwards(pairId)PublicClears all eligible matured rounds
clearAllForwards(pairId)OwnerClears all rounds (skips timestamps with open positions)
The keeper service automatically calls clearMaturedForwards after batch settlement. Manual clearing is only needed if you notice excessive storage from old rounds.

Pyth Price Conversion

Pyth delivers prices as int64 with an int32 exponent (typically -5 for FX). The protocol converts to 18-decimal precision:
if |expo| <= 18:
    price18 = pythPrice * 10^(18 - |expo|)
if |expo| > 18:
    price18 = pythPrice / 10^(|expo| - 18)
Example: Pyth price 108000 with exponent -5 becomes 1,080,000,000,000,000,000 (1.08 in 18 decimals).

Monitoring

The publisher logs structured output via the tracing crate:
  • INFO: Successful publish cycles, fixing price recordings
  • WARN: Safeguard check failures, stale spot prices
  • ERROR: RPC failures, transaction reverts
In M2, the publisher is operated by the protocol team. M3 plans include a multi-publisher model where multiple authorized publishers can submit prices, with the OracleModule selecting the median.

Oracle & Forward Pricing

Product-level pricing overview

Oracle Safeguards

Defense-in-depth oracle protection

Keeper Automation

Settlement and liquidation automation