Smart Contract

PinPin

A.3d4488a67d9b07e1.PinPin

Valid From

130,703,379

Deployed

1w ago
Feb 21, 2026, 03:04:15 PM UTC

Dependents

1 imports
1import FlowTransactionScheduler from 0xe467b9dd11fa00df 
2import FlowToken from 0x1654653399040a61
3import FungibleToken from 0xf233dcee88fe0abe
4
5access(all) contract PinPin {
6    // -----------------------------------------------------------------------
7    // PinPin contract-level fields.
8    // These contain actual values that are stored in the smart contract.
9    // -----------------------------------------------------------------------
10    access(all) var subscriptionFee: UFix64
11
12    // Events
13    access(all) event ContractInitialized()
14    access(all) event SubscriptionActivated(subscriber: Address)
15    // -----------------------------------------------------------------------
16    // PinPin account paths
17    // -----------------------------------------------------------------------
18  
19    access(all) let SubscriptionStoragePath: StoragePath
20    access(all) let SubscriptionPublicPath: PublicPath
21    access(all) let handlerStoragePath: StoragePath
22
23    // access(all) let feeVaultStoragePath: StoragePath
24    // access(all) let receiptsStoragePath: StoragePath
25
26    // -----------------------------------------------------------------------
27    // PinPin contract-level Composite Type definitions
28    // -----------------------------------------------------------------------
29
30    // Subscription resource
31    // This resource is used to store the subscription information
32    access(all) resource Subscription {
33        access(all) let subscriber: Address
34        access(all) let fee: UFix64
35        access(all) let cycles: UFix64
36        access(all) let startedAt: UFix64
37        access(all) let expiresAt: UFix64
38
39        init(subscriber: Address, fee: UFix64, cycles: UFix64, startedAt: UFix64, expiresAt: UFix64) {
40            self.subscriber = subscriber
41            self.fee = fee
42            self.cycles = cycles
43            self.startedAt = startedAt
44            self.expiresAt = expiresAt
45        }
46    }
47    // Subscription manager
48    // This storage is used to store and manage the subscriptions
49    // inside a user's account
50    access(all) resource SubscriptionManager {
51        access(all) var subscriptions: @[Subscription]
52
53        init() {
54            self.subscriptions <- []
55        }
56    }
57    // -----------------------------------------------------------------------
58    /// Handler resource that implements the Scheduled Transaction interface
59    access(all) resource Handler: FlowTransactionScheduler.TransactionHandler {
60
61        access(self) var feeVault: @FlowToken.Vault
62        access(self) var receipts: @[FlowTransactionScheduler.ScheduledTransaction]
63
64        access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) {
65            // Get PinPin's Flow vault ref
66            let pinPinVault = PinPin.account.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
67            // Get a reference to the vaultCap
68            // let ref <- self.feeVault.withdraw(amount: 0.1)
69            // Deposit 0 Flow on the PinPin account
70            pinPinVault.deposit(from: <- self.feeVault.withdraw(amount: 0.1)) 
71            log("Account /self.owner!.address has made a deposit for a subscription")
72            emit SubscriptionActivated(subscriber: self.owner!.address)
73            // Determine delay for the next transaction (default 3 seconds if none provided)
74            var delay: UFix64 = 60.0
75            if data != nil {
76                let t = data!.getType()
77                if t.isSubtype(of: Type<UFix64>()) {
78                    delay = data as! UFix64
79                }
80            }
81
82            let future = getCurrentBlock().timestamp + delay
83            let priority = FlowTransactionScheduler.Priority.Medium
84            let executionEffort: UInt64 = 1000
85
86            let estimate = FlowTransactionScheduler.estimate(
87                data: data,
88                timestamp: future,
89                priority: priority,
90                executionEffort: executionEffort
91            )       
92
93            assert(
94                estimate.timestamp != nil || priority == FlowTransactionScheduler.Priority.Low,
95                message: estimate.error ?? "estimation failed"
96            )   
97
98            // Withdraw FLOW fees from this resource's ownner account vault
99            let fees <- self.feeVault.withdraw(amount: estimate.flowFee ?? 0.0) as! @FlowToken.Vault   
100
101            // Issue a capability to the handler stored in this contract account
102            let handlerCap = PinPin.account.capabilities.storage
103                .issue<auth(FlowTransactionScheduler.Execute) &{FlowTransactionScheduler.TransactionHandler}>(/storage/PinPin)
104
105            let receipt: @FlowTransactionScheduler.ScheduledTransaction <- FlowTransactionScheduler.schedule(
106                handlerCap: handlerCap,
107                data: data,
108                timestamp: future,
109                priority: priority,
110                executionEffort: executionEffort,
111                fees: <-fees
112            )
113
114            log("Loop transaction id: ".concat(receipt.id.toString()).concat(" at ").concat(receipt.timestamp.toString()))
115            
116            destroy receipt
117        }
118
119        init(_ vault: @FlowToken.Vault,_ consentedFee: UFix64,_ consentedCycles: UFix64) {
120            pre {
121                PinPin.subscriptionFee == consentedFee: "Consented fee must be equal to contract's current fee"
122                vault.balance >= consentedCycles * consentedFee: "Vault's balance must be equal or greater than Fees x Cycles"
123            }
124            self.feeVault <- vault
125            self.receipts <- []
126        }
127    }
128
129    /// Factory for the handler resource
130    access(all) fun createHandler(vault: @FlowToken.Vault, consentedFee: UFix64, consentedCycles: UFix64): @Handler {
131        return <- create Handler(<- vault, consentedFee, consentedCycles)
132    }
133
134    init() {
135        self.subscriptionFee = 0.1
136
137        // Set the named paths
138        let identifier = "PinPin_".concat(self.account.address.toString())
139        self.SubscriptionStoragePath = StoragePath(identifier: identifier)!
140		self.SubscriptionPublicPath = PublicPath(identifier: identifier)!
141        self.handlerStoragePath = StoragePath(identifier: identifier)!
142        emit ContractInitialized()
143    }
144}