DeploySEALED
○#^□╳@&~%■!■!█*╳▫█%●█&*╱◇~◆○■▫▒◇▓@$#▪▪*▓?▫#■#░▫&▒╳&▪□◆&^○▓░■$^╲○
Transaction ID
Execution Fee
0.00000599 FLOWTransaction Summary
DeployContract deployment
Contract deployment
Script Arguments
0nameString
PayAsYouGoBilling
1codeString
import FungibleToken from 0xf233dcee88fe0abe
access(all) contract PayAsYouGoBilling {
/// Storage and public paths for user allowances
access(all) let AllowanceStoragePath: StoragePath
access(all) let AllowancePublicPath: PublicPath
/// Minimal interface exposed to providers for charging
access(all) resource interface Charge {
access(all) fun charge(amount: UFix64)
access(all) view fun getRemainingCapacity(): UFix64
access(all) view fun getConfig(): PayAsYouGoBilling.Config
}
/// Immutable configuration for an allowance
access(all) struct Config {
access(all) let minCharge: UFix64
access(all) let monthlyMax: UFix64
access(all) let periodSeconds: UFix64
access(all) let vaultType: Type
access(all) let user: Address
access(all) let provider: Address
init(
minCharge: UFix64,
monthlyMax: UFix64,
periodSeconds: UFix64,
vaultType: Type,
user: Address,
provider: Address
) {
pre {
minCharge > 0.0: "minCharge must be > 0"
monthlyMax >= minCharge: "monthlyMax must be >= minCharge"
periodSeconds > 0.0: "periodSeconds must be > 0"
}
self.minCharge = minCharge
self.monthlyMax = monthlyMax
self.periodSeconds = periodSeconds
self.vaultType = vaultType
self.user = user
self.provider = provider
}
}
/// Allowance resource held by the USER, callable by the PROVIDER through a public capability
access(all) resource Allowance: Charge {
/// Capability to withdraw from the user's vault of the configured token
access(contract) let userVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
/// Capability for the provider's receiver to receive the configured token
access(contract) let providerReceiverCap: Capability<&{FungibleToken.Receiver}>
/// Configuration snapshot
access(all) let config: PayAsYouGoBilling.Config
/// Rolling period tracking
access(contract) var periodStartTimestamp: UFix64
access(contract) var chargedThisPeriod: UFix64
init(
userVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
providerReceiverCap: Capability<&{FungibleToken.Receiver}>,
config: PayAsYouGoBilling.Config
) {
// Validate caps and type matching
let userVault = userVaultCap.borrow()
?? panic("Invalid user vault capability")
let providerReceiver = providerReceiverCap.borrow()
?? panic("Invalid provider receiver capability")
if userVault.getType().identifier != providerReceiver.getType().identifier {
panic("Mismatched token types")
}
if userVault.getType().identifier != config.vaultType.identifier {
panic("Config vault type mismatch")
}
self.userVaultCap = userVaultCap
self.providerReceiverCap = providerReceiverCap
self.config = config
self.periodStartTimestamp = getCurrentBlock().timestamp
self.chargedThisPeriod = 0.0
}
/// Ensure the rolling period is correctly maintained and reset when elapsed
access(contract) fun ensurePeriodFresh() {
let now: UFix64 = getCurrentBlock().timestamp
if now >= self.periodStartTimestamp + self.config.periodSeconds {
self.periodStartTimestamp = now
self.chargedThisPeriod = 0.0
}
}
/// Returns the remaining capacity for this period (after refreshing the window)
access(all) view fun getRemainingCapacity(): UFix64 {
let now: UFix64 = getCurrentBlock().timestamp
let periodEnd: UFix64 = self.periodStartTimestamp + self.config.periodSeconds
if now >= periodEnd {
return self.config.monthlyMax
}
if self.chargedThisPeriod >= self.config.monthlyMax {
return 0.0
}
return self.config.monthlyMax - self.chargedThisPeriod
}
/// Returns immutable configuration
access(all) view fun getConfig(): PayAsYouGoBilling.Config {
return self.config
}
/// Provider-triggered charge respecting min per-charge and monthly cap
access(all) fun charge(amount: UFix64) {
// Validate against current period capacity without mutating
if amount < self.config.minCharge {
panic("Amount below minCharge")
}
if amount > self.getRemainingCapacity() {
panic("Amount exceeds remaining capacity for this period")
}
// Refresh period and perform the charge
self.ensurePeriodFresh()
let vault <- self.userVaultCap.borrow()!.withdraw(amount: amount)
self.providerReceiverCap.borrow()!.deposit(from: <-vault)
self.chargedThisPeriod = self.chargedThisPeriod + amount
}
}
/// Create a new allowance. Caller should save it and link a public capability restricted to Charge
access(all) fun createAllowance(
userVaultCap: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>,
providerReceiverCap: Capability<&{FungibleToken.Receiver}>,
minCharge: UFix64,
monthlyMax: UFix64,
periodSeconds: UFix64,
user: Address,
provider: Address
): @PayAsYouGoBilling.Allowance {
let vt = userVaultCap.borrow()
?? panic("Invalid user vault cap when creating allowance")
let config = PayAsYouGoBilling.Config(
minCharge: minCharge,
monthlyMax: monthlyMax,
periodSeconds: periodSeconds,
vaultType: vt.getType(),
user: user,
provider: provider
)
return <- create PayAsYouGoBilling.Allowance(
userVaultCap: userVaultCap,
providerReceiverCap: providerReceiverCap,
config: config
)
}
init() {
self.AllowanceStoragePath = StoragePath(identifier: "paygAllowance")!
self.AllowancePublicPath = PublicPath(identifier: "paygAllowance")!
}
}
Cadence Script
1transaction(name: String, code: String ) {
2 prepare(signer: auth(AddContract) &Account) {
3 signer.contracts.add(name: name, code: code.utf8 )
4 }
5 }