Skip to main content

Oracle Integration

Seesaw uses Pyth Network as its exclusive oracle for price data. This document explains how prices are sampled, validated, and used for resolution.

Why Pyth?

Pyth provides:

  • Sub-second updates - Fresh prices every ~400ms
  • High fidelity - Aggregated from top exchanges
  • Confidence intervals - Know the uncertainty
  • On-chain verification - Cryptographically signed

Price Feed Structure

Each Pyth price update contains:

struct PriceUpdate {
    price: i64,           // Price in base units
    conf: u64,            // Confidence interval (±)
    expo: i32,            // Exponent (10^expo)
    publish_time: i64,    // Unix timestamp
    ema_price: i64,       // Exponential moving average
    ema_conf: u64,        // EMA confidence
}

Example

SOL/USD Price Update:
├── price: 14250000000
├── conf: 50000000
├── expo: -8
├── publish_time: 1704067200
└── Computed: $142.50 ± $0.50

Sampling Rules

Start Price Capture

Requirements:

  • publish_time >= t_start
  • price > 0
  • Not already captured
  • Valid Pyth signature

End Price Capture

Identical process, but:

  • publish_time >= t_end
  • Start price must exist

Timing Diagram

Time ─────────────────────────────────────────────────►
      │           │                    │
      ▼           ▼                    ▼
   t_start     P_start              t_end      P_end
      │        captured                │       captured
      │           │                    │          │
      ├───────────┼────────────────────┼──────────┤
      │  CREATED  │      TRADING       │ SETTLING │

Confidence Validation (Optional)

Markets can enforce confidence ratio limits:

valid = (conf / |price|) <= max_confidence_ratio

Example

Price: $142.50
Confidence: $0.50
Ratio: 0.50 / 142.50 = 0.0035 (0.35%)

If max_confidence_ratio = 0.01 (1%):
└── 0.35% <= 1% ✓ Valid

If max_confidence_ratio = 0.001 (0.1%):
└── 0.35% > 0.1% ✗ Rejected

This prevents resolution during extreme volatility or oracle degradation.

Resolution Logic

Resolution Rules

ConditionOutcome
P_end > P_startUP
P_end = P_startUP
P_end < P_startDOWN

Key point: Equality resolves as UP. This eliminates ambiguity and slightly favors YES holders.

Supported Price Feeds

AssetPyth Feed IDUpdate Frequency
SOL/USDH6ARHf6Y...~400ms
BTC/USDGVXRSBjF...~400ms
ETH/USDJBu1AL4o...~400ms
BONK/USD8ihFLu5F...~400ms

Immutability Guarantees

Once captured, oracle snapshots cannot be changed:

pub fn snapshot_start(ctx: Context<SnapshotStart>) -> Result<()> {
    let market = &mut ctx.accounts.market;

    // Reject if already captured
    require!(
        market.start_price.is_none(),
        SeesawError::SnapshotAlreadyCaptured
    );

    // Capture immutably
    market.start_price = Some(price);
    market.start_time = Some(publish_time);

    Ok(())
}

This ensures:

  • No retroactive price manipulation
  • Deterministic resolution
  • Auditability

Error Handling

ErrorCauseResolution
PriceNotAvailablePyth feed staleWait for update
InvalidTimestamppublish_time < boundaryWait for valid price
NegativePriceprice <= 0Wait for valid price
ConfidenceTooHighExceeds thresholdWait for stable price
SnapshotAlreadyCapturedAlready capturedNo action needed

Oracle Security

Potential Attacks

Protections

  1. Aggregation - Price from 70+ sources resists manipulation
  2. Signatures - Updates cryptographically verified
  3. Immutable Feed - Feed address set at market creation
  4. Confidence Bounds - Reject uncertain prices
  5. Timestamp Validation - Prevents stale/future prices

Operational Considerations

Pyth Maintenance Windows

Pyth may have brief maintenance. Markets handle this by:

  • Allowing snapshots within grace period
  • Crank operators retry until successful
  • No trades affected (only lifecycle operations)

Feed Deprecation

If Pyth deprecates a feed:

  1. New markets use new feed
  2. Existing markets continue with old feed
  3. Old feed remains available for settlement

Latency

Pyth Update Flow:
├── Exchange prices → Pyth publishers: ~100ms
├── Publisher aggregation: ~50ms
├── On-chain publish: ~400ms
└── Seesaw reads: ~1 slot (400ms)
Total: ~1 second from exchange

For most market durations (60s+), this latency is negligible.