Smart Contract
SimpleUsageSubscriptions
A.6daee039a7b9c2f0.SimpleUsageSubscriptions
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import FlareFDCTriggers from 0x6daee039a7b9c2f0
4
5/// SimpleUsageSubscriptions: Simplified usage-based billing for LiteLLM
6/// Users connect to this contract to create vaults and grant payment entitlements
7access(all) contract SimpleUsageSubscriptions {
8
9 /// Events
10 access(all) event SubscriptionCreated(vaultId: UInt64, customer: Address, provider: Address)
11 access(all) event UsageUpdated(vaultId: UInt64, newPrice: UFix64, tier: String)
12 access(all) event PaymentProcessed(vaultId: UInt64, amount: UFix64)
13
14 /// Storage paths
15 access(all) let VaultStoragePath: StoragePath
16 access(all) let VaultPublicPath: PublicPath
17
18 /// Global state
19 access(all) var totalVaults: UInt64
20
21 /// Pricing tiers
22 access(all) struct PricingTier {
23 access(all) let name: String
24 access(all) let minTokens: UInt64
25 access(all) let pricePerK: UFix64
26 access(all) let discount: UFix64
27
28 init(name: String, minTokens: UInt64, pricePerK: UFix64, discount: UFix64) {
29 self.name = name
30 self.minTokens = minTokens
31 self.pricePerK = pricePerK
32 self.discount = discount
33 }
34 }
35
36 /// Usage report from LiteLLM
37 access(all) struct UsageReport {
38 access(all) let vaultId: UInt64
39 access(all) let totalTokens: UInt64
40 access(all) let apiCalls: UInt64
41 access(all) let gpt4Tokens: UInt64
42 access(all) let gpt35Tokens: UInt64
43 access(all) let timestamp: UFix64
44
45 init(vaultId: UInt64, totalTokens: UInt64, apiCalls: UInt64, gpt4Tokens: UInt64, gpt35Tokens: UInt64) {
46 self.vaultId = vaultId
47 self.totalTokens = totalTokens
48 self.apiCalls = apiCalls
49 self.gpt4Tokens = gpt4Tokens
50 self.gpt35Tokens = gpt35Tokens
51 self.timestamp = getCurrentBlock().timestamp
52 }
53 }
54
55 /// Subscription vault with usage-based pricing
56 access(all) resource SubscriptionVault {
57 access(all) let id: UInt64
58 access(all) let customer: Address
59 access(all) let provider: Address
60 access(self) let vault: @{FungibleToken.Vault}
61
62 // Usage tracking
63 access(all) var lastUsage: UsageReport?
64 access(all) var currentTier: PricingTier
65 access(all) var currentPrice: UFix64
66 access(all) var allowedWithdrawal: UFix64
67
68 /// Process usage update from LiteLLM via FDC
69 access(all) fun updateUsage(_ usage: UsageReport) {
70 self.lastUsage = usage
71
72 // Calculate tier
73 self.currentTier = SimpleUsageSubscriptions.getTierForUsage(usage.totalTokens)
74
75 // Calculate price
76 let basePrice = UFix64(usage.totalTokens) / 1000.0 * self.currentTier.pricePerK
77 let discounted = basePrice * (1.0 - self.currentTier.discount)
78
79 // Model multipliers
80 let gpt4Cost = UFix64(usage.gpt4Tokens) / 1000.0 * self.currentTier.pricePerK * 1.5
81 let gpt35Cost = UFix64(usage.gpt35Tokens) / 1000.0 * self.currentTier.pricePerK * 0.8
82
83 self.currentPrice = gpt4Cost + gpt35Cost
84 self.allowedWithdrawal = self.currentPrice
85
86 emit UsageUpdated(
87 vaultId: self.id,
88 newPrice: self.currentPrice,
89 tier: self.currentTier.name
90 )
91 }
92
93 /// Provider withdraws based on usage
94 access(all) fun withdrawByUsage(amount: UFix64): @{FungibleToken.Vault} {
95 pre {
96 amount <= self.allowedWithdrawal: "Exceeds usage allowance"
97 amount <= self.vault.balance: "Insufficient balance"
98 }
99
100 self.allowedWithdrawal = self.allowedWithdrawal - amount
101 let payment <- self.vault.withdraw(amount: amount)
102
103 emit PaymentProcessed(vaultId: self.id, amount: amount)
104 return <- payment
105 }
106
107 /// Customer deposits funds
108 access(all) fun deposit(from: @{FungibleToken.Vault}) {
109 self.vault.deposit(from: <- from)
110 }
111
112 /// Get vault info
113 access(all) fun getInfo(): {String: AnyStruct} {
114 return {
115 "balance": self.vault.balance,
116 "currentPrice": self.currentPrice,
117 "allowedWithdrawal": self.allowedWithdrawal,
118 "tier": self.currentTier.name,
119 "lastUpdate": self.lastUsage?.timestamp ?? 0.0
120 }
121 }
122
123 init(customer: Address, provider: Address, initialDeposit: @{FungibleToken.Vault}) {
124 self.id = SimpleUsageSubscriptions.totalVaults
125 SimpleUsageSubscriptions.totalVaults = SimpleUsageSubscriptions.totalVaults + 1
126
127 self.customer = customer
128 self.provider = provider
129 self.vault <- initialDeposit
130
131 self.lastUsage = nil
132 self.currentTier = SimpleUsageSubscriptions.getStarterTier()
133 self.currentPrice = 0.0
134 self.allowedWithdrawal = 0.0
135 }
136 }
137
138 /// FDC Handler for LiteLLM usage updates
139 access(all) resource LiteLLMHandler: FlareFDCTriggers.TriggerHandler {
140 access(self) var active: Bool
141
142 access(all) fun handleTrigger(trigger: FlareFDCTriggers.FDCTrigger): Bool {
143 // Extract usage data from FDC payload
144 if let vaultIdAny = trigger.payload["vaultId"] {
145 if let vaultId = vaultIdAny as? UInt64 {
146 let usage = UsageReport(
147 vaultId: vaultId,
148 totalTokens: trigger.payload["totalTokens"] as? UInt64 ?? 0,
149 apiCalls: trigger.payload["apiCalls"] as? UInt64 ?? 0,
150 gpt4Tokens: trigger.payload["gpt4Tokens"] as? UInt64 ?? 0,
151 gpt35Tokens: trigger.payload["gpt35Tokens"] as? UInt64 ?? 0
152 )
153
154 // This would update the vault in production
155 // For demo, just emit the event
156 emit UsageUpdated(
157 vaultId: vaultId,
158 newPrice: UFix64(usage.totalTokens) / 1000.0 * 0.02,
159 tier: "Auto-calculated"
160 )
161
162 return true
163 }
164 }
165 return false
166 }
167
168 access(all) fun getSupportedTriggerTypes(): [FlareFDCTriggers.TriggerType] {
169 return [FlareFDCTriggers.TriggerType.DefiProtocolEvent]
170 }
171
172 access(all) fun isActive(): Bool {
173 return self.active
174 }
175
176 init() {
177 self.active = true
178 }
179 }
180
181 /// Public functions users connect to
182
183 /// Create subscription vault (main user entry point)
184 access(all) fun createSubscriptionVault(
185 customer: Address,
186 provider: Address,
187 initialDeposit: @{FungibleToken.Vault}
188 ): @SubscriptionVault {
189 let vault <- create SubscriptionVault(
190 customer: customer,
191 provider: provider,
192 initialDeposit: <- initialDeposit
193 )
194
195 emit SubscriptionCreated(
196 vaultId: vault.id,
197 customer: customer,
198 provider: provider
199 )
200
201 return <- vault
202 }
203
204 /// Process usage update (called by FDC)
205 access(all) fun processUsageUpdate(usage: UsageReport) {
206 emit UsageUpdated(
207 vaultId: usage.vaultId,
208 newPrice: UFix64(usage.totalTokens) / 1000.0 * 0.02,
209 tier: self.getTierForUsage(usage.totalTokens).name
210 )
211 }
212
213 /// Get pricing tier for usage amount
214 access(all) fun getTierForUsage(_ tokens: UInt64): PricingTier {
215 if tokens >= 10000000 {
216 return PricingTier(name: "Enterprise", minTokens: 10000000, pricePerK: 0.008, discount: 0.3)
217 } else if tokens >= 1000000 {
218 return PricingTier(name: "Scale", minTokens: 1000000, pricePerK: 0.01, discount: 0.2)
219 } else if tokens >= 100000 {
220 return PricingTier(name: "Growth", minTokens: 100000, pricePerK: 0.015, discount: 0.1)
221 } else {
222 return self.getStarterTier()
223 }
224 }
225
226 access(all) fun getStarterTier(): PricingTier {
227 return PricingTier(name: "Starter", minTokens: 0, pricePerK: 0.02, discount: 0.0)
228 }
229
230 /// Create LiteLLM handler
231 access(all) fun createLiteLLMHandler(): @LiteLLMHandler {
232 return <- create LiteLLMHandler()
233 }
234
235 init() {
236 self.VaultStoragePath = /storage/SimpleUsageSubscriptionVault
237 self.VaultPublicPath = /public/SimpleUsageSubscriptionVault
238 self.totalVaults = 0
239 }
240}