Smart Contract

UsageBasedSubscriptions

A.6daee039a7b9c2f0.UsageBasedSubscriptions

Valid From

123,234,620

Deployed

6d ago
Feb 21, 2026, 09:54:40 PM UTC

Dependents

24 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import FlareFDCTriggers from 0x6daee039a7b9c2f0
4import DeFiActions from 0x92195d814edf9cb0
5
6/// UsageBasedSubscriptions: Dynamic pricing based on real-time usage from LiteLLM via Flare Data Connector
7/// Automatically adjusts subscription costs and processes payments based on actual consumption
8access(all) contract UsageBasedSubscriptions {
9    
10    /// Events
11    access(all) event SubscriptionCreated(vaultId: UInt64, owner: Address, provider: Address)
12    access(all) event UsageDataReceived(vaultId: UInt64, usage: UsageReport, source: String)
13    access(all) event PriceCalculated(vaultId: UInt64, basePrice: UFix64, usageMultiplier: UFix64, finalPrice: UFix64)
14    access(all) event PaymentProcessed(vaultId: UInt64, amount: UFix64, provider: Address)
15    access(all) event AutomaticPaymentProcessed(vaultId: UInt64, amount: UFix64, provider: Address, totalPaidToDate: UFix64)
16    access(all) event EntitlementUpdated(vaultId: UInt64, withdrawLimit: UFix64, validUntil: UFix64)
17    access(all) event UsageTierChanged(vaultId: UInt64, oldTier: String, newTier: String)
18    
19    /// Storage paths
20    access(all) let VaultStoragePath: StoragePath
21    access(all) let VaultPublicPath: PublicPath
22    access(all) let ProviderStoragePath: StoragePath
23    
24    /// Global registry
25    access(all) var totalVaults: UInt64
26    access(all) let vaultRegistry: {UInt64: Address}
27    
28    /// Pricing tiers based on usage
29    access(all) struct PricingTier {
30        access(all) let name: String
31        access(all) let minUsage: UInt64      // Min API calls/tokens
32        access(all) let maxUsage: UInt64      // Max API calls/tokens
33        access(all) let pricePerUnit: UFix64  // Price per 1000 tokens/calls
34        access(all) let discountRate: UFix64  // Volume discount (0.0 - 1.0)
35        
36        init(name: String, minUsage: UInt64, maxUsage: UInt64, pricePerUnit: UFix64, discountRate: UFix64) {
37            self.name = name
38            self.minUsage = minUsage
39            self.maxUsage = maxUsage
40            self.pricePerUnit = pricePerUnit
41            self.discountRate = discountRate
42        }
43    }
44    
45    /// Usage report from LiteLLM via FDC
46    access(all) struct UsageReport {
47        access(all) let timestamp: UFix64
48        access(all) let period: String         // "daily", "weekly", "monthly"
49        access(all) let totalTokens: UInt64    // Total tokens consumed
50        access(all) let apiCalls: UInt64       // Number of API calls
51        access(all) let models: {String: UInt64}  // Usage by model (gpt-4, claude, etc)
52        access(all) let costEstimate: UFix64   // Provider's cost estimate
53        access(all) let metadata: {String: String}
54        
55        init(
56            timestamp: UFix64,
57            period: String,
58            totalTokens: UInt64,
59            apiCalls: UInt64,
60            models: {String: UInt64},
61            costEstimate: UFix64,
62            metadata: {String: String}
63        ) {
64            self.timestamp = timestamp
65            self.period = period
66            self.totalTokens = totalTokens
67            self.apiCalls = apiCalls
68            self.models = models
69            self.costEstimate = costEstimate
70            self.metadata = metadata
71        }
72    }
73    
74    /// Entitlement types
75    access(all) enum EntitlementType: UInt8 {
76        access(all) case fixed      // Fixed withdrawal limit set by user
77        access(all) case dynamic    // Grows with usage as long as vault is funded
78    }
79    
80    /// Dynamic entitlement for automated withdrawals
81    access(all) struct Entitlement {
82        access(all) let vaultId: UInt64
83        access(all) var withdrawLimit: UFix64   // Max amount provider can withdraw
84        access(all) var usedAmount: UFix64      // Amount already withdrawn
85        access(all) var validUntil: UFix64      // Expiration timestamp
86        access(all) var lastUpdate: UFix64      // Last FDC update
87        access(all) var isActive: Bool
88        access(all) let entitlementType: EntitlementType  // Fixed or Dynamic
89        access(all) let fixedLimit: UFix64      // Original fixed limit (if fixed type)
90        
91        access(all) fun updateLimit(newLimit: UFix64, validityPeriod: UFix64) {
92            // For fixed entitlements, never exceed the original fixed limit
93            if self.entitlementType == EntitlementType.fixed {
94                self.withdrawLimit = newLimit > self.fixedLimit ? self.fixedLimit : newLimit
95            } else {
96                // Dynamic entitlements can grow with usage
97                self.withdrawLimit = newLimit
98            }
99            
100            self.validUntil = getCurrentBlock().timestamp + validityPeriod
101            self.lastUpdate = getCurrentBlock().timestamp
102        }
103        
104        access(all) fun recordWithdrawal(amount: UFix64) {
105            self.usedAmount = self.usedAmount + amount
106        }
107        
108        access(all) fun getRemainingAllowance(): UFix64 {
109            if getCurrentBlock().timestamp > self.validUntil {
110                return 0.0
111            }
112            return self.withdrawLimit > self.usedAmount 
113                ? self.withdrawLimit - self.usedAmount 
114                : 0.0
115        }
116        
117        init(vaultId: UInt64, entitlementType: EntitlementType, initialLimit: UFix64, validityPeriod: UFix64) {
118            self.vaultId = vaultId
119            self.entitlementType = entitlementType
120            self.fixedLimit = entitlementType == EntitlementType.fixed ? initialLimit : 0.0
121            self.withdrawLimit = initialLimit
122            self.usedAmount = 0.0
123            self.validUntil = getCurrentBlock().timestamp + validityPeriod
124            self.lastUpdate = getCurrentBlock().timestamp
125            self.isActive = true
126        }
127    }
128    
129    /// Usage-based subscription vault
130    access(all) resource SubscriptionVault {
131        access(all) let id: UInt64
132        access(all) let customer: Address
133        access(all) let provider: Address
134        access(all) let serviceName: String
135        
136        // Funding
137        access(self) let vault: @{FungibleToken.Vault}
138        
139        // Usage tracking
140        access(all) var currentUsage: UsageReport?
141        access(all) var usageHistory: [UsageReport]
142        access(all) var currentTier: PricingTier
143        
144        // Cumulative usage tracking for differential payments
145        access(all) var lastPaidTokens: UInt64      // Tokens we've already paid for
146        access(all) var lastPaidRequests: UInt64    // Requests we've already paid for
147        access(all) var totalPaidAmount: UFix64     // Total FLOW paid to provider
148        access(all) var lastOracleUpdate: UFix64    // Timestamp of last oracle confirmation
149        
150        // Dynamic pricing
151        access(all) var basePrice: UFix64
152        access(all) var usageMultiplier: UFix64
153        access(all) var currentPrice: UFix64
154        
155        // Entitlements
156        access(all) var entitlement: Entitlement
157        
158        // Settings
159        access(all) var autoPay: Bool
160        access(all) var maxMonthlySpend: UFix64
161        
162        // Selected AI models (max 3)
163        access(all) let selectedModels: [String]  // Model IDs like ["gpt-4", "claude-3-sonnet"]
164        access(all) let modelPricing: {String: UFix64}  // Model-specific pricing overrides
165        
166        /// Process usage data from FDC and update pricing
167        access(all) fun processUsageData(usage: UsageReport) {
168            // Store usage report
169            self.currentUsage = usage
170            self.usageHistory.append(usage)
171            
172            // Calculate NEW usage since last payment (differential)
173            let newTokens = usage.totalTokens > self.lastPaidTokens ? usage.totalTokens - self.lastPaidTokens : 0
174            let newRequests = usage.apiCalls > self.lastPaidRequests ? usage.apiCalls - self.lastPaidRequests : 0
175            
176            log("📊 Processing differential usage:")
177            log("   Total tokens: ".concat(usage.totalTokens.toString()).concat(" (+").concat(newTokens.toString()).concat(" new)"))
178            log("   Total requests: ".concat(usage.apiCalls.toString()).concat(" (+").concat(newRequests.toString()).concat(" new)"))
179            log("   Last paid tokens: ".concat(self.lastPaidTokens.toString()))
180            
181            // Only process payment if there's NEW usage
182            if UInt64(newTokens) > 0 || UInt64(newRequests) > 0 {
183                // Update pricing tier based on TOTAL usage
184                let newTier = UsageBasedSubscriptions.calculateTier(usage.totalTokens)
185                if newTier.name != self.currentTier.name {
186                    emit UsageTierChanged(
187                        vaultId: self.id,
188                        oldTier: self.currentTier.name,
189                        newTier: newTier.name
190                    )
191                    self.currentTier = newTier
192                }
193                
194                // Calculate price for NEW usage only
195                let newUsageReport = UsageReport(
196                    timestamp: usage.timestamp,
197                    period: usage.period,
198                    totalTokens: UInt64(newTokens),
199                    apiCalls: UInt64(newRequests),
200                    models: usage.models,
201                    costEstimate: 0.0, // Will be calculated
202                    metadata: usage.metadata
203                )
204                
205                self.calculateDynamicPrice(newUsageReport)
206                
207                // Process automatic payment for new usage
208                self.processAutomaticPayment(newUsageAmount: self.currentPrice)
209                
210                // Update paid tracking
211                self.lastPaidTokens = usage.totalTokens
212                self.lastPaidRequests = usage.apiCalls
213                self.lastOracleUpdate = getCurrentBlock().timestamp
214            }
215            
216            emit UsageDataReceived(
217                vaultId: self.id,
218                usage: usage,
219                source: "LiteLLM via FDC"
220            )
221        }
222        
223        /// Calculate dynamic price based on usage
224        access(self) fun calculateDynamicPrice(_ usage: UsageReport) {
225            // Base calculation: tokens * price per unit
226            let tokenThousands = UFix64(usage.totalTokens) / 1000.0
227            var calculatedPrice = tokenThousands * self.currentTier.pricePerUnit
228            
229            // Apply volume discount
230            calculatedPrice = calculatedPrice * (1.0 - self.currentTier.discountRate)
231            
232            // Apply model-specific multipliers based on selected models
233            var modelMultiplier = 1.0
234            var modelCount = 0
235            
236            for model in usage.models.keys {
237                // Only apply pricing for selected models
238                if self.selectedModels.contains(model) {
239                    let multiplier = self.modelPricing[model] ?? 1.0
240                    modelMultiplier = modelMultiplier + multiplier
241                    modelCount = modelCount + 1
242                }
243            }
244            
245            // Average the multipliers if multiple models were used
246            if modelCount > 0 {
247                modelMultiplier = modelMultiplier / UFix64(modelCount)
248            }
249            
250            self.usageMultiplier = modelMultiplier
251            self.currentPrice = calculatedPrice * modelMultiplier
252            
253            emit PriceCalculated(
254                vaultId: self.id,
255                basePrice: self.basePrice,
256                usageMultiplier: self.usageMultiplier,
257                finalPrice: self.currentPrice
258            )
259        }
260        
261        /// Update entitlement for provider withdrawals
262        access(self) fun updateEntitlement() {
263            let withdrawLimit = self.currentPrice
264            let validityPeriod = 86400.0 * 30.0  // 30 days
265            
266            self.entitlement.updateLimit(
267                newLimit: withdrawLimit,
268                validityPeriod: validityPeriod
269            )
270            
271            emit EntitlementUpdated(
272                vaultId: self.id,
273                withdrawLimit: withdrawLimit,
274                validUntil: self.entitlement.validUntil
275            )
276        }
277        
278        /// Provider withdraws based on entitlement
279        access(all) fun withdrawWithEntitlement(amount: UFix64): @{FungibleToken.Vault} {
280            // Check entitlement allowance
281            let remainingAllowance = self.entitlement.getRemainingAllowance()
282            assert(amount <= remainingAllowance, message: "Amount exceeds entitlement allowance")
283            assert(amount <= self.vault.balance, message: "Insufficient vault balance")
284            
285            let payment <- self.vault.withdraw(amount: amount)
286            self.entitlement.recordWithdrawal(amount: amount)
287            
288            emit PaymentProcessed(
289                vaultId: self.id,
290                amount: amount,
291                provider: self.provider
292            )
293            
294            return <- payment
295        }
296        
297        /// Process automatic payment to provider based on new usage
298        access(self) fun processAutomaticPayment(newUsageAmount: UFix64) {
299            // Check if automatic payments are enabled
300            if !self.autoPay {
301                log("⏸️ Auto-pay disabled, skipping automatic payment")
302                return
303            }
304            
305            // Check if vault has sufficient balance
306            if self.vault.balance < newUsageAmount {
307                log("⚠️ Insufficient vault balance for automatic payment")
308                log("   Required: ".concat(newUsageAmount.toString()).concat(" FLOW"))
309                log("   Available: ".concat(self.vault.balance.toString()).concat(" FLOW"))
310                return
311            }
312            
313            // Check monthly spending limits
314            if self.totalPaidAmount + newUsageAmount > self.maxMonthlySpend {
315                log("⚠️ Monthly spending limit exceeded, skipping automatic payment")
316                log("   Would exceed limit by: ".concat((self.totalPaidAmount + newUsageAmount - self.maxMonthlySpend).toString()).concat(" FLOW"))
317                return
318            }
319            
320            // Process automatic payment
321            log("💰 Processing automatic payment:")
322            log("   Amount: ".concat(newUsageAmount.toString()).concat(" FLOW"))
323            log("   Provider: ".concat(self.provider.toString()))
324            
325            // Transfer funds directly to provider
326            let payment <- self.vault.withdraw(amount: newUsageAmount)
327            
328            // Get provider's Flow vault and deposit payment
329            let providerAccount = getAccount(self.provider)
330            let providerReceiver = providerAccount.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
331            let receiverRef = providerReceiver.borrow()!
332            receiverRef.deposit(from: <- payment)
333            
334            // Update tracking
335            self.totalPaidAmount = self.totalPaidAmount + newUsageAmount
336            self.entitlement.recordWithdrawal(amount: newUsageAmount)
337            
338            emit PaymentProcessed(
339                vaultId: self.id,
340                amount: newUsageAmount,
341                provider: self.provider
342            )
343            
344            emit AutomaticPaymentProcessed(
345                vaultId: self.id,
346                amount: newUsageAmount,
347                provider: self.provider,
348                totalPaidToDate: self.totalPaidAmount
349            )
350            
351            log("✅ Automatic payment completed successfully")
352        }
353        
354        /// Deposit funds to vault
355        access(all) fun deposit(from: @{FungibleToken.Vault}) {
356            self.vault.deposit(from: <- from)
357        }
358        
359        /// Get vault balance
360        access(all) fun getBalance(): UFix64 {
361            return self.vault.balance
362        }
363        
364        /// Get current pricing info
365        access(all) fun getPricingInfo(): {String: AnyStruct} {
366            return {
367                "currentTier": self.currentTier.name,
368                "basePrice": self.basePrice,
369                "usageMultiplier": self.usageMultiplier,
370                "currentPrice": self.currentPrice,
371                "remainingEntitlement": self.entitlement.getRemainingAllowance()
372            }
373        }
374        
375        /// Get complete vault information for UI display
376        access(all) fun getVaultInfo(): {String: AnyStruct} {
377            return {
378                "vaultId": self.id,
379                "owner": self.owner,
380                "provider": self.provider,
381                "serviceName": self.serviceName,
382                "balance": self.vault.balance,
383                "selectedModels": self.selectedModels,
384                "modelPricing": self.modelPricing,
385                "entitlementType": self.entitlement.entitlementType.rawValue,
386                "withdrawLimit": self.entitlement.withdrawLimit,
387                "usedAmount": self.entitlement.usedAmount,
388                "validUntil": self.entitlement.validUntil,
389                "isActive": self.entitlement.isActive,
390                "currentTier": self.currentTier.name,
391                "basePrice": self.basePrice,
392                "currentPrice": self.currentPrice,
393                "autoPay": self.autoPay,
394                "maxMonthlySpend": self.maxMonthlySpend,
395                "lastPaidTokens": self.lastPaidTokens,
396                "lastPaidRequests": self.lastPaidRequests,
397                "totalPaidAmount": self.totalPaidAmount,
398                "lastOracleUpdate": self.lastOracleUpdate
399            }
400        }
401        
402        init(
403            owner: Address,
404            provider: Address,
405            serviceName: String,
406            vault: @{FungibleToken.Vault},
407            entitlementType: EntitlementType,
408            initialWithdrawLimit: UFix64,
409            validityPeriod: UFix64,
410            selectedModels: [String]
411        ) {
412            self.id = UsageBasedSubscriptions.totalVaults
413            UsageBasedSubscriptions.totalVaults = UsageBasedSubscriptions.totalVaults + 1
414            
415            self.customer = owner
416            self.provider = provider
417            self.serviceName = serviceName
418            self.vault <- vault
419            
420            self.currentUsage = nil
421            self.usageHistory = []
422            self.currentTier = UsageBasedSubscriptions.getDefaultTier()
423            
424            // Initialize cumulative usage tracking
425            self.lastPaidTokens = 0
426            self.lastPaidRequests = 0
427            self.totalPaidAmount = 0.0
428            self.lastOracleUpdate = 0.0
429            
430            self.basePrice = 10.0  // $10 base
431            self.usageMultiplier = 1.0
432            self.currentPrice = 10.0
433            
434            // Create entitlement with user-specified settings
435            self.entitlement = Entitlement(
436                vaultId: self.id,
437                entitlementType: entitlementType,
438                initialLimit: initialWithdrawLimit,
439                validityPeriod: validityPeriod
440            )
441            
442            self.autoPay = true
443            self.maxMonthlySpend = 1000.0
444            
445            // Initialize selected models and pricing
446            self.selectedModels = selectedModels
447            self.modelPricing = {}
448            
449            // Validate model selection (max 3 models)
450            assert(self.selectedModels.length > 0, message: "At least 1 model must be selected")
451            assert(self.selectedModels.length <= 3, message: "Maximum 3 models allowed per subscription")
452            
453            // Set up model-specific pricing overrides
454            for model in self.selectedModels {
455                if model == "gpt-4" || model == "claude-3-opus" {
456                    self.modelPricing[model] = 1.5  // Premium models cost 50% more
457                } else if model == "gpt-3.5-turbo" || model == "claude-3-haiku" {
458                    self.modelPricing[model] = 0.8  // Budget models cost 20% less
459                } else {
460                    self.modelPricing[model] = 1.0  // Standard pricing
461                }
462            }
463            
464            UsageBasedSubscriptions.vaultRegistry[self.id] = owner
465        }
466    }
467    
468    /// FDC Handler for LiteLLM usage data
469    access(all) resource LiteLLMUsageHandler: FlareFDCTriggers.TriggerHandler {
470        access(self) var isHandlerActive: Bool
471        
472        access(all) fun handleTrigger(trigger: FlareFDCTriggers.FDCTrigger): Bool {
473            // Extract usage data from FDC trigger
474            let vaultId = trigger.payload["vaultId"] as? UInt64 ?? 0
475            let totalTokens = trigger.payload["totalTokens"] as? UInt64 ?? 0
476            let apiCalls = trigger.payload["apiCalls"] as? UInt64 ?? 0
477            
478            // Create usage report
479            let models: {String: UInt64} = {}
480            if let modelUsage = trigger.payload["models"] as? {String: UInt64} {
481                for key in modelUsage.keys {
482                    models[key] = modelUsage[key]!
483                }
484            }
485            
486            let usage = UsageReport(
487                timestamp: trigger.timestamp,
488                period: trigger.payload["period"] as? String ?? "daily",
489                totalTokens: totalTokens,
490                apiCalls: apiCalls,
491                models: models,
492                costEstimate: trigger.payload["costEstimate"] as? UFix64 ?? 0.0,
493                metadata: {}
494            )
495            
496            // Update subscription vault
497            if let ownerAddress = UsageBasedSubscriptions.vaultRegistry[vaultId] {
498                // Vault access should be done via transactions with proper authorization
499                log("Usage update requested for vault ID: ".concat(vaultId.toString()))
500                return true
501            }
502            
503            return false
504        }
505        
506        access(all) fun getSupportedTriggerTypes(): [FlareFDCTriggers.TriggerType] {
507            return [
508                FlareFDCTriggers.TriggerType.DefiProtocolEvent
509            ]
510        }
511        
512        access(all) fun isActive(): Bool {
513            return self.isHandlerActive
514        }
515        
516        init() {
517            self.isHandlerActive = true
518        }
519    }
520    
521    /// Provider resource for managing subscriptions
522    access(all) resource ServiceProvider {
523        access(all) let address: Address
524        access(all) let serviceName: String
525        access(all) var totalEarnings: UFix64
526        access(all) var activeSubscriptions: {UInt64: Bool}
527        
528        /// Withdraw from customer vault based on entitlement
529        access(all) fun collectPayment(vaultId: UInt64, amount: UFix64): @{FungibleToken.Vault}? {
530            if let ownerAddress = UsageBasedSubscriptions.vaultRegistry[vaultId] {
531                // Vault access should be done via transactions with proper authorization
532                log("Payment collection requested for vault ID: ".concat(vaultId.toString()))
533                // Return nil for now - should be handled via transactions
534                return nil
535            }
536            return nil
537        }
538        
539        init(address: Address, serviceName: String) {
540            self.address = address
541            self.serviceName = serviceName
542            self.totalEarnings = 0.0
543            self.activeSubscriptions = {}
544        }
545    }
546    
547    /// Public functions
548    
549    /// Create a new subscription vault with entitlement settings
550    access(all) fun createSubscriptionVault(
551        owner: Address,
552        provider: Address,
553        serviceName: String,
554        initialDeposit: @{FungibleToken.Vault},
555        entitlementType: EntitlementType,
556        initialWithdrawLimit: UFix64,
557        validityPeriod: UFix64,
558        selectedModels: [String]
559    ): @SubscriptionVault {
560        let vault <- create SubscriptionVault(
561            owner: owner,
562            provider: provider,
563            serviceName: serviceName,
564            vault: <- initialDeposit,
565            entitlementType: entitlementType,
566            initialWithdrawLimit: initialWithdrawLimit,
567            validityPeriod: validityPeriod,
568            selectedModels: selectedModels
569        )
570        
571        emit SubscriptionCreated(vaultId: vault.id, owner: owner, provider: provider)
572        
573        return <- vault
574    }
575    
576    /// Get vault storage path
577    access(all) fun getVaultStoragePath(): StoragePath {
578        return self.VaultStoragePath
579    }
580    
581    /// Get all vault IDs for a user
582    access(all) fun getUserVaultIds(owner: Address): [UInt64] {
583        let vaultIds: [UInt64] = []
584        
585        for vaultId in self.vaultRegistry.keys {
586            if self.vaultRegistry[vaultId] == owner {
587                vaultIds.append(vaultId)
588            }
589        }
590        
591        return vaultIds
592    }
593    
594    /// Get vault information by vault ID
595    access(all) fun getVaultInfo(vaultId: UInt64): {String: AnyStruct}? {
596        if let ownerAddress = self.vaultRegistry[vaultId] {
597            // Vault info access should be done via transactions
598            return {"vaultId": vaultId, "owner": ownerAddress}
599        }
600        return nil
601    }
602    
603    /// Calculate pricing tier based on usage
604    access(all) fun calculateTier(_ totalTokens: UInt64): PricingTier {
605        let tiers = self.getPricingTiers()
606        
607        for tier in tiers {
608            if totalTokens >= tier.minUsage && totalTokens <= tier.maxUsage {
609                return tier
610            }
611        }
612        
613        return self.getDefaultTier()
614    }
615    
616    /// Get pricing tiers
617    access(all) fun getPricingTiers(): [PricingTier] {
618        return [
619            PricingTier(name: "Starter", minUsage: 0, maxUsage: 100000, pricePerUnit: 0.02, discountRate: 0.0),
620            PricingTier(name: "Growth", minUsage: 100001, maxUsage: 1000000, pricePerUnit: 0.015, discountRate: 0.1),
621            PricingTier(name: "Scale", minUsage: 1000001, maxUsage: 10000000, pricePerUnit: 0.01, discountRate: 0.2),
622            PricingTier(name: "Enterprise", minUsage: 10000001, maxUsage: UInt64.max, pricePerUnit: 0.008, discountRate: 0.3)
623        ]
624    }
625    
626    /// Get default tier
627    access(all) fun getDefaultTier(): PricingTier {
628        return PricingTier(name: "Starter", minUsage: 0, maxUsage: 100000, pricePerUnit: 0.02, discountRate: 0.0)
629    }
630    
631    /// Helper function to convert time periods to seconds
632    access(all) fun convertToSeconds(amount: UInt64, unit: String): UFix64 {
633        switch unit {
634            case "hours":
635                return UFix64(amount) * 3600.0
636            case "days":
637                return UFix64(amount) * 86400.0
638            case "months":
639                return UFix64(amount) * 2592000.0  // 30 days
640            default:
641                return UFix64(amount) * 86400.0    // Default to days
642        }
643    }
644    
645    /// Create service provider
646    access(all) fun createServiceProvider(address: Address, serviceName: String): @ServiceProvider {
647        return <- create ServiceProvider(address: address, serviceName: serviceName)
648    }
649    
650    /// Create LiteLLM usage handler
651    access(all) fun createLiteLLMHandler(): @LiteLLMUsageHandler {
652        return <- create LiteLLMUsageHandler()
653    }
654    
655    init() {
656        self.VaultStoragePath = /storage/UsageBasedSubscriptionVault
657        self.VaultPublicPath = /public/UsageBasedSubscriptionVault
658        self.ProviderStoragePath = /storage/UsageBasedServiceProvider
659        
660        self.totalVaults = 0
661        self.vaultRegistry = {}
662    }
663}