242 lines
6.6 KiB
Markdown
242 lines
6.6 KiB
Markdown
# Milestone 1: Pricing Oracle
|
|
|
|
> Build decentralized price feeds for SYNOR/USD conversion, enabling stable pricing for L2 services.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The Pricing Oracle aggregates price data from multiple sources to provide reliable SYNOR/USD rates for service pricing. This ensures users can estimate costs in familiar fiat terms while paying in SYNOR tokens.
|
|
|
|
---
|
|
|
|
## Components
|
|
|
|
### 1.1 Price Feed Aggregator
|
|
|
|
```rust
|
|
// crates/synor-economics/src/oracle/price_feed.rs
|
|
|
|
/// Price source for SYNOR/USD rates
|
|
pub enum PriceSource {
|
|
/// On-chain DEX (Synor DEX, Uniswap, etc.)
|
|
Dex { pool_address: Address, pair: TradingPair },
|
|
/// Centralized exchange API
|
|
Cex { exchange: String, api_endpoint: String },
|
|
/// Chainlink-style oracle
|
|
Chainlink { feed_address: Address },
|
|
/// Off-chain oracle (signed by trusted nodes)
|
|
OffChain { oracle_id: OracleId },
|
|
}
|
|
|
|
/// Aggregated price with metadata
|
|
pub struct AggregatedPrice {
|
|
/// SYNOR/USD price (6 decimal places)
|
|
pub price: u64,
|
|
/// Unix timestamp
|
|
pub timestamp: u64,
|
|
/// Sources used
|
|
pub sources: Vec<PriceSource>,
|
|
/// Confidence (0-100)
|
|
pub confidence: u8,
|
|
/// 24h high
|
|
pub high_24h: u64,
|
|
/// 24h low
|
|
pub low_24h: u64,
|
|
/// 24h volume
|
|
pub volume_24h: u64,
|
|
}
|
|
|
|
pub struct PriceFeedAggregator {
|
|
sources: Vec<PriceSource>,
|
|
cache: LruCache<TradingPair, AggregatedPrice>,
|
|
update_interval_ms: u64,
|
|
}
|
|
|
|
impl PriceFeedAggregator {
|
|
/// Get current price with aggregation across sources
|
|
pub async fn get_price(&self, pair: &TradingPair) -> Result<AggregatedPrice, OracleError> {
|
|
// Fetch from all sources in parallel
|
|
let prices = futures::future::join_all(
|
|
self.sources.iter().map(|s| self.fetch_from_source(s, pair))
|
|
).await;
|
|
|
|
// Filter out failures and outliers
|
|
let valid_prices = self.filter_outliers(prices);
|
|
|
|
// Calculate median price
|
|
let median = self.calculate_median(&valid_prices);
|
|
|
|
// Calculate confidence based on source agreement
|
|
let confidence = self.calculate_confidence(&valid_prices, median);
|
|
|
|
Ok(AggregatedPrice {
|
|
price: median,
|
|
timestamp: current_timestamp(),
|
|
sources: self.sources.clone(),
|
|
confidence,
|
|
..Default::default()
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
### 1.2 Time-Weighted Average Price (TWAP)
|
|
|
|
```rust
|
|
// crates/synor-economics/src/oracle/twap.rs
|
|
|
|
/// TWAP calculator for stable pricing
|
|
pub struct TwapCalculator {
|
|
/// Price observations
|
|
observations: VecDeque<PriceObservation>,
|
|
/// TWAP window (e.g., 1 hour)
|
|
window_seconds: u64,
|
|
/// Minimum observations required
|
|
min_observations: usize,
|
|
}
|
|
|
|
pub struct PriceObservation {
|
|
pub price: u64,
|
|
pub timestamp: u64,
|
|
pub cumulative_price: u128,
|
|
}
|
|
|
|
impl TwapCalculator {
|
|
/// Calculate TWAP over the configured window
|
|
pub fn calculate_twap(&self) -> Option<u64> {
|
|
if self.observations.len() < self.min_observations {
|
|
return None;
|
|
}
|
|
|
|
let oldest = self.observations.front()?;
|
|
let newest = self.observations.back()?;
|
|
|
|
let time_elapsed = newest.timestamp - oldest.timestamp;
|
|
if time_elapsed == 0 {
|
|
return None;
|
|
}
|
|
|
|
let cumulative_diff = newest.cumulative_price - oldest.cumulative_price;
|
|
Some((cumulative_diff / time_elapsed as u128) as u64)
|
|
}
|
|
|
|
/// Add new price observation
|
|
pub fn observe(&mut self, price: u64) {
|
|
let timestamp = current_timestamp();
|
|
let cumulative = self.observations
|
|
.back()
|
|
.map(|o| o.cumulative_price + (price as u128 * (timestamp - o.timestamp) as u128))
|
|
.unwrap_or(0);
|
|
|
|
self.observations.push_back(PriceObservation {
|
|
price,
|
|
timestamp,
|
|
cumulative_price: cumulative,
|
|
});
|
|
|
|
// Remove old observations outside window
|
|
let cutoff = timestamp - self.window_seconds;
|
|
while self.observations.front().map(|o| o.timestamp < cutoff).unwrap_or(false) {
|
|
self.observations.pop_front();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 1.3 Oracle Smart Contract
|
|
|
|
```rust
|
|
// crates/synor-economics/src/oracle/contract.rs
|
|
|
|
/// On-chain oracle contract for price storage
|
|
pub struct OracleContract {
|
|
/// Current price (6 decimals)
|
|
pub price: u64,
|
|
/// Last update timestamp
|
|
pub updated_at: u64,
|
|
/// Authorized updaters
|
|
pub updaters: HashSet<Address>,
|
|
/// TWAP values
|
|
pub twap_1h: u64,
|
|
pub twap_24h: u64,
|
|
/// Deviation threshold for updates (basis points)
|
|
pub deviation_threshold_bps: u16,
|
|
}
|
|
|
|
impl OracleContract {
|
|
/// Update price (only authorized updaters)
|
|
pub fn update_price(
|
|
&mut self,
|
|
caller: Address,
|
|
new_price: u64,
|
|
sources: Vec<SignedPriceAttestation>,
|
|
) -> Result<(), OracleError> {
|
|
// Verify caller is authorized
|
|
if !self.updaters.contains(&caller) {
|
|
return Err(OracleError::Unauthorized);
|
|
}
|
|
|
|
// Verify attestations
|
|
let verified_prices = self.verify_attestations(&sources)?;
|
|
|
|
// Check consensus among sources
|
|
let median = self.calculate_median(&verified_prices);
|
|
|
|
// Check deviation from current price
|
|
let deviation = self.calculate_deviation(self.price, median);
|
|
if deviation < self.deviation_threshold_bps {
|
|
return Ok(()); // No significant change
|
|
}
|
|
|
|
// Update price
|
|
self.price = median;
|
|
self.updated_at = current_timestamp();
|
|
|
|
// Emit event
|
|
emit_event(PriceUpdatedEvent { price: median, timestamp: self.updated_at });
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Tasks
|
|
|
|
- [ ] Implement PriceFeedAggregator with multiple source support
|
|
- [ ] Implement TWAP calculator with configurable windows
|
|
- [ ] Deploy oracle smart contract to testnet
|
|
- [ ] Set up price updater nodes
|
|
- [ ] Integrate with DEX liquidity pools
|
|
- [ ] Add CEX price feed support (optional)
|
|
- [ ] Implement price staleness detection
|
|
- [ ] Add circuit breaker for extreme volatility
|
|
|
|
---
|
|
|
|
## Validation Commands
|
|
|
|
```bash
|
|
# Run oracle tests
|
|
cargo test --package synor-economics --lib oracle
|
|
|
|
# Check price feed connectivity
|
|
synor oracle status
|
|
|
|
# Get current SYNOR/USD price
|
|
synor oracle price SYNOR/USD
|
|
|
|
# Check TWAP values
|
|
synor oracle twap SYNOR/USD --window 1h
|
|
```
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
- **Manipulation Resistance**: TWAP smooths out flash loan attacks
|
|
- **Source Diversity**: Multiple independent sources prevent single point of failure
|
|
- **Deviation Limits**: Large price swings require multiple confirmations
|
|
- **Staleness Detection**: Alert when prices haven't updated recently
|