Smart Contract
FlovatarComponentTemplate
A.921ea449dffec68a.FlovatarComponentTemplate
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