Smart Contract

Flunks

A.807c3d470888cc48.Flunks

Valid From

132,003,572

Deployed

2w ago
Feb 11, 2026, 06:36:46 PM UTC

Dependents

3493 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import ViewResolver from 0x1d7e57aa55817448
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5
6
7access(all)
8contract Flunks: NonFungibleToken{ 
9	access(all)
10	event ContractInitialized()
11	
12	access(all)
13	event SetCreated(setID: UInt64)
14	
15	access(all)
16	event NFTTemplateCreated(templateID: UInt64, metadata:{ String: String})
17	
18	access(all)
19	event Withdraw(id: UInt64, from: Address?)
20	
21	access(all)
22	event Deposit(id: UInt64, to: Address?)
23	
24	access(all)
25	event Minted(id: UInt64, templateID: UInt64)
26	
27	access(all)
28	event TemplateAddedToSet(setID: UInt64, templateID: UInt64)
29	
30	access(all)
31	event TemplateLockedFromSet(setID: UInt64, templateID: UInt64)
32	
33	access(all)
34	event TemplateUpdated(template: FlunksTemplate)
35	
36	access(all)
37	event SetLocked(setID: UInt64)
38	
39	access(all)
40	let CollectionStoragePath: StoragePath
41	
42	access(all)
43	let CollectionPublicPath: PublicPath
44	
45	access(all)
46	let AdminStoragePath: StoragePath
47	
48	access(all)
49	var totalSupply: UInt64
50	
51	access(all)
52	var nextTemplateID: UInt64
53	
54	access(all)
55	var nextSetID: UInt64
56	
57	access(self)
58	var FlunksTemplates:{ UInt64: FlunksTemplate}
59	
60	access(self)
61	var sets: @{UInt64: Set}
62	
63	access(all)
64	resource interface FlunksCollectionPublic{ 
65		access(all)
66		fun deposit(token: @{NonFungibleToken.NFT}): Void
67		
68		access(all)
69		view fun getIDs(): [UInt64]
70		
71		access(all)
72		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
73		
74		access(all)
75		fun borrowFlunks(id: UInt64): &Flunks.NFT?{ 
76			post{ 
77				result == nil || result?.id == id:
78					"Cannot borrow Flunks reference: The ID of the returned reference is incorrect"
79			}
80		}
81	}
82	
83	access(all)
84	struct FlunksTemplate{ 
85		access(all)
86		let templateID: UInt64
87		
88		access(all)
89		var name: String
90		
91		access(all)
92		var description: String
93		
94		access(all)
95		var locked: Bool
96		
97		access(all)
98		var addedToSet: UInt64
99		
100		access(self)
101		var metadata:{ String: String}
102		
103		access(all)
104		fun getMetadata():{ String: String}{ 
105			return self.metadata
106		}
107		
108		access(all)
109		fun lockTemplate(){ 
110			self.locked = true
111		}
112		
113		access(all)
114		fun updateMetadata(newMetadata:{ String: String}){ 
115			pre{ 
116				newMetadata.length != 0:
117					"New Template metadata cannot be empty"
118			}
119			self.metadata = newMetadata
120		}
121		
122		access(all)
123		fun markAddedToSet(setID: UInt64){ 
124			self.addedToSet = setID
125		}
126		
127		init(templateID: UInt64, name: String, description: String, metadata:{ String: String}){ 
128			pre{ 
129				metadata.length != 0:
130					"New Template metadata cannot be empty"
131			}
132			self.templateID = templateID
133			self.name = name
134			self.description = description
135			self.metadata = metadata
136			self.locked = false
137			self.addedToSet = 0
138			Flunks.nextTemplateID = Flunks.nextTemplateID + 1
139			emit NFTTemplateCreated(templateID: self.templateID, metadata: self.metadata)
140		}
141	}
142	
143	access(all)
144	struct PixelUrl{ 
145		access(all)
146		let url: String
147		
148		init(url: String){ 
149			self.url = url
150		}
151	}
152	
153	access(all)
154	resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver{ 
155		access(all)
156		let id: UInt64
157		
158		access(all)
159		let templateID: UInt64
160		
161		access(all)
162		let serialNumber: UInt64
163		
164		access(all)
165		view fun getViews(): [Type]{ 
166			return [Type<MetadataViews.Display>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.Traits>(), Type<MetadataViews.Edition>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.Serial>()]
167		}
168		
169		access(all)
170		fun resolveView(_ view: Type): AnyStruct?{ 
171			switch view{ 
172				case Type<Flunks.PixelUrl>():
173					let pixelUri = self.getNFTTemplate().getMetadata()["pixelUri"]
174					if pixelUri == nil{ 
175						return nil
176					} else{ 
177						let pixelUrlLen = (pixelUri!).length
178						let encodedPart = (pixelUri!).slice(from: pixelUrlLen - 68, upTo: pixelUrlLen)
179						return "https://storage.googleapis.com/flunks_public/flunks/".concat(encodedPart)
180					}
181				case Type<MetadataViews.Display>():
182					return MetadataViews.Display(name: self.getNFTTemplate().name.concat(" #").concat(self.templateID.toString()), description: self.getNFTTemplate().description, thumbnail: MetadataViews.HTTPFile(url: self.getNFTTemplate().getMetadata()["uri"]!))
183				case Type<MetadataViews.ExternalURL>():
184					return MetadataViews.ExternalURL("https://flunks.net/")
185				case Type<MetadataViews.NFTCollectionData>():
186					return MetadataViews.NFTCollectionData(storagePath: Flunks.CollectionStoragePath, publicPath: Flunks.CollectionPublicPath, publicCollection: Type<&Flunks.Collection>(), publicLinkedType: Type<&Flunks.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
187							return <-Flunks.createEmptyCollection(nftType: Type<@Flunks.NFT>())
188						})
189				case Type<MetadataViews.NFTCollectionDisplay>():
190					let bannerMedia = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://storage.googleapis.com/flunks_public/website-assets/banner_2023.png"), mediaType: "image/png")
191					let logoFull = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://storage.googleapis.com/flunks_public/images/F%20for%20flowty.png"), mediaType: "image/png")
192					return MetadataViews.NFTCollectionDisplay(name: "Flunks", description: "Flunks are cute but mischievous high-schoolers wreaking havoc #onFlow", externalURL: MetadataViews.ExternalURL("https://flunks.net/"), squareImage: logoFull, bannerImage: bannerMedia, socials:{ "twitter": MetadataViews.ExternalURL("https://twitter.com/flunks_nft")})
193				case Type<MetadataViews.Traits>():
194					let excludedTraits = ["mimetype", "uri", "pixelUri", "path", "cid"]
195					let traitsView = MetadataViews.dictToTraits(dict: self.getNFTTemplate().getMetadata(), excludedNames: excludedTraits)
196					return traitsView
197				case Type<MetadataViews.Edition>():
198					return MetadataViews.Edition(name: "Flunks", number: self.templateID, max: 9999)
199				case Type<MetadataViews.Serial>():
200					return MetadataViews.Serial(self.templateID)
201				case Type<MetadataViews.Royalties>():
202					// Note: replace the address for different merchant accounts across various networks
203					let merchant = getAccount(0x0cce91b08cb58286)
204					return MetadataViews.Royalties([MetadataViews.Royalty(receiver: merchant.capabilities.get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)!, cut: 0.05, description: "Flunks creator royalty in DUC")])
205			}
206			return nil
207		}
208		
209		access(all)
210		fun getNFTTemplate(): FlunksTemplate{ 
211			return Flunks.FlunksTemplates[self.templateID]!
212		}
213		
214		access(all)
215		fun getNFTMetadata():{ String: String}{ 
216			return (Flunks.FlunksTemplates[self.templateID]!).getMetadata()
217		}
218		
219		access(all)
220		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
221			return <-create Collection()
222		}
223		
224		init(initID: UInt64, initTemplateID: UInt64, serialNumber: UInt64){ 
225			self.id = initID
226			self.templateID = initTemplateID
227			self.serialNumber = serialNumber
228		}
229	}
230	
231	access(all)
232	resource Collection: FlunksCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection{ 
233		access(all)
234		var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
235		
236		access(NonFungibleToken.Withdraw)
237		fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
238			let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
239			emit Withdraw(id: token.id, from: self.owner?.address)
240			return <-token
241		}
242		
243		access(all)
244		fun deposit(token: @{NonFungibleToken.NFT}): Void{ 
245			let token <- token as! @Flunks.NFT
246			let id: UInt64 = token.id
247			let oldToken <- self.ownedNFTs[id] <- token
248			emit Deposit(id: id, to: self.owner?.address)
249			destroy oldToken
250		}
251		
252		access(all)
253		fun batchDeposit(collection: @Collection){ 
254			let keys = collection.getIDs()
255			for key in keys{ 
256				self.deposit(token: <-collection.withdraw(withdrawID: key))
257			}
258			destroy collection
259		}
260		
261		access(all)
262		view fun getIDs(): [UInt64]{ 
263			return self.ownedNFTs.keys
264		}
265		
266		access(all)
267		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
268			return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
269		}
270		
271		access(all)
272		fun borrowFlunks(id: UInt64): &Flunks.NFT?{ 
273			if self.ownedNFTs[id] != nil{ 
274				let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
275				return ref as! &Flunks.NFT
276			} else{ 
277				return nil
278			}
279		}
280		
281		access(all)
282		view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
283			let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
284			let exampleNFT = nft as! &Flunks.NFT
285			return exampleNFT as &{ViewResolver.Resolver}
286		}
287		
288		access(all)
289                view fun getSupportedNFTTypes(): {Type: Bool} {
290                        return {Type<@Flunks.NFT>(): true}
291                }
292
293                access(all)
294                view fun isSupportedNFTType(type: Type): Bool {
295                        return type == Type<@Flunks.NFT>()
296                }
297		
298		access(all)
299		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
300			return <-create Collection()
301		}
302		
303		init(){ 
304			self.ownedNFTs <-{} 
305		}
306	}
307	
308	access(all)
309	fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
310		return <-create Collection()
311	}
312	
313	access(all)
314	resource Set{ 
315		access(all)
316		let setID: UInt64
317		
318		access(all)
319		let name: String
320		
321		access(self)
322		var templateIDs: [UInt64]
323		
324		access(self)
325		var availableTemplateIDs: [UInt64]
326		
327		access(self)
328		var lockedTemplates:{ UInt64: Bool}
329		
330		access(all)
331		var locked: Bool
332		
333		access(all)
334		var nextSetSerialNumber: UInt64
335		
336		access(all)
337		var isPublic: Bool
338		
339		init(name: String){ 
340			self.name = name
341			self.setID = Flunks.nextSetID
342			self.templateIDs = []
343			self.lockedTemplates ={} 
344			self.locked = false
345			self.availableTemplateIDs = []
346			self.nextSetSerialNumber = 1
347			self.isPublic = false
348			Flunks.nextSetID = Flunks.nextSetID + 1
349			emit SetCreated(setID: self.setID)
350		}
351		
352		access(all)
353		fun getAvailableTemplateIDs(): [UInt64]{ 
354			return self.availableTemplateIDs
355		}
356		
357		access(all)
358		fun makeSetPublic(){ 
359			self.isPublic = true
360		}
361		
362		access(all)
363		fun makeSetPrivate(){ 
364			self.isPublic = false
365		}
366		
367		access(all)
368		fun addTemplate(templateID: UInt64){ 
369			pre{ 
370				Flunks.FlunksTemplates[templateID] != nil:
371					"Template doesn't exist"
372				!self.locked:
373					"Cannot add template - set is locked"
374				!self.templateIDs.contains(templateID):
375					"Cannot add template - template is already added to the set"
376				!((Flunks.FlunksTemplates[templateID]!).addedToSet != 0):
377					"Cannot add template - template is already added to another set"
378			}
379			self.templateIDs.append(templateID)
380			self.availableTemplateIDs.append(templateID)
381			self.lockedTemplates[templateID] = false
382			(Flunks.FlunksTemplates[templateID]!).markAddedToSet(setID: self.setID)
383			emit TemplateAddedToSet(setID: self.setID, templateID: templateID)
384		}
385		
386		access(all)
387		fun addTemplates(templateIDs: [UInt64]){ 
388			for template in templateIDs{ 
389				self.addTemplate(templateID: template)
390			}
391		}
392		
393		access(all)
394		fun lockTemplate(templateID: UInt64){ 
395			pre{ 
396				self.lockedTemplates[templateID] != nil:
397					"Cannot lock the template: Template is locked already!"
398				!self.availableTemplateIDs.contains(templateID):
399					"Cannot lock a not yet minted template!"
400			}
401			if !self.lockedTemplates[templateID]!{ 
402				self.lockedTemplates[templateID] = true
403				emit TemplateLockedFromSet(setID: self.setID, templateID: templateID)
404			}
405		}
406		
407		access(all)
408		fun lockAllTemplates(){ 
409			for template in self.templateIDs{ 
410				self.lockTemplate(templateID: template)
411			}
412		}
413		
414		access(all)
415		fun lock(){ 
416			if !self.locked{ 
417				self.locked = true
418				emit SetLocked(setID: self.setID)
419			}
420		}
421		
422		access(all)
423		fun updateTemplateMetadata(templateID: UInt64, newMetadata:{ String: String}): FlunksTemplate{ 
424			pre{ 
425				Flunks.FlunksTemplates[templateID] != nil:
426					"Template doesn't exist"
427				!self.locked:
428					"Cannot edit template - set is locked"
429			}
430			(Flunks.FlunksTemplates[templateID]!).updateMetadata(newMetadata: newMetadata)
431			emit TemplateUpdated(template: Flunks.FlunksTemplates[templateID]!)
432			return Flunks.FlunksTemplates[templateID]!
433		}
434	}
435	
436	access(all)
437	resource Admin{ 						
438		access(all)
439		fun borrowSet(setID: UInt64): &Set{ 
440			pre{ 
441				Flunks.sets[setID] != nil:
442					"Cannot borrow Set: The Set doesn't exist"
443			}
444			return (&Flunks.sets[setID] as &Set?)!
445		}
446		
447		access(all)
448		fun updateFlunksTemplate(templateID: UInt64, newMetadata:{ String: String}){ 
449			pre{ 
450				Flunks.FlunksTemplates.containsKey(templateID):
451					"Template does not exit."
452			}
453			(Flunks.FlunksTemplates[templateID]!).updateMetadata(newMetadata: newMetadata)
454		}
455	}
456	
457	access(all)
458	fun getFlunksTemplateByID(templateID: UInt64): Flunks.FlunksTemplate{ 
459		return Flunks.FlunksTemplates[templateID]!
460	}
461	
462	access(all)
463	fun getFlunksTemplates():{ UInt64: Flunks.FlunksTemplate}{ 
464		return Flunks.FlunksTemplates
465	}
466	
467	access(all)
468	fun getAvailableTemplateIDsInSet(setID: UInt64): [UInt64]{ 
469		pre{ 
470			Flunks.sets[setID] != nil:
471				"Cannot borrow Set: The Set doesn't exist"
472		}
473		let set = (&Flunks.sets[setID] as &Set?)!
474		return set.getAvailableTemplateIDs()
475	}
476
477	access(all) view fun getContractViews(resourceType: Type?): [Type] {
478		return [
479			Type<MetadataViews.NFTCollectionData>(),
480			Type<MetadataViews.NFTCollectionDisplay>(),
481			Type<MetadataViews.EVMBridgedMetadata>(),
482			Type<MetadataViews.Royalties>()
483		]
484	}
485
486	access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
487		switch viewType {
488			case Type<MetadataViews.NFTCollectionData>():
489				return MetadataViews.NFTCollectionData(
490					storagePath: Flunks.CollectionStoragePath,
491					publicPath: Flunks.CollectionPublicPath,
492					publicCollection: Type<&Flunks.Collection>(),
493					publicLinkedType: Type<&Flunks.Collection>(),
494					createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
495						return <-Flunks.createEmptyCollection(nftType: Type<@Flunks.NFT>())
496					})
497				)
498			case Type<MetadataViews.NFTCollectionDisplay>():
499				return MetadataViews.NFTCollectionDisplay(
500					name: "Flunks",
501					description: "Flunks are cute but mischievous high-schoolers wreaking havoc #onFlow",
502					externalURL: MetadataViews.ExternalURL("https://flunks.net/"),
503					squareImage: MetadataViews.Media(
504						file: MetadataViews.HTTPFile(url: "https://storage.googleapis.com/flunks_public/images/F%20for%20flowty.png"),
505						mediaType: "image/png"
506					),
507					bannerImage: MetadataViews.Media(
508						file: MetadataViews.HTTPFile(url: "https://storage.googleapis.com/flunks_public/website-assets/banner_2023.png"),
509						mediaType: "image/png"
510					),
511					socials: {
512						"twitter": MetadataViews.ExternalURL("https://twitter.com/flunks_nft")
513					}
514				)
515			case Type<MetadataViews.EVMBridgedMetadata>():
516				return MetadataViews.EVMBridgedMetadata(
517					name: "Flunks",
518					symbol: "FLNK",
519					uri: MetadataViews.URI(
520						baseURI: "https://flunks.net/nft/",
521						value: ""
522					)
523				)
524			case Type<MetadataViews.Royalties>():
525				return MetadataViews.Royalties([
526					MetadataViews.Royalty(
527						receiver: getAccount(0xbfffec679fff3a94)
528							.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver),
529						cut: 0.10,
530						description: "Flunks creator royalty"
531					)
532				])
533		}
534		return nil
535	}
536	
537	init(){ 
538		self.CollectionStoragePath = /storage/FlunksCollection
539		self.CollectionPublicPath = /public/FlunksCollection
540		self.AdminStoragePath = /storage/FlunksAdmin
541		self.totalSupply = 0
542		self.nextTemplateID = 1
543		self.nextSetID = 1
544		self.sets <-{} 
545		self.FlunksTemplates ={} 
546		let admin <- create Admin()
547		self.account.storage.save(<-admin, to: self.AdminStoragePath)
548		emit ContractInitialized()
549	}
550}
551