Smart Contract

Flovatar

A.921ea449dffec68a.Flovatar

Valid From

86,755,278

Deployed

2w ago
Feb 11, 2026, 06:35:26 PM UTC

Dependents

3535 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import FlowToken from 0x1654653399040a61
4import FlovatarComponentTemplate from 0x921ea449dffec68a
5import FlovatarComponent from 0x921ea449dffec68a
6import FlovatarPack from 0x921ea449dffec68a
7import MetadataViews from 0x1d7e57aa55817448
8import FlovatarDustToken from 0x921ea449dffec68a
9import ViewResolver from 0x1d7e57aa55817448
10
11/*
12
13 The contract that defines the Flovatar NFT and a Collection to manage them
14
15Base components that will be used to generate the unique combination of the Flovatar
16'body', 'hair', 'facialhair', 'eyes', 'nose', 'mouth', 'clothing'
17
18Extra components that can be added in a second moment
19'accessory', 'hat', eyeglass', 'background'
20
21
22This contract contains also the Admin resource that can be used to manage and generate all the other ones (Components, Templates, Packs).
23
24 */
25
26access(all)
27contract Flovatar: NonFungibleToken{ 
28	access(all)
29	let CollectionStoragePath: StoragePath
30	
31	access(all)
32	let CollectionPublicPath: PublicPath
33	
34	access(all)
35	let AdminStoragePath: StoragePath
36	
37	// These will be used in the Marketplace to pay out
38	// royalties to the creator and to the marketplace
39	access(account)
40	var royaltyCut: UFix64
41	
42	access(account)
43	var marketplaceCut: UFix64
44	
45	// Here we keep track of all the Flovatar unique combinations and names
46	// that people will generate to make sure that there are no duplicates
47	access(all)
48	var totalSupply: UInt64
49	
50	access(contract)
51	let mintedCombinations:{ String: Bool}
52	
53	access(contract)
54	let mintedNames:{ String: Bool}
55	
56	access(all)
57	event ContractInitialized()
58	
59	access(all)
60	event Withdraw(id: UInt64, from: Address?)
61	
62	access(all)
63	event Deposit(id: UInt64, to: Address?)
64	
65	access(all)
66	event Created(id: UInt64, metadata: Metadata)
67	
68	access(all)
69	event Updated(id: UInt64)
70	
71	access(all)
72	event NameSet(id: UInt64, name: String)
73	
74	access(all)
75	event PositionChanged(id: UInt64, position: String)
76	
77	access(all)
78	event StoryAdded(id: UInt64, story: String)
79	
80	access(all)
81	event Unlocked3DFile(id: UInt64)
82	
83	access(all)
84	event UnlockedChat(id: UInt64)
85	
86	access(all)
87	struct Royalties{ 
88		access(all)
89		let royalty: [Royalty]
90		
91		init(royalty: [Royalty]){ 
92			self.royalty = royalty
93		}
94	}
95	
96	access(all)
97	enum RoyaltyType: UInt8{ 
98		access(all)
99		case fixed
100		
101		access(all)
102		case percentage
103	}
104	
105	access(all)
106	struct Royalty{ 
107		access(all)
108		let wallet: Capability<&{FungibleToken.Receiver}>
109		
110		access(all)
111		let cut: UFix64
112		
113		//can be percentage
114		access(all)
115		let type: RoyaltyType
116		
117		init(wallet: Capability<&{FungibleToken.Receiver}>, cut: UFix64, type: RoyaltyType){ 
118			self.wallet = wallet
119			self.cut = cut
120			self.type = type
121		}
122	}
123	
124	// This Metadata struct contains all the most important informations about the Flovatar
125	access(all)
126	struct Metadata{ 
127		access(all)
128		let mint: UInt64
129		
130		access(all)
131		let series: UInt32
132		
133		access(all)
134		let svg: String
135		
136		access(all)
137		let combination: String
138		
139		access(all)
140		let creatorAddress: Address
141		
142		access(self)
143		let components:{ String: UInt64}
144		
145		access(all)
146		let rareCount: UInt8
147		
148		access(all)
149		let epicCount: UInt8
150		
151		access(all)
152		let legendaryCount: UInt8
153		
154		init(mint: UInt64, series: UInt32, svg: String, combination: String, creatorAddress: Address, components:{ String: UInt64}, rareCount: UInt8, epicCount: UInt8, legendaryCount: UInt8){ 
155			self.mint = mint
156			self.series = series
157			self.svg = svg
158			self.combination = combination
159			self.creatorAddress = creatorAddress
160			self.components = components
161			self.rareCount = rareCount
162			self.epicCount = epicCount
163			self.legendaryCount = legendaryCount
164		}
165		
166		access(all)
167		fun getComponents():{ String: UInt64}{ 
168			return self.components
169		}
170	}
171
172	access(all) entitlement PrivateEnt
173	
174	// The public interface can show metadata and the content for the Flovatar.
175	// In addition to it, it provides methods to access the additional optional
176	// components (accessory, hat, eyeglasses, background) for everyone.
177	access(all)
178	resource interface Public{ 
179		access(all)
180		let id: UInt64
181		
182		access(contract)
183		let metadata: Metadata
184		
185		access(contract)
186		let royalties: Royalties
187		
188		// these three are added because I think they will be in the standard. At least Dieter thinks it will be needed
189		access(contract)
190		var name: String
191		
192		access(all)
193		let description: String
194		
195		access(all)
196		let schema: String?
197		
198		access(all)
199		fun getName(): String
200		
201		access(all)
202		fun getAccessory(): UInt64?
203		
204		access(all)
205		fun getHat(): UInt64?
206		
207		access(all)
208		fun getEyeglasses(): UInt64?
209		
210		access(all)
211		fun getBackground(): UInt64?
212		
213		access(all)
214		fun getSvg(): String
215		
216		access(all)
217		fun getMetadata(): Metadata
218		
219		access(all)
220		fun getRoyalties(): Royalties
221		
222		access(all)
223		fun getBio():{ String: String}
224		
225		access(all)
226		fun getRarityScore(): UFix64
227	}
228	
229	//The private interface can update the Accessory, Hat, Eyeglasses and Background
230	//for the Flovatar and is accessible only to the owner of the NFT
231	access(all)
232	resource interface Private{ 
233		access(Flovatar.PrivateEnt)
234		fun setName(name: String, vault: @{FungibleToken.Vault}): String
235		
236		access(Flovatar.PrivateEnt)
237		fun addStory(text: String, vault: @{FungibleToken.Vault}): String
238		
239		access(Flovatar.PrivateEnt)
240		fun unlock3DFile(vault: @{FungibleToken.Vault})
241		
242		access(Flovatar.PrivateEnt)
243		fun unlockChat(vault: @{FungibleToken.Vault})
244		
245		access(Flovatar.PrivateEnt)
246		fun setPosition(latitude: Fix64, longitude: Fix64, vault: @{FungibleToken.Vault}): String
247		
248		access(Flovatar.PrivateEnt)
249		fun setAccessory(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?
250		
251		access(Flovatar.PrivateEnt)
252		fun setHat(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?
253		
254		access(Flovatar.PrivateEnt)
255		fun setEyeglasses(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?
256		
257		access(Flovatar.PrivateEnt)
258		fun setBackground(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?
259		
260		access(Flovatar.PrivateEnt)
261		fun removeAccessory(): @FlovatarComponent.NFT?
262		
263		access(Flovatar.PrivateEnt)
264		fun removeHat(): @FlovatarComponent.NFT?
265		
266		access(Flovatar.PrivateEnt)
267		fun removeEyeglasses(): @FlovatarComponent.NFT?
268		
269		access(Flovatar.PrivateEnt)
270		fun removeBackground(): @FlovatarComponent.NFT?
271	}
272	
273	//The NFT resource that implements both Private and Public interfaces
274	access(all)
275	resource NFT: NonFungibleToken.NFT, Public, Private{ 
276		access(all)
277		let id: UInt64
278		
279		access(contract)
280		let metadata: Metadata
281		
282		access(contract)
283		let royalties: Royalties
284		
285		access(contract)
286		var accessory: @FlovatarComponent.NFT?
287		
288		access(contract)
289		var hat: @FlovatarComponent.NFT?
290		
291		access(contract)
292		var eyeglasses: @FlovatarComponent.NFT?
293		
294		access(contract)
295		var background: @FlovatarComponent.NFT?
296		
297		access(contract)
298		var name: String
299		
300		access(all)
301		let description: String
302		
303		access(all)
304		let schema: String?
305		
306		access(self)
307		let bio:{ String: String}
308		
309		init(metadata: Metadata, royalties: Royalties){ 
310			Flovatar.totalSupply = Flovatar.totalSupply + UInt64(1)
311			self.id = Flovatar.totalSupply
312			self.metadata = metadata
313			self.royalties = royalties
314			self.accessory <- nil
315			self.hat <- nil
316			self.eyeglasses <- nil
317			self.background <- nil
318			self.schema = nil
319			self.name = ""
320			self.description = ""
321			self.bio ={} 
322		}
323		
324		access(all)
325		fun getID(): UInt64{ 
326			return self.id
327		}
328		
329		access(all)
330		fun getMetadata(): Metadata{ 
331			return self.metadata
332		}
333		
334		access(all)
335		fun getRoyalties(): Royalties{ 
336			return self.royalties
337		}
338		
339		access(all)
340		fun getBio():{ String: String}{ 
341			return self.bio
342		}
343		
344		access(all)
345		fun getName(): String{ 
346			return self.name
347		}
348		
349		// This will allow to change the Name of the Flovatar only once.
350		// It checks for the current name is empty, otherwise it will throw an error.
351		// $DUST vault must contain 100 tokens that will be burned in the process
352		access(Flovatar.PrivateEnt)
353		fun setName(name: String, vault: @{FungibleToken.Vault}): String{ 
354			pre{ 
355				// TODO: Make sure that the text of the name is sanitized
356				//and that bad words are not accepted?
357				name.length > 2:
358					"The name is too short"
359				name.length < 32:
360					"The name is too long"
361				self.name == "":
362					"The name has already been set"
363				vault.balance == 100.0:
364					"The amount of $DUST is not correct"
365				vault.isInstance(Type<@FlovatarDustToken.Vault>()):
366					"Vault not of the right Token Type"
367			}
368			
369			// Makes sure that the name is available and not taken already
370			if Flovatar.checkNameAvailable(name: name) == false{ 
371				panic("This name has already been taken")
372			}
373			destroy vault
374			self.name = name
375			
376			// Adds the name to the array to remember it
377			Flovatar.addMintedName(name: name)
378			emit NameSet(id: self.id, name: name)
379			return self.name
380		}
381		
382		// This will allow to add a text Story to the Flovatar Bio.
383		// The String will be concatenated each time.
384		// There is a limit of 300 characters per story but there is no limit in the full concatenated story length
385		// $DUST vault must contain 50 tokens that will be burned in the process
386		access(Flovatar.PrivateEnt)
387		fun addStory(text: String, vault: @{FungibleToken.Vault}): String{ 
388			pre{ 
389				// TODO: Make sure that the text of the name is sanitized
390				//and that bad words are not accepted?
391				text.length > 0:
392					"The text is too short"
393				text.length <= 300:
394					"The text is too long"
395				vault.balance == 50.0:
396					"The amount of $DUST is not correct"
397				vault.isInstance(Type<@FlovatarDustToken.Vault>()):
398					"Vault not of the right Token Type"
399			}
400			destroy vault
401			let currentStory: String = self.bio["story"] ?? ""
402			let story: String = currentStory.concat("\n").concat(text)
403			self.bio.insert(key: "story", story)
404			emit StoryAdded(id: self.id, story: story)
405			return story
406		}
407		
408		// This will unlock the 3D files for a Flovatar.
409		// $DUST vault must contain 150 tokens that will be burned in the process
410		access(Flovatar.PrivateEnt)
411		fun unlock3DFile(vault: @{FungibleToken.Vault}){ 
412			pre{ 
413				self.bio["3d"] == nil:
414					"The 3D File has been already unlocked"
415				vault.balance == 150.0:
416					"The amount of $DUST is not correct"
417				vault.isInstance(Type<@FlovatarDustToken.Vault>()):
418					"Vault not of the right Token Type"
419			}
420			destroy vault
421			self.bio.insert(key: "3d", "true")
422			emit Unlocked3DFile(id: self.id)
423		}
424		
425		// This will enable the chat customization for a Flovatar.
426		// $DUST vault must contain 5000 tokens that will be burned in the process
427		access(Flovatar.PrivateEnt)
428		fun unlockChat(vault: @{FungibleToken.Vault}){ 
429			pre{ 
430				self.bio["chat"] == nil:
431					"The Chat has been already unlocked"
432				vault.balance == 5000.0:
433					"The amount of $DUST is not correct"
434				vault.isInstance(Type<@FlovatarDustToken.Vault>()):
435					"Vault not of the right Token Type"
436			}
437			destroy vault
438			self.bio.insert(key: "chat", "true")
439			emit UnlockedChat(id: self.id)
440		}
441		
442		// This will allow to set the GPS location of a Flovatar
443		// It can be run multiple times and each time it will override the previous state
444		// $DUST vault must contain 10 tokens that will be burned in the process
445		access(Flovatar.PrivateEnt)
446		fun setPosition(latitude: Fix64, longitude: Fix64, vault: @{FungibleToken.Vault}): String{ 
447			pre{ 
448				latitude >= -90.0:
449					"The latitude is out of range"
450				latitude <= 90.0:
451					"The latitude is out of range"
452				longitude >= -180.0:
453					"The longitude is out of range"
454				longitude <= 180.0:
455					"The longitude is out of range"
456				vault.balance == 10.0:
457					"The amount of $DUST is not correct"
458				vault.isInstance(Type<@FlovatarDustToken.Vault>()):
459					"Vault not of the right Token Type"
460			}
461			destroy vault
462			let position: String = latitude.toString().concat(",").concat(longitude.toString())
463			self.bio.insert(key: "position", position)
464			emit PositionChanged(id: self.id, position: position)
465			return position
466		}
467		
468		access(all)
469		fun getAccessory(): UInt64?{ 
470			return self.accessory?.templateId
471		}
472		
473		// This will allow to change the Accessory of the Flovatar any time.
474		// It checks for the right category and series before executing.
475		access(Flovatar.PrivateEnt)
476		fun setAccessory(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?{ 
477			if(component.getCategory() != "accessory") {
478				panic("The component needs to be an accessory")
479			}
480			if(component.getSeries() != self.metadata.series) {
481				panic("The accessory belongs to a different series")
482			}
483			emit Updated(id: self.id)
484			let compNFT <- self.accessory <- component
485			return <-compNFT
486		}
487		
488		// This will allow to remove the Accessory of the Flovatar any time.
489		access(Flovatar.PrivateEnt)
490		fun removeAccessory(): @FlovatarComponent.NFT?{ 
491			emit Updated(id: self.id)
492			let compNFT <- self.accessory <- nil
493			return <-compNFT
494		}
495		
496		access(all)
497		fun getHat(): UInt64?{ 
498			return self.hat?.templateId
499		}
500		
501		// This will allow to change the Hat of the Flovatar any time.
502		// It checks for the right category and series before executing.
503		access(Flovatar.PrivateEnt)
504		fun setHat(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?{ 
505			if(component.getCategory() != "hat") {
506				panic("The component needs to be a hat")
507			}
508			if(component.getSeries() != self.metadata.series) {
509				panic("The hat belongs to a different series")
510			}
511			emit Updated(id: self.id)
512			let compNFT <- self.hat <- component
513			return <-compNFT
514		}
515		
516		// This will allow to remove the Hat of the Flovatar any time.
517		access(Flovatar.PrivateEnt)
518		fun removeHat(): @FlovatarComponent.NFT?{ 
519			emit Updated(id: self.id)
520			let compNFT <- self.hat <- nil
521			return <-compNFT
522		}
523		
524		access(all)
525		fun getEyeglasses(): UInt64?{ 
526			return self.eyeglasses?.templateId
527		}
528		
529		// This will allow to change the Eyeglasses of the Flovatar any time.
530		// It checks for the right category and series before executing.
531		access(Flovatar.PrivateEnt)
532		fun setEyeglasses(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?{ 
533			if(component.getCategory() != "eyeglasses") {
534				panic("The component needs to be a pair of eyeglasses")
535			}
536			if(component.getSeries() != self.metadata.series) {
537				panic("The eyeglasses belongs to a different series")
538			}
539			emit Updated(id: self.id)
540			let compNFT <- self.eyeglasses <- component
541			return <-compNFT
542		}
543		
544		// This will allow to remove the Eyeglasses of the Flovatar any time.
545		access(Flovatar.PrivateEnt)
546		fun removeEyeglasses(): @FlovatarComponent.NFT?{ 
547			emit Updated(id: self.id)
548			let compNFT <- self.eyeglasses <- nil
549			return <-compNFT
550		}
551		
552		access(all)
553		fun getBackground(): UInt64?{ 
554			return self.background?.templateId
555		}
556		
557		// This will allow to change the Background of the Flovatar any time.
558		// It checks for the right category and series before executing.
559		access(Flovatar.PrivateEnt)
560		fun setBackground(component: @FlovatarComponent.NFT): @FlovatarComponent.NFT?{ 
561			if(component.getCategory() != "background") {
562				panic("The component needs to be a background")
563			}
564			if(component.getSeries() != self.metadata.series) {
565				panic("The background belongs to a different series")
566			}
567			emit Updated(id: self.id)
568			let compNFT <- self.background <- component
569			return <-compNFT
570		}
571		
572		// This will allow to remove the Background of the Flovatar any time.
573		access(Flovatar.PrivateEnt)
574		fun removeBackground(): @FlovatarComponent.NFT?{ 
575			emit Updated(id: self.id)
576			let compNFT <- self.background <- nil
577			return <-compNFT
578		}
579		
580		// This function will return the full SVG of the Flovatar. It will take the
581		// optional components (Accessory, Hat, Eyeglasses and Background) from their
582		// original Template resources, while all the other unmutable components are
583		// taken from the Metadata directly.
584		access(all)
585		fun getSvg(): String{ 
586			var svg: String = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3000 3000' width='100%' height='100%'>"
587			if let background = self.getBackground(){ 
588				if let template = FlovatarComponentTemplate.getComponentTemplate(id: background){ 
589					svg = svg.concat(template.svg!)
590				}
591			}
592			svg = svg.concat(self.metadata.svg)
593			if let eyeglasses = self.getEyeglasses(){ 
594				if let template = FlovatarComponentTemplate.getComponentTemplate(id: eyeglasses){ 
595					svg = svg.concat(template.svg!)
596				}
597			}
598			if let hat = self.getHat(){ 
599				if let template = FlovatarComponentTemplate.getComponentTemplate(id: hat){ 
600					svg = svg.concat(template.svg!)
601				}
602			}
603			if let accessory = self.getAccessory(){ 
604				if let template = FlovatarComponentTemplate.getComponentTemplate(id: accessory){ 
605					svg = svg.concat(template.svg!)
606				}
607			}
608			svg = svg.concat("</svg>")
609			return svg
610		}
611		
612		access(all)
613		fun getRarityScore(): UFix64{ 
614			var rareCount: UInt8 = self.metadata.rareCount
615			var epicCount: UInt8 = self.metadata.epicCount
616			var legendaryCount: UInt8 = self.metadata.legendaryCount
617			var totalBoosters: UInt8 = legendaryCount + epicCount + rareCount
618			let totalCommon: UInt8 = totalBoosters > UInt8(6) ? 0 : UInt8(6) - totalBoosters
619			if totalBoosters > UInt8(6){ 
620				if rareCount > UInt8(0){ 
621					rareCount = rareCount - UInt8(1)
622				} else if epicCount > UInt8(0){ 
623					epicCount = epicCount - UInt8(1)
624				} else if legendaryCount > UInt8(0){ 
625					legendaryCount = legendaryCount - UInt8(1)
626				}
627			}
628			let score: UInt64 = UInt64(legendaryCount) * UInt64(125) + UInt64(epicCount) * UInt64(25) + UInt64(rareCount) * UInt64(5) + UInt64(totalCommon)
629			let min: UInt64 = 6
630			let max: UInt64 = 6 * 125
631			let scoreFix: UFix64 = UFix64(score - min) * UFix64(100.0) / UFix64(max - min)
632			return scoreFix
633		}
634		
635		access(all)
636		view fun getViews(): [Type]{ 
637			return [
638			Type<MetadataViews.NFTCollectionData>(),
639			Type<MetadataViews.NFTCollectionDisplay>(),
640			Type<MetadataViews.Display>(),
641			Type<MetadataViews.Royalties>(),
642			Type<MetadataViews.Edition>(),
643			Type<MetadataViews.ExternalURL>(),
644			Type<MetadataViews.Serial>(),
645			Type<MetadataViews.Traits>(),
646			Type<MetadataViews.EVMBridgedMetadata>()
647			]
648		}
649		
650		access(all)
651		fun resolveView(_ view: Type): AnyStruct?{ 
652			if view == Type<MetadataViews.ExternalURL>(){ 
653				return MetadataViews.ExternalURL("https://flovatar.com/flovatars/".concat(self.id.toString()))
654			}
655			if view == Type<MetadataViews.Royalties>(){ 
656				let royalties: [MetadataViews.Royalty] = []
657				var count: Int = 0
658				for royalty in self.royalties.royalty{ 
659					royalties.append(MetadataViews.Royalty(receiver: royalty.wallet, cut: royalty.cut, description: "Flovatar Royalty ".concat(count.toString())))
660					count = count + Int(1)
661				}
662				return MetadataViews.Royalties(royalties)
663			}
664			if view == Type<MetadataViews.Serial>(){ 
665				return MetadataViews.Serial(self.id)
666			}
667			if view == Type<MetadataViews.Editions>(){ 
668				let editionInfo = MetadataViews.Edition(name: "Flovatar Series 1", number: self.id, max: UInt64(9999))
669				let editionList: [MetadataViews.Edition] = [editionInfo]
670				return MetadataViews.Editions(editionList)
671			}
672			if view == Type<MetadataViews.NFTCollectionDisplay>(){ 
673				let mediaSquare = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://images.flovatar.com/logo.svg"), mediaType: "image/svg+xml")
674				let mediaBanner = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://images.flovatar.com/logo-horizontal.svg"), mediaType: "image/svg+xml")
675				return MetadataViews.NFTCollectionDisplay(name: "Flovatar", description: "Flovatar is pioneering a new way to unleash community creativity in Web3 by allowing users to be co-creators of their prized NFTs, instead of just being passive collectors.", externalURL: MetadataViews.ExternalURL("https://flovatar.com"), squareImage: mediaSquare, bannerImage: mediaBanner, socials:{ "discord": MetadataViews.ExternalURL("https://discord.gg/flovatar"), "twitter": MetadataViews.ExternalURL("https://twitter.com/flovatar"), "instagram": MetadataViews.ExternalURL("https://instagram.com/flovatar_nft"), "tiktok": MetadataViews.ExternalURL("https://www.tiktok.com/@flovatar")})
676			}
677			if view == Type<MetadataViews.Display>(){ 
678				return MetadataViews.Display(name: self.name == "" ? "Flovatar #".concat(self.id.toString()) : self.name, description: self.description, thumbnail: MetadataViews.HTTPFile(url: "https://images.flovatar.com/flovatar/svg/".concat(self.id.toString()).concat(".svg")))
679			}
680			if view == Type<MetadataViews.Traits>(){ 
681				let traits: [MetadataViews.Trait] = []
682				let components:{ String: UInt64} = self.metadata.getComponents()
683				for k in components.keys{ 
684					if let template = FlovatarComponentTemplate.getComponentTemplate(id: components[k]!){ 
685						let trait = MetadataViews.Trait(name: k, value: template.name, displayType: "String", rarity: MetadataViews.Rarity(score: nil, max: nil, description: template.rarity))
686						traits.append(trait)
687					}
688				}
689				if let accessory = self.getAccessory(){ 
690					if let template = FlovatarComponentTemplate.getComponentTemplate(id: accessory){ 
691						let trait = MetadataViews.Trait(name: template.category, value: template.name, displayType: "String", rarity: MetadataViews.Rarity(score: nil, max: nil, description: template.rarity))
692						traits.append(trait)
693					}
694				}
695				if let background = self.getBackground(){ 
696					if let template = FlovatarComponentTemplate.getComponentTemplate(id: background){ 
697						let trait = MetadataViews.Trait(name: template.category, value: template.name, displayType: "String", rarity: MetadataViews.Rarity(score: nil, max: nil, description: template.rarity))
698						traits.append(trait)
699					}
700				}
701				if let eyeglasses = self.getEyeglasses(){ 
702					if let template = FlovatarComponentTemplate.getComponentTemplate(id: eyeglasses){ 
703						let trait = MetadataViews.Trait(name: template.category, value: template.name, displayType: "String", rarity: MetadataViews.Rarity(score: nil, max: nil, description: template.rarity))
704						traits.append(trait)
705					}
706				}
707				if let hat = self.getHat(){ 
708					if let template = FlovatarComponentTemplate.getComponentTemplate(id: hat){ 
709						let trait = MetadataViews.Trait(name: template.category, value: template.name, displayType: "String", rarity: MetadataViews.Rarity(score: nil, max: nil, description: template.rarity))
710						traits.append(trait)
711					}
712				}
713				return MetadataViews.Traits(traits)
714			}
715			if view == Type<MetadataViews.Rarity>(){ 
716				return MetadataViews.Rarity(score: self.getRarityScore(), max: 100.0, description: nil)
717			}
718			if view == Type<MetadataViews.NFTCollectionData>(){ 
719				return MetadataViews.NFTCollectionData(storagePath: Flovatar.CollectionStoragePath, publicPath: Flovatar.CollectionPublicPath, publicCollection: Type<&Flovatar.Collection>(), publicLinkedType: Type<&Flovatar.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
720						return <-Flovatar.createEmptyCollection(nftType: Type<@Flovatar.Collection>())
721					})
722			}
723			if view == Type<MetadataViews.EVMBridgedMetadata>(){ 
724				let contractLevel = Flovatar.resolveContractView(
725						resourceType: nil,
726						viewType: Type<MetadataViews.EVMBridgedMetadata>()
727					) as! MetadataViews.EVMBridgedMetadata?
728					?? panic("Could not resolve contract-level EVMBridgedMetadata")
729				
730				return MetadataViews.EVMBridgedMetadata(
731					name: contractLevel.name,
732					symbol: contractLevel.symbol,
733					uri: MetadataViews.URI(
734						baseURI: "https://flovatar.com/flovatars/json/",
735						value: self.id.toString()
736					)
737				)
738			}
739			return nil
740		}
741		
742		access(all)
743		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
744			return <-create Collection()
745		}
746	}
747	
748	// Standard NFT collectionPublic interface that can also borrowFlovatar as the correct type
749	access(all)
750	resource interface CollectionPublic{ 
751		access(all)
752		fun deposit(token: @{NonFungibleToken.NFT})
753		
754		access(all)
755		fun getIDs(): [UInt64]
756		
757		access(all)
758		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
759		
760		access(all)
761		fun borrowFlovatar(id: UInt64): &Flovatar.NFT?{ 
762			// If the result isn't nil, the id of the returned reference
763			// should be the same as the argument to the function
764			post{ 
765				result == nil || result?.id == id:
766					"Cannot borrow Flovatar reference: The ID of the returned reference is incorrect"
767			}
768		}
769	}
770	
771	// Main Collection to manage all the Flovatar NFT
772	access(all)
773	resource Collection: CollectionPublic, NonFungibleToken.Collection { 
774		// dictionary of NFT conforming tokens
775		// NFT is a resource type with an `UInt64` ID field
776		access(all)
777		var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
778		
779		init(){ 
780			self.ownedNFTs <-{} 
781		}
782		
783		// withdraw removes an NFT from the collection and moves it to the caller
784		access(NonFungibleToken.Withdraw)
785		fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
786			let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
787			emit Withdraw(id: token.id, from: self.owner?.address)
788			return <-token
789		}
790		
791		// deposit takes a NFT and adds it to the collections dictionary
792		// and adds the ID to the id array
793		access(all)
794		fun deposit(token: @{NonFungibleToken.NFT}){ 
795			let token <- token as! @Flovatar.NFT
796			let id: UInt64 = token.id
797			
798			// add the new token to the dictionary which removes the old one
799			let oldToken <- self.ownedNFTs[id] <- token
800			emit Deposit(id: id, to: self.owner?.address)
801			destroy oldToken
802		}
803		
804		// getIDs returns an array of the IDs that are in the collection
805		access(all)
806		view fun getIDs(): [UInt64]{ 
807			return self.ownedNFTs.keys
808		}
809		
810		// borrowNFT gets a reference to an NFT in the collection
811		// so that the caller can read its metadata and call its methods
812		access(all)
813		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
814			return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
815		}
816		
817		// borrowFlovatar returns a borrowed reference to a Flovatar
818		// so that the caller can read data and call methods from it.
819		access(all)
820		fun borrowFlovatar(id: UInt64): &Flovatar.NFT?{ 
821			if self.ownedNFTs[id] != nil {
822				let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
823				let flovatarNFT = ref as! &Flovatar.NFT
824				return flovatarNFT
825			} else {
826				return nil
827			}
828		}
829		
830		/*
831		// borrowFlovatarPrivate returns a borrowed reference to a Flovatar using the Private interface
832		// so that the caller can read data and call methods from it, like setting the optional components.
833		 */
834		access(Flovatar.PrivateEnt)
835		fun borrowFlovatarPrivate(id: UInt64): auth(Flovatar.PrivateEnt) &Flovatar.NFT?{
836			if self.ownedNFTs[id] != nil{ 
837                let ref = (&self.ownedNFTs[id] as auth(Flovatar.PrivateEnt) &{NonFungibleToken.NFT}?)!
838                return ref as! auth(Flovatar.PrivateEnt) &Flovatar.NFT
839			} else{ 
840				return nil
841			}
842		}
843
844
845		access(all)
846		view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
847			if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
848				return nft as &{ViewResolver.Resolver}
849			}
850			return nil
851		}
852		
853		
854		/// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
855		access(all) 
856		view fun getSupportedNFTTypes(): {Type: Bool} {
857			let supportedTypes: {Type: Bool} = {}
858			supportedTypes[Type<@Flovatar.NFT>()] = true
859			return supportedTypes
860		}
861
862		/// Returns whether or not the given type is accepted by the collection
863		/// A collection that can accept any type should just return true by default
864		access(all) 
865		view fun isSupportedNFTType(type: Type): Bool {
866			return type == Type<@Flovatar.NFT>()
867		}
868		
869		access(all)
870		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
871			return <-Flovatar.createEmptyCollection(nftType: Type<@Flovatar.NFT>())
872		}
873
874
875		access(all) view fun getLength(): Int {
876			return self.ownedNFTs.length
877		}
878	}
879	
880	// public function that anyone can call to create a new empty collection
881	access(all)
882	fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
883		return <-create Collection()
884	}
885
886
887
888
889	access(all) 
890	view fun getContractViews(resourceType: Type?): [Type] {
891		return [
892			Type<MetadataViews.NFTCollectionData>(),
893			Type<MetadataViews.NFTCollectionDisplay>(),
894			Type<MetadataViews.EVMBridgedMetadata>()
895		]
896	}
897
898
899	access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
900		switch viewType {
901			case Type<MetadataViews.NFTCollectionData>():
902				let collectionData = MetadataViews.NFTCollectionData(
903					storagePath: self.CollectionStoragePath,
904					publicPath: self.CollectionPublicPath,
905					publicCollection: Type<&Flovatar.Collection>(),
906					publicLinkedType: Type<&Flovatar.Collection>(),
907					createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
908						return <-Flovatar.createEmptyCollection(nftType: Type<@Flovatar.NFT>())
909					})
910				)
911				return collectionData
912			case Type<MetadataViews.NFTCollectionDisplay>():
913				let media = MetadataViews.Media(
914					file: MetadataViews.HTTPFile(
915						url: "https://images.flovatar.com/logo.svg"
916					),
917					mediaType: "image/svg+xml"
918				)
919				let mediaBanner = MetadataViews.Media(
920					file: MetadataViews.HTTPFile(
921						url: "https://images.flovatar.com/logo-horizontal.svg"
922					),
923					mediaType: "image/svg+xml"
924				)
925				return MetadataViews.NFTCollectionDisplay(
926					name: "Flovatar Collection",
927					description: "Flovatar is pioneering a new way to unleash community creativity in Web3 by allowing users to be co-creators of their prized NFTs, instead of just being passive collectors.",
928					externalURL: MetadataViews.ExternalURL("https://flovatar.com"),
929					squareImage: media,
930					bannerImage: mediaBanner,
931					socials: {
932						"twitter": MetadataViews.ExternalURL("https://x.com/flovatar"),
933						"discord": MetadataViews.ExternalURL("https://discord.gg/flovatar"),
934						"instagram": MetadataViews.ExternalURL("https://instagram.com/flovatar_nft"),
935						"tiktok": MetadataViews.ExternalURL("https://www.tiktok.com/@flovatar")
936					}
937				)
938			case Type<MetadataViews.EVMBridgedMetadata>():
939				// Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
940				// when bridged to EVM on Flow via the public infrastructure bridge.
941
942				// Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
943				// but it could be IPFS, S3, a data URL containing the JSON directly, etc.
944				return MetadataViews.EVMBridgedMetadata(
945					name: "Flovatar",
946					symbol: "FLVT",
947					uri: MetadataViews.URI(
948						baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
949						value: "https://flovatar.com"
950					)
951				)
952		}
953		return nil
954	}
955
956	
957	// This struct is used to send a data representation of the Flovatars
958	// when retrieved using the contract helper methods outside the collection.
959	access(all)
960	struct FlovatarData{ 
961		access(all)
962		let id: UInt64
963		
964		access(all)
965		let name: String
966		
967		access(all)
968		let metadata: Flovatar.Metadata
969		
970		access(all)
971		let accessoryId: UInt64?
972		
973		access(all)
974		let hatId: UInt64?
975		
976		access(all)
977		let eyeglassesId: UInt64?
978		
979		access(all)
980		let backgroundId: UInt64?
981		
982		access(all)
983		let bio:{ String: String}
984		
985		init(id: UInt64, name: String, metadata: Flovatar.Metadata, accessoryId: UInt64?, hatId: UInt64?, eyeglassesId: UInt64?, backgroundId: UInt64?, bio:{ String: String}){ 
986			self.id = id
987			self.name = name
988			self.metadata = metadata
989			self.accessoryId = accessoryId
990			self.hatId = hatId
991			self.eyeglassesId = eyeglassesId
992			self.backgroundId = backgroundId
993			self.bio = bio
994		}
995	}
996	
997	// This function will look for a specific Flovatar on a user account and return a FlovatarData if found
998	access(all)
999	fun getFlovatar(address: Address, flovatarId: UInt64): FlovatarData?{ 
1000		let account = getAccount(address)
1001		if let flovatarCollection = account.capabilities.borrow<&Flovatar.Collection>(Flovatar.CollectionPublicPath){ 
1002			if !flovatarCollection.isInstance(Type<@Flovatar.Collection>()){ 
1003				panic("The Collection is not from the correct Type")
1004			}
1005			if let flovatar = flovatarCollection.borrowFlovatar(id: flovatarId){ 
1006				return FlovatarData(id: flovatarId, name: (flovatar!).getName(), metadata: (flovatar!).getMetadata(), accessoryId: (flovatar!).getAccessory(), hatId: (flovatar!).getHat(), eyeglassesId: (flovatar!).getEyeglasses(), backgroundId: (flovatar!).getBackground(), bio: (flovatar!).getBio())
1007			}
1008		}
1009		return nil
1010	}
1011	
1012	// This function will look for a specific Flovatar on a user account and return the Score
1013	access(all)
1014	fun getFlovatarRarityScore(address: Address, flovatarId: UInt64): UFix64?{ 
1015		let account = getAccount(address)
1016		if let flovatarCollection = account.capabilities.borrow<&Flovatar.Collection>(Flovatar.CollectionPublicPath){ 
1017			if !flovatarCollection.isInstance(Type<@Flovatar.Collection>()){ 
1018				panic("The Collection is not from the correct Type")
1019			}
1020			if let flovatar = flovatarCollection.borrowFlovatar(id: flovatarId){ 
1021				return flovatar.getRarityScore()
1022			}
1023		}
1024		
1025		return nil
1026	}
1027	
1028	// This function will return all Flovatars on a user account and return an array of FlovatarData
1029	access(all)
1030	fun getFlovatars(address: Address): [FlovatarData]{ 
1031		var flovatarData: [FlovatarData] = []
1032		let account = getAccount(address)
1033		if let flovatarCollection = account.capabilities.borrow<&Flovatar.Collection>(Flovatar.CollectionPublicPath){ 
1034			if !flovatarCollection.isInstance(Type<@Flovatar.Collection>()){ 
1035				panic("The Collection is not from the correct Type")
1036			}
1037			for id in flovatarCollection.getIDs(){ 
1038				var flovatar = flovatarCollection.borrowFlovatar(id: id)
1039				let flovatarMetadata = (flovatar!).getMetadata()
1040				let newMetadata = Metadata(mint: flovatarMetadata.mint, series: flovatarMetadata.series, svg: "", combination: flovatarMetadata.combination, creatorAddress: flovatarMetadata.creatorAddress, components: flovatarMetadata.getComponents(), rareCount: flovatarMetadata.rareCount, epicCount: flovatarMetadata.epicCount, legendaryCount: flovatarMetadata.legendaryCount)
1041				flovatarData.append(FlovatarData(id: id, name: (flovatar!).getName(), metadata: newMetadata, accessoryId: (flovatar!).getAccessory(), hatId: (flovatar!).getHat(), eyeglassesId: (flovatar!).getEyeglasses(), backgroundId: (flovatar!).getBackground(), bio: (flovatar!).getBio()))
1042			}
1043		}
1044		
1045		return flovatarData
1046	}
1047	
1048	// This returns all the previously minted combinations, so that duplicates won't be allowed
1049	access(all)
1050	fun getMintedCombinations(): [String]{ 
1051		return Flovatar.mintedCombinations.keys
1052	}
1053	
1054	// This returns all the previously minted names, so that duplicates won't be allowed
1055	access(all)
1056	fun getMintedNames(): [String]{ 
1057		return Flovatar.mintedNames.keys
1058	}
1059	
1060	// This function will add a minted combination to the array
1061	access(account)
1062	fun addMintedCombination(combination: String){ 
1063		Flovatar.mintedCombinations.insert(key: combination, true)
1064	}
1065	
1066	// This function will add a new name to the array
1067	access(account)
1068	fun addMintedName(name: String){ 
1069		Flovatar.mintedNames.insert(key: name, true)
1070	}
1071	
1072	// This helper function will generate a string from a list of components,
1073	// to be used as a sort of barcode to keep the inventory of the minted
1074	// Flovatars and to avoid duplicates
1075	access(all)
1076	fun getCombinationString(body: UInt64, hair: UInt64, facialHair: UInt64?, eyes: UInt64, nose: UInt64, mouth: UInt64, clothing: UInt64): String{ 
1077		let facialHairString = facialHair != nil ? (facialHair!).toString() : "x"
1078		return "B".concat(body.toString()).concat("H").concat(hair.toString()).concat("F").concat(facialHairString).concat("E").concat(eyes.toString()).concat("N").concat(nose.toString()).concat("M").concat(mouth.toString()).concat("C").concat(clothing.toString())
1079	}
1080	
1081	// This function will get a list of component IDs and will check if the
1082	// generated string is unique or if someone already used it before.
1083	access(all)
1084	fun checkCombinationAvailable(body: UInt64, hair: UInt64, facialHair: UInt64?, eyes: UInt64, nose: UInt64, mouth: UInt64, clothing: UInt64): Bool{ 
1085		let combinationString = Flovatar.getCombinationString(body: body, hair: hair, facialHair: facialHair, eyes: eyes, nose: nose, mouth: mouth, clothing: clothing)
1086		return !Flovatar.mintedCombinations.containsKey(combinationString)
1087	}
1088	
1089	// This will check if a specific Name has already been taken
1090	// and assigned to some Flovatar
1091	access(all)
1092	fun checkNameAvailable(name: String): Bool{ 
1093		return name.length > 2 && name.length < 32 && !Flovatar.mintedNames.containsKey(name)
1094	}
1095	
1096	// This is a public function that anyone can call to generate a new Flovatar
1097	// A list of components resources needs to be passed to executed.
1098	// It will check first for uniqueness of the combination + name and will then
1099	// generate the Flovatar and burn all the passed components.
1100	// The Spark NFT will entitle to use any common basic component (body, hair, etc.)
1101	// In order to use special rare components a boost of the same rarity will be needed
1102	// for each component used
1103	access(all)
1104	fun createFlovatar(spark: @FlovatarComponent.NFT, body: UInt64, hair: UInt64, facialHair: UInt64?, eyes: UInt64, nose: UInt64, mouth: UInt64, clothing: UInt64, accessory: @FlovatarComponent.NFT?, hat: @FlovatarComponent.NFT?, eyeglasses: @FlovatarComponent.NFT?, background: @FlovatarComponent.NFT?, rareBoost: @[FlovatarComponent.NFT], epicBoost: @[FlovatarComponent.NFT], legendaryBoost: @[FlovatarComponent.NFT], address: Address): @Flovatar.NFT{ 
1105		if(spark.getCategory() != "spark"){
1106			panic("The spark component belongs to the wrong category")
1107		}
1108		let bodyTemplate: FlovatarComponentTemplate.ComponentTemplateData = FlovatarComponentTemplate.getComponentTemplate(id: body)!
1109		let hairTemplate: FlovatarComponentTemplate.ComponentTemplateData = FlovatarComponentTemplate.getComponentTemplate(id: hair)!
1110		let eyesTemplate: FlovatarComponentTemplate.ComponentTemplateData = FlovatarComponentTemplate.getComponentTemplate(id: eyes)!
1111		let noseTemplate: FlovatarComponentTemplate.ComponentTemplateData = FlovatarComponentTemplate.getComponentTemplate(id: nose)!
1112		let mouthTemplate: FlovatarComponentTemplate.ComponentTemplateData = FlovatarComponentTemplate.getComponentTemplate(id: mouth)!
1113		let clothingTemplate: FlovatarComponentTemplate.ComponentTemplateData = FlovatarComponentTemplate.getComponentTemplate(id: clothing)!
1114		
1115		// Make sure that all components belong to the correct category
1116		if bodyTemplate.category != "body"{ 
1117			panic("The body component belongs to the wrong category")
1118		}
1119		if hairTemplate.category != "hair"{ 
1120			panic("The hair component belongs to the wrong category")
1121		}
1122		if eyesTemplate.category != "eyes"{ 
1123			panic("The eyes component belongs to the wrong category")
1124		}
1125		if noseTemplate.category != "nose"{ 
1126			panic("The nose component belongs to the wrong category")
1127		}
1128		if mouthTemplate.category != "mouth"{ 
1129			panic("The mouth component belongs to the wrong category")
1130		}
1131		if clothingTemplate.category != "clothing"{ 
1132			panic("The clothing component belongs to the wrong category")
1133		}
1134		let sparkSeries = spark.getSeries()
1135		// Make sure that all the components belong to the same series like the spark
1136		if bodyTemplate.series != sparkSeries{ 
1137			panic("The body doesn't belong to the correct series")
1138		}
1139		if hairTemplate.series != sparkSeries{ 
1140			panic("The hair doesn't belong to the correct series")
1141		}
1142		if eyesTemplate.series != sparkSeries{ 
1143			panic("The eyes doesn't belong to the correct series")
1144		}
1145		if noseTemplate.series != sparkSeries{ 
1146			panic("The nose doesn't belong to the correct series")
1147		}
1148		if mouthTemplate.series != sparkSeries{ 
1149			panic("The mouth doesn't belong to the correct series")
1150		}
1151		if clothingTemplate.series != sparkSeries{ 
1152			panic("The clothing doesn't belong to the correct series")
1153		}
1154		
1155		// Make more checks for the additional components to check for the right category and uniqueness
1156		var facialHairTemplate: FlovatarComponentTemplate.ComponentTemplateData? = nil
1157		if facialHair != nil{ 
1158			facialHairTemplate = FlovatarComponentTemplate.getComponentTemplate(id: facialHair!)
1159			if facialHairTemplate?.category != "facialHair"{ 
1160				panic("The facial hair component belongs to the wrong category")
1161			}
1162			if facialHairTemplate?.series != sparkSeries{ 
1163				panic("The facial hair doesn't belong to the correct series")
1164			}
1165		}
1166		if accessory != nil{ 
1167			if !accessory?.checkCategorySeries(category: "accessory", series: sparkSeries)!{ 
1168				panic("The accessory component belongs to the wrong category or the wrong series")
1169			}
1170		}
1171		if hat != nil{ 
1172			if !hat?.checkCategorySeries(category: "hat", series: sparkSeries)!{ 
1173				panic("The hat component belongs to the wrong category or the wrong series")
1174			}
1175		}
1176		if eyeglasses != nil{ 
1177			if !eyeglasses?.checkCategorySeries(category: "eyeglasses", series: sparkSeries)!{ 
1178				panic("The eyeglasses component belongs to the wrong category or the wrong series")
1179			}
1180		}
1181		if background != nil{ 
1182			if !background?.checkCategorySeries(category: "background", series: sparkSeries)!{ 
1183				panic("The background component belongs to the wrong category or the wrong series")
1184			}
1185		}
1186		
1187		//Make sure that all the Rarity Boosts are from the correct category
1188		var i: Int = 0
1189		while i < rareBoost.length{ 
1190			if !rareBoost[i].isBooster(rarity: "rare"){ 
1191				panic("The rare boost belongs to the wrong category")
1192			}
1193			if rareBoost[i].getSeries() != sparkSeries{ 
1194				panic("The rare boost doesn't belong to the correct series")
1195			}
1196			i = i + 1
1197		}
1198		i = 0
1199		while i < epicBoost.length{ 
1200			if !epicBoost[i].isBooster(rarity: "epic"){ 
1201				panic("The epic boost belongs to the wrong category")
1202			}
1203			if epicBoost[i].getSeries() != sparkSeries{ 
1204				panic("The epic boost doesn't belong to the correct series")
1205			}
1206			i = i + 1
1207		}
1208		i = 0
1209		while i < legendaryBoost.length{ 
1210			if !legendaryBoost[i].isBooster(rarity: "legendary"){ 
1211				panic("The legendary boost belongs to the wrong category")
1212			}
1213			if legendaryBoost[i].getSeries() != sparkSeries{ 
1214				panic("The legendary boost doesn't belong to the correct series")
1215			}
1216			i = i + 1
1217		}
1218		
1219		//Keep count of the necessary rarity boost for the selected templates
1220		var rareCount: UInt8 = 0
1221		var epicCount: UInt8 = 0
1222		var legendaryCount: UInt8 = 0
1223		if bodyTemplate.rarity == "rare"{ 
1224			rareCount = rareCount + 1
1225		}
1226		if hairTemplate.rarity == "rare"{ 
1227			rareCount = rareCount + 1
1228		}
1229		if eyesTemplate.rarity == "rare"{ 
1230			rareCount = rareCount + 1
1231		}
1232		if noseTemplate.rarity == "rare"{ 
1233			rareCount = rareCount + 1
1234		}
1235		if mouthTemplate.rarity == "rare"{ 
1236			rareCount = rareCount + 1
1237		}
1238		if clothingTemplate.rarity == "rare"{ 
1239			rareCount = rareCount + 1
1240		}
1241		if bodyTemplate.rarity == "epic"{ 
1242			epicCount = epicCount + 1
1243		}
1244		if hairTemplate.rarity == "epic"{ 
1245			epicCount = epicCount + 1
1246		}
1247		if eyesTemplate.rarity == "epic"{ 
1248			epicCount = epicCount + 1
1249		}
1250		if noseTemplate.rarity == "epic"{ 
1251			epicCount = epicCount + 1
1252		}
1253		if mouthTemplate.rarity == "epic"{ 
1254			epicCount = epicCount + 1
1255		}
1256		if clothingTemplate.rarity == "epic"{ 
1257			epicCount = epicCount + 1
1258		}
1259		if bodyTemplate.rarity == "legendary"{ 
1260			legendaryCount = legendaryCount + 1
1261		}
1262		if hairTemplate.rarity == "legendary"{ 
1263			legendaryCount = legendaryCount + 1
1264		}
1265		if eyesTemplate.rarity == "legendary"{ 
1266			legendaryCount = legendaryCount + 1
1267		}
1268		if noseTemplate.rarity == "legendary"{ 
1269			legendaryCount = legendaryCount + 1
1270		}
1271		if mouthTemplate.rarity == "legendary"{ 
1272			legendaryCount = legendaryCount + 1
1273		}
1274		if clothingTemplate.rarity == "legendary"{ 
1275			legendaryCount = legendaryCount + 1
1276		}
1277		if facialHairTemplate != nil{ 
1278			if facialHairTemplate?.rarity == "rare"{ 
1279				rareCount = rareCount + 1
1280			}
1281			if facialHairTemplate?.rarity == "epic"{ 
1282				epicCount = epicCount + 1
1283			}
1284			if facialHairTemplate?.rarity == "legendary"{ 
1285				legendaryCount = legendaryCount + 1
1286			}
1287		}
1288		if Int(rareCount) != rareBoost.length{ 
1289			panic("The rare boosts are not equal the ones needed")
1290		}
1291		if Int(epicCount) != epicBoost.length{ 
1292			panic("The epic boosts are not equal the ones needed")
1293		}
1294		if Int(legendaryCount) != legendaryBoost.length{ 
1295			panic("The epic boosts are not equal the ones needed")
1296		}
1297		
1298		// Generates the combination string to check for uniqueness.
1299		// This is like a barcode that defines exactly which components were used
1300		// to create the Flovatar
1301		let combinationString = Flovatar.getCombinationString(body: body, hair: hair, facialHair: facialHair, eyes: eyes, nose: nose, mouth: mouth, clothing: clothing)
1302		
1303		// Makes sure that the combination is available and not taken already
1304		if Flovatar.mintedCombinations.containsKey(combinationString) == true{ 
1305			panic("This combination has already been taken")
1306		}
1307		let facialHairSvg: String = facialHairTemplate != nil ? facialHairTemplate?.svg! : ""
1308		let svg = (bodyTemplate.svg!).concat(clothingTemplate.svg!).concat(hairTemplate.svg!).concat(eyesTemplate.svg!).concat(noseTemplate.svg!).concat(mouthTemplate.svg!).concat(facialHairSvg)
1309		
1310		// TODO fix this with optional if possible. If I define it as UInt64?
1311		// instead of UInt64 it's throwing an error even if it's defined in Metadata struct
1312		let facialHairId: UInt64 = facialHair != nil ? facialHair! : 0
1313		
1314		// Creates the metadata for the new Flovatar
1315		let metadata = Metadata(mint: Flovatar.totalSupply + UInt64(1), series: spark.getSeries(), svg: svg, combination: combinationString, creatorAddress: address, components:{ "body": body, "hair": hair, "facialHair": facialHairId, "eyes": eyes, "nose": nose, "mouth": mouth, "clothing": clothing}, rareCount: rareCount, epicCount: epicCount, legendaryCount: legendaryCount)
1316		let royalties: [Royalty] = []
1317		let creatorAccount = getAccount(address)
1318		royalties.append(Royalty(wallet: creatorAccount.capabilities.get<&FlowToken.Vault>(/public/flowTokenReceiver), cut: Flovatar.getRoyaltyCut(), type: RoyaltyType.percentage))
1319		royalties.append(Royalty(wallet: self.account.capabilities.get<&FlowToken.Vault>(/public/flowTokenReceiver), cut: Flovatar.getMarketplaceCut(), type: RoyaltyType.percentage))
1320		
1321		// Mint the new Flovatar NFT by passing the metadata to it
1322		var newNFT <- create NFT(metadata: metadata, royalties: Royalties(royalty: royalties))
1323		
1324		// Adds the combination to the arrays to remember it
1325		Flovatar.addMintedCombination(combination: combinationString)
1326		
1327		// Checks for any additional optional component (accessory, hat,
1328		// eyeglasses, background) and assigns it to the Flovatar if present.
1329		if accessory != nil{ 
1330			let temp <- newNFT.setAccessory(component: <-accessory!)
1331			destroy temp
1332		} else{ 
1333			destroy accessory
1334		}
1335		if hat != nil{ 
1336			let temp <- newNFT.setHat(component: <-hat!)
1337			destroy temp
1338		} else{ 
1339			destroy hat
1340		}
1341		if eyeglasses != nil{ 
1342			let temp <- newNFT.setEyeglasses(component: <-eyeglasses!)
1343			destroy temp
1344		} else{ 
1345			destroy eyeglasses
1346		}
1347		if background != nil{ 
1348			let temp <- newNFT.setBackground(component: <-background!)
1349			destroy temp
1350		} else{ 
1351			destroy background
1352		}
1353		
1354		// Emits the Created event to notify about its existence
1355		emit Created(id: newNFT.id, metadata: metadata)
1356		
1357		// Destroy all the spark and the rarity boost since they are not needed anymore.
1358		destroy spark
1359		while rareBoost.length > 0{ 
1360			let boost <- rareBoost.remove(at: 0)
1361			destroy boost
1362		}
1363		destroy rareBoost
1364		while epicBoost.length > 0{ 
1365			let boost <- epicBoost.remove(at: 0)
1366			destroy boost
1367		}
1368		destroy epicBoost
1369		while legendaryBoost.length > 0{ 
1370			let boost <- legendaryBoost.remove(at: 0)
1371			destroy boost
1372		}
1373		destroy legendaryBoost
1374		return <-newNFT
1375	}
1376	
1377	// These functions will return the current Royalty cuts for
1378	// both the Creator and the Marketplace.
1379	access(all)
1380	fun getRoyaltyCut(): UFix64{ 
1381		return self.royaltyCut
1382	}
1383	
1384	access(all)
1385	fun getMarketplaceCut(): UFix64{ 
1386		return self.marketplaceCut
1387	}
1388	
1389	// Only Admins will be able to call the set functions to
1390	// manage Royalties and Marketplace cuts.
1391	access(account)
1392	fun setRoyaltyCut(value: UFix64){ 
1393		self.royaltyCut = value
1394	}
1395	
1396	access(account)
1397	fun setMarketplaceCut(value: UFix64){ 
1398		self.marketplaceCut = value
1399	}
1400	
1401	// This is the main Admin resource that will allow the owner
1402	// to generate new Templates, Components and Packs
1403	access(all)
1404	resource Admin{ 
1405		
1406
1407
1408
1409		//This will create a new FlovatarComponentTemplate that
1410		// contains all the SVG and basic informations to represent
1411		// a specific part of the Flovatar (body, hair, eyes, mouth, etc.)
1412		// More info in the FlovatarComponentTemplate.cdc file
1413		access(all)
1414		fun createComponentTemplate(name: String, category: String, color: String, description: String, svg: String, series: UInt32, maxMintableComponents: UInt64, rarity: String): @FlovatarComponentTemplate.ComponentTemplate{ 
1415			return <-FlovatarComponentTemplate.createComponentTemplate(name: name, category: category, color: color, description: description, svg: svg, series: series, maxMintableComponents: maxMintableComponents, rarity: rarity)
1416		}
1417		
1418		// This will mint a new Component based from a selected Template
1419		access(all)
1420		fun createComponent(templateId: UInt64): @FlovatarComponent.NFT{ 
1421			return <-FlovatarComponent.createComponent(templateId: templateId)
1422		}
1423		
1424		// This will mint Components in batch and return a Collection instead of the single NFT
1425		access(all)
1426		fun batchCreateComponents(templateId: UInt64, quantity: UInt64): @FlovatarComponent.Collection{ 
1427			return <-FlovatarComponent.batchCreateComponents(templateId: templateId, quantity: quantity)
1428		}
1429		
1430		// This function will generate a new Pack containing a set of components.
1431		// A random string is passed to manage permissions for the
1432		// purchase of it (more info on FlovatarPack.cdc).
1433		// Finally the sale price is set as well.
1434		access(all)
1435		fun createPack(components: @[FlovatarComponent.NFT], randomString: String, price: UFix64, sparkCount: UInt32, series: UInt32, name: String): @FlovatarPack.Pack{ 
1436			return <-FlovatarPack.createPack(components: <-components, randomString: randomString, price: price, sparkCount: sparkCount, series: series, name: name)
1437		}
1438		 
1439		
1440		// With this function you can generate a new Admin resource
1441		// and pass it to another user if needed
1442		access(all)
1443		fun createNewAdmin(): @Admin{ 
1444			return <-create Admin()
1445		}
1446		
1447		// Helper functions to update the Royalty cut
1448		access(all)
1449		fun setRoyaltyCut(value: UFix64){ 
1450			Flovatar.setRoyaltyCut(value: value)
1451		}
1452		
1453		// Helper functions to update the Marketplace cut
1454		access(all)
1455		fun setMarketplaceCut(value: UFix64){ 
1456			Flovatar.setMarketplaceCut(value: value)
1457		}
1458	}
1459	
1460	init(){ 
1461		self.CollectionPublicPath = /public/FlovatarCollection
1462		self.CollectionStoragePath = /storage/FlovatarCollection
1463		self.AdminStoragePath = /storage/FlovatarAdmin
1464		
1465		// Initialize the total supply
1466		self.totalSupply = UInt64(0)
1467		self.mintedCombinations ={} 
1468		self.mintedNames ={} 
1469		
1470		// Set the default Royalty and Marketplace cuts
1471		self.royaltyCut = 0.01
1472		self.marketplaceCut = 0.05
1473		self.account.storage.save<@{NonFungibleToken.Collection}>(<-Flovatar.createEmptyCollection(nftType: Type<@Flovatar.Collection>()), to: Flovatar.CollectionStoragePath)
1474		var capability_1 = self.account.capabilities.storage.issue<&{Flovatar.CollectionPublic}>(Flovatar.CollectionStoragePath)
1475		self.account.capabilities.publish(capability_1, at: Flovatar.CollectionPublicPath)
1476		
1477		// Put the Admin resource in storage
1478		self.account.storage.save<@Admin>(<-create Admin(), to: self.AdminStoragePath)
1479		emit ContractInitialized()
1480	}
1481}
1482