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_startprice > 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
| Condition | Outcome |
|---|---|
P_end > P_start | UP |
P_end = P_start | UP |
P_end < P_start | DOWN |
Key point: Equality resolves as UP. This eliminates ambiguity and slightly favors YES holders.
Supported Price Feeds
| Asset | Pyth Feed ID | Update Frequency |
|---|---|---|
| SOL/USD | H6ARHf6Y... | ~400ms |
| BTC/USD | GVXRSBjF... | ~400ms |
| ETH/USD | JBu1AL4o... | ~400ms |
| BONK/USD | 8ihFLu5F... | ~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
| Error | Cause | Resolution |
|---|---|---|
PriceNotAvailable | Pyth feed stale | Wait for update |
InvalidTimestamp | publish_time < boundary | Wait for valid price |
NegativePrice | price <= 0 | Wait for valid price |
ConfidenceTooHigh | Exceeds threshold | Wait for stable price |
SnapshotAlreadyCaptured | Already captured | No action needed |
Oracle Security
Potential Attacks
Protections
- Aggregation - Price from 70+ sources resists manipulation
- Signatures - Updates cryptographically verified
- Immutable Feed - Feed address set at market creation
- Confidence Bounds - Reject uncertain prices
- 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:
- New markets use new feed
- Existing markets continue with old feed
- 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.