6.6 KiB
6.6 KiB
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
// 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)
// 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
// 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
# 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