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