Smart Contract
FlunksGumDrop
A.807c3d470888cc48.FlunksGumDrop
1// FlunksGumDrop.cdc
2// Production-ready GumDrop contract for special events
3// This contract allows Flunk NFT holders to claim special GUM rewards during limited-time events
4
5access(all) contract FlunksGumDrop {
6
7 // Event emitted when a user claims their GumDrop
8 access(all) event GumDropClaimed(user: Address, amount: UInt64, timestamp: UFix64)
9
10 // Event emitted when the drop window changes
11 access(all) event DropWindowUpdated(startTime: UFix64, endTime: UFix64, isActive: Bool)
12
13 // Storage paths
14 access(all) let AdminStoragePath: StoragePath
15
16 // Contract state
17 access(all) var dropStartTime: UFix64
18 access(all) var dropEndTime: UFix64
19 access(all) var gumAmountPerFlunk: UInt64
20 access(all) var dropActive: Bool
21
22 // Track who has claimed (address -> claimed timestamp)
23 access(self) let claimRecord: {Address: UFix64}
24
25 // Admin resource for managing drops
26 access(all) resource Admin {
27 // Start a new GumDrop event
28 access(all) fun startDrop(startTime: UFix64, endTime: UFix64, gumPerFlunk: UInt64) {
29 pre {
30 endTime > startTime: "End time must be after start time"
31 gumPerFlunk > 0: "GUM amount must be positive"
32 }
33
34 FlunksGumDrop.dropStartTime = startTime
35 FlunksGumDrop.dropEndTime = endTime
36 FlunksGumDrop.gumAmountPerFlunk = gumPerFlunk
37 FlunksGumDrop.dropActive = true
38
39 emit DropWindowUpdated(startTime: startTime, endTime: endTime, isActive: true)
40 }
41
42 // End the current drop
43 access(all) fun endDrop() {
44 FlunksGumDrop.dropActive = false
45 emit DropWindowUpdated(startTime: 0.0, endTime: 0.0, isActive: false)
46 }
47
48 // Reset a user's claim (emergency only)
49 access(all) fun resetUserClaim(user: Address) {
50 let removed = FlunksGumDrop.claimRecord.remove(key: user)
51 }
52 }
53
54 // Check if drop is currently active
55 access(all) view fun checkDropActive(): Bool {
56 let now = getCurrentBlock().timestamp
57 return self.dropActive &&
58 now >= self.dropStartTime &&
59 now <= self.dropEndTime
60 }
61
62 // Get remaining time in seconds
63 access(all) view fun getTimeRemaining(): UFix64 {
64 if !self.checkDropActive() {
65 return 0.0
66 }
67 let now = getCurrentBlock().timestamp
68 if now >= self.dropEndTime {
69 return 0.0
70 }
71 return self.dropEndTime - now
72 }
73
74 // Check if user is eligible to claim
75 access(all) view fun isEligibleForGumDrop(user: Address): Bool {
76 // User is eligible if:
77 // 1. Drop is active
78 // 2. They haven't claimed yet
79 // 3. They own at least one Flunk NFT (checked in frontend/API)
80 return self.checkDropActive() &&
81 self.claimRecord[user] == nil
82 }
83
84 // Get drop info
85 access(all) view fun getGumDropInfo(): {String: AnyStruct} {
86 let now = getCurrentBlock().timestamp
87 let active = self.checkDropActive()
88
89 return {
90 "isActive": active,
91 "startTime": self.dropStartTime,
92 "endTime": self.dropEndTime,
93 "timeRemaining": active ? self.getTimeRemaining() : 0.0,
94 "gumPerFlunk": self.gumAmountPerFlunk,
95 "currentTime": now
96 }
97 }
98
99 // Claim GumDrop - records on-chain that user claimed
100 access(all) fun claimGumDrop(user: Address) {
101 pre {
102 self.checkDropActive(): "No active GumDrop event"
103 self.claimRecord[user] == nil: "Already claimed this drop"
104 }
105
106 // Record the claim
107 let now = getCurrentBlock().timestamp
108 self.claimRecord[user] = now
109
110 // Emit event (actual GUM crediting happens via Supabase API)
111 emit GumDropClaimed(user: user, amount: self.gumAmountPerFlunk, timestamp: now)
112 }
113
114 // Get user's claim status
115 access(all) view fun getUserClaimTime(user: Address): UFix64? {
116 return self.claimRecord[user]
117 }
118
119 init() {
120 // Set storage paths
121 self.AdminStoragePath = /storage/FlunksGumDropAdmin
122
123 // Initialize with no active drop
124 self.dropStartTime = 0.0
125 self.dropEndTime = 0.0
126 self.gumAmountPerFlunk = 100
127 self.dropActive = false
128 self.claimRecord = {}
129
130 // Create and store admin resource
131 let admin <- create Admin()
132 self.account.storage.save(<-admin, to: self.AdminStoragePath)
133 }
134}
135