Smart Contract

FlovatarComponentTemplate

A.921ea449dffec68a.FlovatarComponentTemplate

Deployed

1w ago
Feb 16, 2026, 11:20:41 PM UTC

Dependents

8 imports
1/*
2
3 This contract defines the Flovatar Component Templates and the Collection to manage them.
4 While Components are the building blocks (lego bricks) of the final Flovatar, 
5 Templates are the blueprint where all the details and characteristics of each component are defined.
6 The main part is the SVG (stored on-chain as a String) and the category that can be one of the following: 
7 body, hair, facialHair, eyes, nose, mouth, clothing, accessory, hat, eyeglasses, background.
8
9 Templates are NOT using the NFT standard and will be always linked only to the contract's owner account.
10
11 Each templates will also declare in advance the maximum amount of mintable components, so that 
12 the scarcity can be enforced by the smart contract itself and so that different rarities can be guaranteed.
13
14 Finally, Templates are organized in Series, so that in the future it will be possible to create new editions
15 with different characters and styles.
16
17 */
18
19access(all)
20contract FlovatarComponentTemplate{ 
21	access(all)
22	let CollectionStoragePath: StoragePath
23	
24	access(all)
25	let CollectionPublicPath: PublicPath
26	
27	// Counter for all the Templates ever minted
28	access(all)
29	var totalSupply: UInt64
30	
31	//These counters will keep track of how many Components were minted for each Template
32	access(contract)
33	let totalMintedComponents:{ UInt64: UInt64}
34	
35	access(contract)
36	let lastComponentMintedAt:{ UInt64: UFix64}
37	
38	// Event to notify about the Template creation
39	access(all)
40	event ContractInitialized()
41	
42	access(all)
43	event Created(
44		id: UInt64,
45		name: String,
46		category: String,
47		color: String,
48		maxMintableComponents: UInt64
49	)
50	
51	// The public interface providing the SVG and all the other 
52	// metadata like name, category, color, series, description and 
53	// the maximum mintable Components
54	access(all)
55	resource interface Public{ 
56		access(all)
57		let id: UInt64
58		
59		access(all)
60		let name: String
61		
62		access(all)
63		let category: String
64		
65		access(all)
66		let color: String
67		
68		access(all)
69		let description: String
70		
71		access(all)
72		let svg: String
73		
74		access(all)
75		let series: UInt32
76		
77		access(all)
78		let maxMintableComponents: UInt64
79		
80		access(all)
81		let rarity: String
82	}
83	
84	// The Component resource implementing the public interface as well
85	access(all)
86	resource ComponentTemplate: Public{ 
87		access(all)
88		let id: UInt64
89		
90		access(all)
91		let name: String
92		
93		access(all)
94		let category: String
95		
96		access(all)
97		let color: String
98		
99		access(all)
100		let description: String
101		
102		access(all)
103		let svg: String
104		
105		access(all)
106		let series: UInt32
107		
108		access(all)
109		let maxMintableComponents: UInt64
110		
111		access(all)
112		let rarity: String
113		
114		// Initialize a Template with all the necessary data
115		init(name: String, category: String, color: String, description: String, svg: String, series: UInt32, maxMintableComponents: UInt64, rarity: String){ 
116			// increments the counter and stores it as the ID
117			FlovatarComponentTemplate.totalSupply = FlovatarComponentTemplate.totalSupply + UInt64(1)
118			self.id = FlovatarComponentTemplate.totalSupply
119			self.name = name
120			self.category = category
121			self.color = color
122			self.description = description
123			self.svg = svg
124			self.series = series
125			self.maxMintableComponents = maxMintableComponents
126			self.rarity = rarity
127		}
128	}
129	
130	// Standard CollectionPublic interface that can also borrow Component Templates
131	access(all)
132	resource interface CollectionPublic{ 
133		access(all)
134		fun getIDs(): [UInt64]
135		
136		access(all)
137		fun borrowComponentTemplate(id: UInt64): &{FlovatarComponentTemplate.Public}?
138	}
139	
140	// The main Collection that manages the Templates and that implements also the Public interface
141	access(all)
142	resource Collection: CollectionPublic{ 
143		// Dictionary of Component Templates
144		access(all)
145		var ownedComponentTemplates: @{UInt64: FlovatarComponentTemplate.ComponentTemplate}
146		
147		init(){ 
148			self.ownedComponentTemplates <-{} 
149		}
150		
151		// deposit takes a Component Template and adds it to the collections dictionary
152		// and adds the ID to the id array
153		access(all)
154		fun deposit(componentTemplate: @FlovatarComponentTemplate.ComponentTemplate){ 
155			let id: UInt64 = componentTemplate.id
156			
157			// add the new Component Template to the dictionary which removes the old one
158			let oldComponentTemplate <- self.ownedComponentTemplates[id] <- componentTemplate
159			destroy oldComponentTemplate
160		}
161		
162		// getIDs returns an array of the IDs that are in the collection
163		access(all)
164		fun getIDs(): [UInt64]{ 
165			return self.ownedComponentTemplates.keys
166		}
167		
168		// borrowComponentTemplate returns a borrowed reference to a Component Template
169		// so that the caller can read data and call methods from it.
170		access(all)
171		fun borrowComponentTemplate(id: UInt64): &{FlovatarComponentTemplate.Public}?{ 
172			if self.ownedComponentTemplates[id] != nil{ 
173				let ref = (&self.ownedComponentTemplates[id] as &FlovatarComponentTemplate.ComponentTemplate?)!
174				return ref
175			} else{ 
176				return nil
177			}
178		}
179	}
180	
181	// This function can only be called by the account owner to create an empty Collection
182	access(account)
183	fun createEmptyCollection(): @FlovatarComponentTemplate.Collection{ 
184		return <-create Collection()
185	}
186	
187	// This struct is used to send a data representation of the Templates 
188	// when retrieved using the contract helper methods outside the collection.
189	access(all)
190	struct ComponentTemplateData{ 
191		access(all)
192		let id: UInt64
193		
194		access(all)
195		let name: String
196		
197		access(all)
198		let category: String
199		
200		access(all)
201		let color: String
202		
203		access(all)
204		let description: String
205		
206		access(all)
207		let svg: String?
208		
209		access(all)
210		let series: UInt32
211		
212		access(all)
213		let maxMintableComponents: UInt64
214		
215		access(all)
216		let totalMintedComponents: UInt64
217		
218		access(all)
219		let lastComponentMintedAt: UFix64
220		
221		access(all)
222		let rarity: String
223		
224		init(
225			id: UInt64,
226			name: String,
227			category: String,
228			color: String,
229			description: String,
230			svg: String?,
231			series: UInt32,
232			maxMintableComponents: UInt64,
233			rarity: String
234		){ 
235			self.id = id
236			self.name = name
237			self.category = category
238			self.color = color
239			self.description = description
240			self.svg = svg
241			self.series = series
242			self.maxMintableComponents = maxMintableComponents
243			self.totalMintedComponents = FlovatarComponentTemplate.getTotalMintedComponents(id: id)!
244			self.lastComponentMintedAt = FlovatarComponentTemplate.getLastComponentMintedAt(id: id)!
245			self.rarity = rarity
246		}
247	}
248	
249	// Get all the Component Templates from the account. 
250	// We hide the SVG field because it might be too big to execute in a script
251	access(all)
252	fun getComponentTemplates(): [ComponentTemplateData]{ 
253		var componentTemplateData: [ComponentTemplateData] = []
254
255		if let componentTemplateCollection = self.account.capabilities.borrow<&FlovatarComponentTemplate.Collection>(FlovatarComponentTemplate.CollectionPublicPath){ 
256			for id in componentTemplateCollection.getIDs(){ 
257				var componentTemplate = componentTemplateCollection.borrowComponentTemplate(id: id)
258				componentTemplateData.append(ComponentTemplateData(id: id, name: (componentTemplate!).name, category: (componentTemplate!).category, color: (componentTemplate!).color, description: (componentTemplate!).description, svg: nil, series: (componentTemplate!).series, maxMintableComponents: (componentTemplate!).maxMintableComponents, rarity: (componentTemplate!).rarity))
259			}
260		}
261			 
262		return componentTemplateData
263	}
264	
265	// Gets a specific Template from its ID
266	access(all)
267	fun getComponentTemplate(id: UInt64): ComponentTemplateData?{ 
268		
269		if let componentTemplateCollection = self.account.capabilities.borrow<&FlovatarComponentTemplate.Collection>(FlovatarComponentTemplate.CollectionPublicPath){ 
270			if let componentTemplate = componentTemplateCollection.borrowComponentTemplate(id: id){ 
271				return ComponentTemplateData(id: id, name: (componentTemplate!).name, category: (componentTemplate!).category, color: (componentTemplate!).color, description: (componentTemplate!).description, svg: (componentTemplate!).svg, series: (componentTemplate!).series, maxMintableComponents: (componentTemplate!).maxMintableComponents, rarity: (componentTemplate!).rarity)
272			}
273		}
274		return nil
275	}
276	
277	// Returns the amount of minted Components for a specific Template
278	access(all)
279	fun getTotalMintedComponents(id: UInt64): UInt64?{ 
280		return FlovatarComponentTemplate.totalMintedComponents[id]
281	}
282	
283	// Returns the timestamp of the last time a Component for a specific Template was minted
284	access(all)
285	fun getLastComponentMintedAt(id: UInt64): UFix64?{ 
286		return FlovatarComponentTemplate.lastComponentMintedAt[id]
287	}
288	
289	// This function is used within the contract to set the new counter for each Template
290	access(account)
291	fun setTotalMintedComponents(id: UInt64, value: UInt64){ 
292		FlovatarComponentTemplate.totalMintedComponents[id] = value
293	}
294	
295	// This function is used within the contract to set the timestamp 
296	// when a Component for a specific Template was minted
297	access(account)
298	fun setLastComponentMintedAt(id: UInt64, value: UFix64){ 
299		FlovatarComponentTemplate.lastComponentMintedAt[id] = value
300	}
301	
302	// It creates a new Template with the data provided.
303	// This is used from the Flovatar Admin resource
304	access(account)
305	fun createComponentTemplate(
306		name: String,
307		category: String,
308		color: String,
309		description: String,
310		svg: String,
311		series: UInt32,
312		maxMintableComponents: UInt64,
313		rarity: String
314	): @FlovatarComponentTemplate.ComponentTemplate{ 
315		var newComponentTemplate <-
316			create ComponentTemplate(
317				name: name,
318				category: category,
319				color: color,
320				description: description,
321				svg: svg,
322				series: series,
323				maxMintableComponents: maxMintableComponents,
324				rarity: rarity
325			)
326		
327		// Emits the Created event to notify about the new Template
328		emit Created(
329			id: newComponentTemplate.id,
330			name: newComponentTemplate.name,
331			category: newComponentTemplate.category,
332			color: newComponentTemplate.color,
333			maxMintableComponents: newComponentTemplate.maxMintableComponents
334		)
335		
336		// Set the counter for the minted Components of this Template to 0
337		FlovatarComponentTemplate.setTotalMintedComponents(
338			id: newComponentTemplate.id,
339			value: UInt64(0)
340		)
341		FlovatarComponentTemplate.setLastComponentMintedAt(
342			id: newComponentTemplate.id,
343			value: UFix64(0)
344		)
345		return <-newComponentTemplate
346	}
347	
348	init(){ 
349		self.CollectionPublicPath = /public/FlovatarComponentTemplateCollection
350		self.CollectionStoragePath = /storage/FlovatarComponentTemplateCollection
351		
352		// Initialize the total supply
353		self.totalSupply = 0
354		self.totalMintedComponents ={} 
355		self.lastComponentMintedAt ={} 
356		self.account.storage.save<@FlovatarComponentTemplate.Collection>(
357			<-FlovatarComponentTemplate.createEmptyCollection(),
358			to: FlovatarComponentTemplate.CollectionStoragePath
359		)
360		var capability_1 =
361			self.account.capabilities.storage.issue<&{FlovatarComponentTemplate.CollectionPublic}>(
362				FlovatarComponentTemplate.CollectionStoragePath
363			)
364		self.account.capabilities.publish(
365			capability_1,
366			at: FlovatarComponentTemplate.CollectionPublicPath
367		)
368		emit ContractInitialized()
369	}
370}
371