//! # Synor Economics //! //! Economics, pricing, metering, and billing infrastructure for Synor L2 services. //! //! ## Architecture //! //! ```text //! ┌─────────────────────────────────────────────────────────────┐ //! │ SYNOR ECONOMICS LAYER │ //! ├─────────────────────────────────────────────────────────────┤ //! │ Billing Engine: Invoice → Payment → Credit │ //! │ Metering: Storage | Hosting | Database | Compute | Network │ //! │ Pricing: Oracle → TWAP → Tiers → Discounts │ //! └─────────────────────────────────────────────────────────────┘ //! ``` //! //! ## Components //! //! - **Pricing Oracle**: Aggregates SYNOR/USD prices from multiple sources //! - **Metering Service**: Tracks real-time resource usage across L2 services //! - **Billing Engine**: Generates invoices, processes payments, manages credits //! - **Cost Calculator**: Estimates costs before resource consumption pub mod billing; pub mod calculator; pub mod error; pub mod metering; pub mod oracle; pub mod pricing; pub use billing::{BillingEngine, Credit, Invoice, InvoiceStatus, Payment, PaymentMethod}; pub use calculator::{CostEstimate, CostEstimator, UsageProjection}; pub use error::{EconomicsError, Result}; pub use metering::{ ComputeUsage, DatabaseUsage, HostingUsage, MeteringService, NetworkUsage, StorageUsage, UsageEvent, UsageRecord, }; pub use oracle::{ AggregateFeed, ChainlinkFeed, CoinGeckoFeed, InternalDexFeed, MockPriceFeed, OracleFactory, PriceFeed, PriceOracle, PriceOracleBuilder, PriceSource, ProductionOracleConfig, TokenPrice, }; pub use pricing::{Discount, DiscountType, PricingEngine, PricingTier, ServicePricing}; use chrono::{DateTime, Utc}; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; /// Synor token amount in smallest unit (1 SYNOR = 10^18 units) pub type SynorAmount = u128; /// SYNOR decimal representation (for UI display) pub type SynorDecimal = Decimal; /// Account identifier pub type AccountId = String; /// Service identifier pub type ServiceId = String; /// Fee distribution configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FeeDistribution { /// Percentage burned (deflationary) pub burn_rate: Decimal, /// Percentage to stakers pub staker_rate: Decimal, /// Percentage to community treasury pub treasury_rate: Decimal, /// Percentage to validators/node operators pub operator_rate: Decimal, } impl Default for FeeDistribution { fn default() -> Self { Self::transaction_fees() } } impl FeeDistribution { /// Transaction fee distribution (L1) pub fn transaction_fees() -> Self { Self { burn_rate: Decimal::new(10, 2), // 10% staker_rate: Decimal::new(60, 2), // 60% treasury_rate: Decimal::new(20, 2), // 20% operator_rate: Decimal::new(10, 2), // 10% } } /// L2 service fee distribution pub fn l2_service_fees() -> Self { Self { burn_rate: Decimal::new(10, 2), // 10% staker_rate: Decimal::ZERO, // 0% treasury_rate: Decimal::new(20, 2), // 20% operator_rate: Decimal::new(70, 2), // 70% } } /// Calculate distribution amounts from a total fee pub fn distribute(&self, total: SynorDecimal) -> FeeBreakdown { FeeBreakdown { burn: total * self.burn_rate, stakers: total * self.staker_rate, treasury: total * self.treasury_rate, operators: total * self.operator_rate, total, } } /// Validate that rates sum to 100% pub fn validate(&self) -> Result<()> { let sum = self.burn_rate + self.staker_rate + self.treasury_rate + self.operator_rate; if sum != Decimal::ONE { return Err(EconomicsError::InvalidFeeDistribution(format!( "Rates must sum to 100%, got {}%", sum * Decimal::ONE_HUNDRED ))); } Ok(()) } } /// Breakdown of fee distribution #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FeeBreakdown { pub burn: SynorDecimal, pub stakers: SynorDecimal, pub treasury: SynorDecimal, pub operators: SynorDecimal, pub total: SynorDecimal, } /// Service type for metering and billing #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ServiceType { /// Storage L2 Storage, /// Web hosting Hosting, /// Database L2 Database, /// Compute L2 (CPU/GPU/TPU) Compute, /// Network bandwidth Network, /// Smart contract execution Contract, } impl ServiceType { /// Get the fee distribution model for this service type pub fn fee_distribution(&self) -> FeeDistribution { match self { ServiceType::Contract => FeeDistribution::transaction_fees(), _ => FeeDistribution::l2_service_fees(), } } } impl std::fmt::Display for ServiceType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ServiceType::Storage => write!(f, "storage"), ServiceType::Hosting => write!(f, "hosting"), ServiceType::Database => write!(f, "database"), ServiceType::Compute => write!(f, "compute"), ServiceType::Network => write!(f, "network"), ServiceType::Contract => write!(f, "contract"), } } } /// Resource unit for pricing and metering #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ResourceUnit { /// Storage: bytes Bytes, /// Storage: gigabytes per month GbMonth, /// Database: queries Queries, /// Database: vector searches VectorSearches, /// Compute: CPU core hours CpuCoreHours, /// Compute: GPU hours GpuHours, /// Compute: memory GB hours MemoryGbHours, /// Compute: serverless invocations Invocations, /// Network: bandwidth in GB BandwidthGb, /// Hosting: custom domains Domains, /// Contract: gas units Gas, } impl std::fmt::Display for ResourceUnit { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ResourceUnit::Bytes => write!(f, "bytes"), ResourceUnit::GbMonth => write!(f, "GB/month"), ResourceUnit::Queries => write!(f, "queries"), ResourceUnit::VectorSearches => write!(f, "vector searches"), ResourceUnit::CpuCoreHours => write!(f, "CPU core hours"), ResourceUnit::GpuHours => write!(f, "GPU hours"), ResourceUnit::MemoryGbHours => write!(f, "memory GB hours"), ResourceUnit::Invocations => write!(f, "invocations"), ResourceUnit::BandwidthGb => write!(f, "GB bandwidth"), ResourceUnit::Domains => write!(f, "domains"), ResourceUnit::Gas => write!(f, "gas"), } } } /// Account balance and usage summary #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AccountSummary { pub account_id: AccountId, pub balance: SynorDecimal, pub credit_balance: SynorDecimal, pub prepaid_balance: SynorDecimal, pub current_period_usage: HashMap, pub current_period_cost: SynorDecimal, pub billing_tier: String, pub last_payment: Option>, pub next_invoice_date: Option>, } /// Unified economics manager combining all services pub struct EconomicsManager { pub oracle: Arc>, pub metering: Arc, pub billing: Arc, pub pricing: Arc, pub calculator: Arc, } impl EconomicsManager { /// Create a new economics manager with default configuration /// Uses a development oracle with mock price feeds pub fn new() -> Self { use rust_decimal_macros::dec; // Default to development oracle with mock feeds at $1.50 base price let oracle = Arc::new(RwLock::new(oracle::OracleFactory::development(dec!(1.50)))); let pricing = Arc::new(PricingEngine::new()); let metering = Arc::new(MeteringService::new(pricing.clone())); let billing = Arc::new(BillingEngine::new(metering.clone(), pricing.clone())); let calculator = Arc::new(CostEstimator::new(pricing.clone())); Self { oracle, metering, billing, pricing, calculator, } } /// Create an economics manager with production oracle configuration pub fn with_production_oracle(config: oracle::ProductionOracleConfig) -> Self { let oracle = Arc::new(RwLock::new(oracle::OracleFactory::production(config))); let pricing = Arc::new(PricingEngine::new()); let metering = Arc::new(MeteringService::new(pricing.clone())); let billing = Arc::new(BillingEngine::new(metering.clone(), pricing.clone())); let calculator = Arc::new(CostEstimator::new(pricing.clone())); Self { oracle, metering, billing, pricing, calculator, } } /// Create an economics manager with a custom oracle pub fn with_oracle(oracle: PriceOracle) -> Self { let oracle = Arc::new(RwLock::new(oracle)); let pricing = Arc::new(PricingEngine::new()); let metering = Arc::new(MeteringService::new(pricing.clone())); let billing = Arc::new(BillingEngine::new(metering.clone(), pricing.clone())); let calculator = Arc::new(CostEstimator::new(pricing.clone())); Self { oracle, metering, billing, pricing, calculator, } } /// Get account summary including balance, usage, and billing info pub async fn get_account_summary(&self, account_id: &str) -> Result { let usage = self.metering.get_account_usage(account_id).await?; let billing_info = self.billing.get_account_info(account_id).await?; let mut current_period_usage = HashMap::new(); let mut current_period_cost = Decimal::ZERO; for (service_type, amount) in usage.by_service.iter() { current_period_usage.insert(*service_type, *amount); current_period_cost += *amount; } Ok(AccountSummary { account_id: account_id.to_string(), balance: billing_info.balance, credit_balance: billing_info.credit_balance, prepaid_balance: billing_info.prepaid_balance, current_period_usage, current_period_cost, billing_tier: billing_info.tier_name, last_payment: billing_info.last_payment, next_invoice_date: billing_info.next_invoice, }) } /// Estimate cost for a usage projection pub async fn estimate_cost(&self, projection: UsageProjection) -> Result { self.calculator.estimate(projection).await } /// Record usage event pub async fn record_usage(&self, event: UsageEvent) -> Result<()> { self.metering.record(event).await } /// Update price from external feed pub async fn update_price(&self, price: TokenPrice) -> Result<()> { let mut oracle = self.oracle.write().await; oracle.update_price(price) } /// Get current SYNOR/USD price pub async fn get_synor_price(&self) -> Result { let oracle = self.oracle.read().await; oracle.get_price("SYNOR", "USD") } /// Generate invoice for account pub async fn generate_invoice(&self, account_id: &str) -> Result { self.billing.generate_invoice(account_id).await } /// Process payment pub async fn process_payment(&self, payment: Payment) -> Result<()> { self.billing.process_payment(payment).await } } impl Default for EconomicsManager { fn default() -> Self { Self::new() } } /// Convert raw amount to SYNOR decimal (18 decimals) pub fn to_synor_decimal(raw: SynorAmount) -> SynorDecimal { Decimal::from(raw) / Decimal::from(10u128.pow(18)) } /// Convert SYNOR decimal to raw amount pub fn from_synor_decimal(decimal: SynorDecimal) -> SynorAmount { let scaled = decimal * Decimal::from(10u128.pow(18)); scaled.try_into().unwrap_or(0) } #[cfg(test)] mod tests { use super::*; use rust_decimal_macros::dec; #[test] fn test_fee_distribution_transaction() { let dist = FeeDistribution::transaction_fees(); assert!(dist.validate().is_ok()); let breakdown = dist.distribute(dec!(100)); assert_eq!(breakdown.burn, dec!(10)); assert_eq!(breakdown.stakers, dec!(60)); assert_eq!(breakdown.treasury, dec!(20)); assert_eq!(breakdown.operators, dec!(10)); } #[test] fn test_fee_distribution_l2() { let dist = FeeDistribution::l2_service_fees(); assert!(dist.validate().is_ok()); let breakdown = dist.distribute(dec!(100)); assert_eq!(breakdown.burn, dec!(10)); assert_eq!(breakdown.stakers, dec!(0)); assert_eq!(breakdown.treasury, dec!(20)); assert_eq!(breakdown.operators, dec!(70)); } #[test] fn test_synor_decimal_conversion() { // 1 SYNOR = 10^18 units let one_synor = 1_000_000_000_000_000_000u128; let decimal = to_synor_decimal(one_synor); assert_eq!(decimal, Decimal::ONE); let back = from_synor_decimal(decimal); assert_eq!(back, one_synor); } #[test] fn test_invalid_fee_distribution() { let invalid = FeeDistribution { burn_rate: dec!(0.50), staker_rate: dec!(0.50), treasury_rate: dec!(0.10), operator_rate: dec!(0.10), }; assert!(invalid.validate().is_err()); } }