a
This commit is contained in:
parent
162227dc71
commit
3c9470abba
3 changed files with 912 additions and 0 deletions
|
|
@ -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<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
|
||||
|
|
@ -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<LineItem>,
|
||||
/// Subtotal before discounts
|
||||
pub subtotal_micro_synor: u64,
|
||||
/// Applied discounts
|
||||
pub discounts: Vec<AppliedDiscount>,
|
||||
/// 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<TxId>,
|
||||
/// 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<Invoice, BillingError> {
|
||||
// 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<PaymentResult, PaymentError> {
|
||||
// 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<AutoPayConfig, PaymentError> {
|
||||
// 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<CreditEntry>,
|
||||
}
|
||||
|
||||
pub struct CreditEntry {
|
||||
pub credit_id: CreditId,
|
||||
pub amount_micro_synor: u64,
|
||||
pub remaining_micro_synor: u64,
|
||||
pub source: CreditSource,
|
||||
pub expires_at: Option<u64>,
|
||||
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<u64>,
|
||||
) -> Result<CreditEntry, CreditError> {
|
||||
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<u64, CreditError> {
|
||||
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<u64, CreditError> {
|
||||
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<DepositResult, PrepaidError> {
|
||||
// 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<bool, PrepaidError> {
|
||||
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<DeductResult, PrepaidError> {
|
||||
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
|
||||
```
|
||||
187
docs/PLAN/PHASE12-EconomicsBilling/README.md
Normal file
187
docs/PLAN/PHASE12-EconomicsBilling/README.md
Normal file
|
|
@ -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*
|
||||
Loading…
Add table
Reference in a new issue