diff --git a/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-01-PricingOracle.md b/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-01-PricingOracle.md new file mode 100644 index 0000000..faeac4c --- /dev/null +++ b/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-01-PricingOracle.md @@ -0,0 +1,242 @@ +# 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, + /// 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, + cache: LruCache, + update_interval_ms: u64, +} + +impl PriceFeedAggregator { + /// Get current price with aggregation across sources + pub async fn get_price(&self, pair: &TradingPair) -> Result { + // 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, + /// 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 { + 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
, + /// 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, + ) -> 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 diff --git a/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-03-BillingEngine.md b/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-03-BillingEngine.md new file mode 100644 index 0000000..d02b5d9 --- /dev/null +++ b/docs/PLAN/PHASE12-EconomicsBilling/01-Milestone-03-BillingEngine.md @@ -0,0 +1,483 @@ +# Milestone 3: Billing Engine + +> Generate invoices, process payments, and manage account credits. + +--- + +## Overview + +The Billing Engine transforms metered usage into invoices, processes SYNOR payments, and manages prepaid balances and credits. + +--- + +## Components + +### 3.1 Invoice Generator + +```rust +// crates/synor-economics/src/billing/invoice.rs + +/// Invoice for a billing period +pub struct Invoice { + /// Invoice ID + pub invoice_id: InvoiceId, + /// Account ID + pub account_id: AccountId, + /// Billing period + pub period_start: u64, + pub period_end: u64, + /// Line items + pub line_items: Vec, + /// Subtotal before discounts + pub subtotal_micro_synor: u64, + /// Applied discounts + pub discounts: Vec, + /// Credits applied + pub credits_applied: u64, + /// Total due + pub total_due_micro_synor: u64, + /// Invoice status + pub status: InvoiceStatus, + /// Due date + pub due_date: u64, + /// Payment transaction (if paid) + pub payment_tx: Option, + /// Created at + pub created_at: u64, +} + +pub struct LineItem { + pub service: ServiceType, + pub resource: ResourceType, + pub description: String, + pub quantity: u64, + pub unit: MeteringUnit, + pub unit_price_micro_synor: u64, + pub amount_micro_synor: u64, +} + +pub struct AppliedDiscount { + pub discount_type: DiscountType, + pub description: String, + pub amount_micro_synor: u64, +} + +pub enum DiscountType { + /// Volume discount based on usage tier + Volume { tier: u8 }, + /// Commitment discount (reserved capacity) + Commitment { commitment_id: CommitmentId }, + /// Promotional credit + Promotional { promo_code: String }, + /// Referral credit + Referral { referrer_id: AccountId }, +} + +pub enum InvoiceStatus { + Draft, + Finalized, + Sent, + Paid, + Overdue, + Voided, +} + +pub struct InvoiceGenerator { + metering: UsageAggregator, + pricing: PricingEngine, + discounts: DiscountEngine, +} + +impl InvoiceGenerator { + /// Generate invoice for an account + pub async fn generate_invoice( + &self, + account: &AccountId, + period_start: u64, + period_end: u64, + ) -> Result { + // Get usage summary + let usage = self.metering.get_usage_summary(account, period_start, period_end).await?; + + // Convert to line items + let line_items = self.usage_to_line_items(&usage); + + // Calculate subtotal + let subtotal = line_items.iter().map(|li| li.amount_micro_synor).sum(); + + // Apply discounts + let discounts = self.discounts.calculate_discounts(account, &line_items).await?; + let discount_total: u64 = discounts.iter().map(|d| d.amount_micro_synor).sum(); + + // Apply credits + let available_credits = self.get_available_credits(account).await?; + let credits_applied = std::cmp::min(available_credits, subtotal - discount_total); + + // Calculate total + let total_due = subtotal.saturating_sub(discount_total).saturating_sub(credits_applied); + + Ok(Invoice { + invoice_id: InvoiceId::new(), + account_id: account.clone(), + period_start, + period_end, + line_items, + subtotal_micro_synor: subtotal, + discounts, + credits_applied, + total_due_micro_synor: total_due, + status: InvoiceStatus::Draft, + due_date: period_end + 30 * 24 * 3600, // 30 days + payment_tx: None, + created_at: current_timestamp(), + }) + } +} +``` + +### 3.2 Payment Processor + +```rust +// crates/synor-economics/src/billing/payment.rs + +/// Payment processor for SYNOR token payments +pub struct PaymentProcessor { + /// Synor blockchain client + blockchain: SynorClient, + /// Billing contract address + billing_contract: Address, +} + +pub struct PaymentRequest { + pub invoice_id: InvoiceId, + pub amount_micro_synor: u64, + pub payer: Address, +} + +pub struct PaymentResult { + pub payment_id: PaymentId, + pub tx_id: TxId, + pub amount_micro_synor: u64, + pub timestamp: u64, + pub status: PaymentStatus, +} + +pub enum PaymentStatus { + Pending, + Confirmed, + Failed { reason: String }, +} + +impl PaymentProcessor { + /// Process payment for an invoice + pub async fn process_payment( + &self, + request: PaymentRequest, + ) -> Result { + // Verify invoice exists and is payable + let invoice = self.get_invoice(&request.invoice_id).await?; + if invoice.status != InvoiceStatus::Finalized && invoice.status != InvoiceStatus::Sent { + return Err(PaymentError::InvoiceNotPayable); + } + + // Build payment transaction + let tx = self.blockchain.build_transaction( + TransactionType::Transfer { + to: self.billing_contract, + amount: request.amount_micro_synor, + memo: format!("Invoice payment: {}", request.invoice_id), + }, + request.payer, + )?; + + // Submit and wait for confirmation + let tx_id = self.blockchain.submit_transaction(tx).await?; + let confirmation = self.blockchain.wait_for_confirmation(&tx_id).await?; + + // Update invoice status + self.mark_invoice_paid(&request.invoice_id, &tx_id).await?; + + Ok(PaymentResult { + payment_id: PaymentId::new(), + tx_id, + amount_micro_synor: request.amount_micro_synor, + timestamp: current_timestamp(), + status: PaymentStatus::Confirmed, + }) + } + + /// Set up auto-pay for an account + pub async fn setup_auto_pay( + &self, + account: &AccountId, + funding_source: Address, + max_amount_per_invoice: u64, + ) -> Result { + // Verify funding source has sufficient balance/allowance + // Store auto-pay configuration + Ok(AutoPayConfig { + account_id: account.clone(), + funding_source, + max_amount_per_invoice, + enabled: true, + }) + } +} +``` + +### 3.3 Credit System + +```rust +// crates/synor-economics/src/billing/credit.rs + +/// Account credit management +pub struct CreditManager { + storage: CreditStorage, +} + +pub struct AccountCredit { + pub account_id: AccountId, + pub balance_micro_synor: u64, + pub credits: Vec, +} + +pub struct CreditEntry { + pub credit_id: CreditId, + pub amount_micro_synor: u64, + pub remaining_micro_synor: u64, + pub source: CreditSource, + pub expires_at: Option, + pub created_at: u64, +} + +pub enum CreditSource { + /// Prepaid balance deposit + Prepaid { tx_id: TxId }, + /// Promotional credit + Promotional { campaign: String }, + /// Referral bonus + Referral { referred_by: AccountId }, + /// SLA credit (compensation) + SlaCredit { incident_id: String }, + /// Manual adjustment + Manual { reason: String, admin: Address }, +} + +impl CreditManager { + /// Add credits to an account + pub async fn add_credits( + &self, + account: &AccountId, + amount: u64, + source: CreditSource, + expires_at: Option, + ) -> Result { + let entry = CreditEntry { + credit_id: CreditId::new(), + amount_micro_synor: amount, + remaining_micro_synor: amount, + source, + expires_at, + created_at: current_timestamp(), + }; + + self.storage.add_credit(account, &entry).await?; + Ok(entry) + } + + /// Get available credit balance + pub async fn get_balance(&self, account: &AccountId) -> Result { + let credits = self.storage.get_credits(account).await?; + let now = current_timestamp(); + + let balance = credits.iter() + .filter(|c| c.expires_at.map(|e| e > now).unwrap_or(true)) + .map(|c| c.remaining_micro_synor) + .sum(); + + Ok(balance) + } + + /// Apply credits to an invoice + pub async fn apply_credits( + &self, + account: &AccountId, + amount: u64, + invoice_id: &InvoiceId, + ) -> Result { + let mut credits = self.storage.get_credits(account).await?; + let now = current_timestamp(); + + // Sort by expiration (FIFO for expiring credits) + credits.sort_by_key(|c| c.expires_at.unwrap_or(u64::MAX)); + + let mut remaining = amount; + let mut applied = 0u64; + + for credit in credits.iter_mut() { + if remaining == 0 { + break; + } + + // Skip expired credits + if credit.expires_at.map(|e| e <= now).unwrap_or(false) { + continue; + } + + let apply_amount = std::cmp::min(credit.remaining_micro_synor, remaining); + credit.remaining_micro_synor -= apply_amount; + remaining -= apply_amount; + applied += apply_amount; + + // Record usage + self.storage.record_credit_usage( + &credit.credit_id, + apply_amount, + invoice_id, + ).await?; + } + + Ok(applied) + } +} +``` + +### 3.4 Prepaid Balance + +```rust +// crates/synor-economics/src/billing/prepaid.rs + +/// Prepaid balance management (pay-as-you-go with deposits) +pub struct PrepaidManager { + credits: CreditManager, + blockchain: SynorClient, + deposit_address: Address, +} + +impl PrepaidManager { + /// Deposit SYNOR to prepaid balance + pub async fn deposit( + &self, + account: &AccountId, + amount: u64, + from: Address, + ) -> Result { + // Build deposit transaction + let tx = self.blockchain.build_transaction( + TransactionType::Transfer { + to: self.deposit_address, + amount, + memo: format!("Prepaid deposit: {}", account), + }, + from, + )?; + + // Submit and confirm + let tx_id = self.blockchain.submit_transaction(tx).await?; + self.blockchain.wait_for_confirmation(&tx_id).await?; + + // Add to credits + self.credits.add_credits( + account, + amount, + CreditSource::Prepaid { tx_id: tx_id.clone() }, + None, // Prepaid credits don't expire + ).await?; + + Ok(DepositResult { + tx_id, + amount, + new_balance: self.credits.get_balance(account).await?, + }) + } + + /// Check if account has sufficient balance + pub async fn has_sufficient_balance( + &self, + account: &AccountId, + required: u64, + ) -> Result { + let balance = self.credits.get_balance(account).await?; + Ok(balance >= required) + } + + /// Deduct from prepaid balance (for real-time billing) + pub async fn deduct( + &self, + account: &AccountId, + amount: u64, + reason: &str, + ) -> Result { + let balance = self.credits.get_balance(account).await?; + + if balance < amount { + return Err(PrepaidError::InsufficientBalance { + available: balance, + required: amount, + }); + } + + // Create a temporary invoice for the deduction + let invoice_id = InvoiceId::new_instant(); + self.credits.apply_credits(account, amount, &invoice_id).await?; + + Ok(DeductResult { + amount_deducted: amount, + remaining_balance: self.credits.get_balance(account).await?, + }) + } +} +``` + +--- + +## Tasks + +- [ ] Implement Invoice data model and generator +- [ ] Build PaymentProcessor for SYNOR payments +- [ ] Implement CreditManager for account credits +- [ ] Build PrepaidManager for deposit-based billing +- [ ] Create billing smart contract +- [ ] Implement auto-pay functionality +- [ ] Add invoice email/notification system +- [ ] Build billing dashboard API +- [ ] Implement usage alerts and spending limits + +--- + +## Validation Commands + +```bash +# Check account balance +synor billing balance + +# Get current invoice +synor billing invoice --current + +# View invoice history +synor billing invoices --limit 10 + +# Add prepaid balance +synor billing deposit 100 SYNOR + +# Set up auto-pay +synor billing auto-pay enable --max 500 + +# Export usage report +synor billing export --format csv --period 2026-01 +``` + +--- + +## API Endpoints + +``` +GET /api/v1/billing/balance - Get account balance +GET /api/v1/billing/invoices - List invoices +GET /api/v1/billing/invoices/:id - Get invoice details +POST /api/v1/billing/invoices/:id/pay - Pay invoice +GET /api/v1/billing/usage - Get usage summary +POST /api/v1/billing/deposit - Deposit prepaid balance +GET /api/v1/billing/credits - List credits +POST /api/v1/billing/auto-pay - Configure auto-pay +``` diff --git a/docs/PLAN/PHASE12-EconomicsBilling/README.md b/docs/PLAN/PHASE12-EconomicsBilling/README.md new file mode 100644 index 0000000..6bab0e0 --- /dev/null +++ b/docs/PLAN/PHASE12-EconomicsBilling/README.md @@ -0,0 +1,187 @@ +# Phase 12: Economics & Billing + +> **Mission**: Build a comprehensive billing infrastructure to monetize Synor's L2 services (Storage, Hosting, Database, Compute) with real-time metering, pricing oracles, and transparent cost management. + +--- + +## Executive Summary + +Phase 12 creates the economic backbone for Synor's service ecosystem: +- **Real-time Metering**: Track resource usage across all L2 services +- **Pricing Oracles**: Dynamic SYNOR/USD price feeds for stable pricing +- **Billing Engine**: Usage-based invoicing and payment processing +- **Cost Calculator**: CLI and API tools for cost estimation +- **Revenue Distribution**: Automatic fee distribution to stakeholders + +--- + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ SYNOR ECONOMICS LAYER │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ USER INTERFACE LAYER │ │ +│ ├──────────────┬──────────────┬──────────────┬──────────────┬────────────┤ │ +│ │ Billing │ Cost │ Usage │ Payment │ Admin │ │ +│ │ Dashboard │ Calculator │ Reports │ Portal │ Console │ │ +│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ BILLING ENGINE LAYER │ │ +│ ├──────────────┬──────────────┬──────────────┬──────────────┬────────────┤ │ +│ │ Invoice │ Payment │ Credit │ Prepaid │ SLA │ │ +│ │ Generator │ Processor │ System │ Balance │ Credits │ │ +│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ METERING LAYER │ │ +│ ├──────────────┬──────────────┬──────────────┬──────────────┬────────────┤ │ +│ │ Storage │ Hosting │ Database │ Compute │ Network │ │ +│ │ Meter │ Meter │ Meter │ Meter │ Meter │ │ +│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ PRICING LAYER │ │ +│ ├──────────────┬──────────────┬──────────────┬──────────────┬────────────┤ │ +│ │ Price │ Spot │ Reserved │ Discount │ Tiered │ │ +│ │ Oracle │ Market │ Pricing │ Engine │ Pricing │ │ +│ └──────────────┴──────────────┴──────────────┴──────────────┴────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────┐ │ +│ │ SYNOR L1 BLOCKCHAIN (Smart Contracts) │ │ +│ └─────────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Milestones + +| Milestone | Description | Progress | +|-----------|-------------|----------| +| 1 | Pricing Oracle | 0% | +| 2 | Metering Service | 0% | +| 3 | Billing Engine | 0% | + +--- + +## Fee Distribution Model + +``` +Transaction Fees: +├── 10% → Burn (deflationary) +├── 60% → Stakers (rewards) +├── 20% → Community Pool (treasury) +└── 10% → Miners/Validators + +L2 Service Fees (Storage, Hosting, Database, Compute): +├── 70% → Node Operators +├── 20% → Protocol Treasury +└── 10% → Burn +``` + +--- + +## Service Pricing Summary + +### Storage L2 +| Resource | Unit | Price (SYNOR) | +|----------|------|---------------| +| Storage | GB/month | 0.02 | +| Retrieval | GB | 0.01 | +| Deal creation | per deal | 0.001 | + +### Hosting +| Resource | Unit | Price (SYNOR) | +|----------|------|---------------| +| Bandwidth | GB | 0.05 | +| Custom domain | month | 0.50 | +| SSL certificate | month | FREE | + +### Database L2 +| Resource | Unit | Price (SYNOR) | +|----------|------|---------------| +| Storage | GB/month | 0.10 | +| Queries | 1M | 0.01 | +| Vector search | 1M | 0.05 | + +### Compute L2 +| Resource | Unit | Price (SYNOR) | +|----------|------|---------------| +| CPU | core/hour | 0.02 | +| GPU (RTX 4090) | hour | 0.50 | +| Memory | GB/hour | 0.005 | +| Serverless | 1M invocations | 0.20 | + +--- + +## Implementation Timeline + +### Week 1-2: Pricing Oracle +- SYNOR/USD price aggregation from DEXes +- Time-weighted average pricing (TWAP) +- Oracle smart contract deployment + +### Week 3-4: Metering Service +- Real-time usage tracking for all L2 services +- Event stream processing with Kafka/Redis Streams +- Usage aggregation and storage + +### Week 5-6: Billing Engine +- Invoice generation from metered usage +- Payment processing with SYNOR tokens +- Credit system and prepaid balances + +### Week 7-8: Cost Calculator & Dashboard +- CLI `synor cost estimate` command +- Web dashboard for usage visualization +- Cost alerts and budget limits + +--- + +## Files to Create + +``` +crates/synor-economics/ +├── Cargo.toml +├── src/ +│ ├── lib.rs +│ ├── oracle/ +│ │ ├── mod.rs +│ │ ├── price_feed.rs +│ │ └── twap.rs +│ ├── metering/ +│ │ ├── mod.rs +│ │ ├── storage.rs +│ │ ├── hosting.rs +│ │ ├── database.rs +│ │ └── compute.rs +│ ├── billing/ +│ │ ├── mod.rs +│ │ ├── invoice.rs +│ │ ├── payment.rs +│ │ └── credit.rs +│ ├── pricing/ +│ │ ├── mod.rs +│ │ ├── tiers.rs +│ │ └── discounts.rs +│ └── calculator/ +│ ├── mod.rs +│ └── estimator.rs +``` + +--- + +## Next Steps + +1. Create [01-Milestone-01-PricingOracle.md](./01-Milestone-01-PricingOracle.md) +2. Create [01-Milestone-02-MeteringService.md](./01-Milestone-02-MeteringService.md) +3. Create [01-Milestone-03-BillingEngine.md](./01-Milestone-03-BillingEngine.md) + +--- + +*Created: January 11, 2026*