Smart Contract
Collector
A.7c8995e83c4b1843.Collector
1// SPDX-License-Identifier: UNLICENSED
2
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5
6pub contract Collector: NonFungibleToken {
7 // Emitted when the Collector contract is created
8 pub event ContractInitialized()
9
10 // Emitted when a new Set is created
11 pub event SetCreated(id: UInt64, name: String)
12 // Emitted when a Set is locked, meaning Set data cannot be updated
13 pub event SetLocked(id: UInt64, name: String)
14 // Emitted when a Set is unlocked, meaning Set data can be updated
15 pub event SetUnlocked(id: UInt64, name: String)
16 // Emitted when a Set is updated
17 pub event SetUpdated(id: UInt64, name: String)
18
19 // Emitted when a new Template is created
20 pub event TemplateCreated(id: UInt64, name: String)
21 // Emitted when a Template is locked, meaning Template data cannot be updated
22 pub event TemplateLocked(id: UInt64, name: String)
23 // Emitted when a Template is updated
24 pub event TemplateUpdated(id: UInt64, name: String)
25 // Emitted when a Template is added to a Set
26 pub event TemplateAddedToSet(id: UInt64, name: String, setID: UInt64, setName: String)
27
28 // Emitted when an NFT is minted
29 pub event Minted(id: UInt64, templateID: UInt64, setID: UInt64)
30 // Emitted when an NFT is withdrawn from a Collection
31 pub event Withdraw(id: UInt64, from: Address?)
32 // Emitted when an NFT is deposited into a Collection
33 pub event Deposit(id: UInt64, to: Address?)
34
35 pub let CollectionStoragePath: StoragePath
36 pub let CollectionPublicPath: PublicPath
37 pub let AdminStoragePath: StoragePath
38 pub let AdminPrivatePath: PrivatePath
39
40 // The total number of Collector NFT that have been minted
41 pub var totalSupply: UInt64
42 pub var nextTemplateID: UInt64
43 pub var nextSetID: UInt64
44
45 // Variable size dictionary of Template structs
46 access(self) var Templates: {UInt64: Template}
47
48 // Variable size dictionary of SetData structs
49 access(self) var SetsData: {UInt64: SetData}
50
51 // Variable size dictionary of Set resources
52 access(self) var sets: @{UInt64: Set}
53
54 // An Template is a Struct that holds data associated with a specific NFT
55 pub struct Template {
56 pub let id: UInt64
57
58 pub var name: String
59 pub var description: String
60 pub var image: String
61
62 pub var locked: Bool
63 pub var addedToSet: UInt64
64
65 access(self) var metadata: {String: AnyStruct}
66
67 init(id: UInt64, name: String, description: String, image: String, metadata: {String: AnyStruct}){
68 pre {
69 metadata.length != 0: "New template metadata cannot be empty"
70 }
71
72 self.id = id
73 self.name = name
74 self.description = description
75 self.image = image
76 self.metadata = metadata
77 self.locked = false
78 self.addedToSet = 0
79
80 Collector.nextTemplateID = Collector.nextTemplateID + 1
81
82 emit TemplateCreated(id: self.id, name: self.name)
83 }
84
85 pub fun updateName(newName: String) {
86 pre {
87 self.locked == false: "Cannot update name: template is locked"
88 }
89
90 self.name = newName
91 emit TemplateUpdated(id: self.id, name: self.name)
92 }
93
94 pub fun updateDescription(newDescription: String) {
95 pre {
96 self.locked == false: "Cannot update description: template is locked"
97 }
98
99 self.description = newDescription
100 emit TemplateUpdated(id: self.id, name: self.name)
101 }
102
103 pub fun updateImage(newImage: String) {
104 pre {
105 self.locked == false: "Cannot update image: template is locked"
106 }
107
108 self.image = newImage
109 emit TemplateUpdated(id: self.id, name: self.name)
110 }
111
112 pub fun updateMetadata(newMetadata: {String: AnyStruct}) {
113 pre {
114 self.locked == false: "Cannot update metadata: template is locked"
115 newMetadata.length != 0: "New template metadata cannot be empty"
116 }
117
118 self.metadata = newMetadata
119 emit TemplateUpdated(id: self.id, name: self.name)
120 }
121
122 pub fun markAddedToSet(setID: UInt64) {
123 pre {
124 self.addedToSet == 0: "Template is already to a set"
125 }
126
127 self.addedToSet = setID
128
129 let setName = Collector.SetsData[setID]!.name
130 emit TemplateAddedToSet(id: self.id, name: self.name, setID: setID, setName: setName)
131 }
132
133 pub fun lock() {
134 pre {
135 self.locked == false: "Template is already locked"
136 }
137
138 self.locked = true
139 emit TemplateLocked(id: self.id, name: self.name)
140 }
141
142 pub fun getMetadata(): {String: AnyStruct} {
143 return self.metadata
144 }
145 }
146
147 // An SetData is a Struct that holds data associated with a specific Set
148 pub struct SetData {
149 pub let id: UInt64
150
151 pub var name: String
152 pub var description: String
153 pub var image: String
154
155 access(self) var metadata: {String: AnyStruct}
156
157 pub var maxSize: UInt64?
158
159 init(id: UInt64, name: String, description: String, image: String, metadata: {String: AnyStruct}, maxSize: UInt64?) {
160 self.id = id
161 self.name = name
162 self.description = description
163 self.image = image
164 self.metadata = metadata
165 self.maxSize = maxSize
166 }
167
168 pub fun getMetadata(): {String: AnyStruct} {
169 return self.metadata
170 }
171 }
172
173 /* DEPRECATED - DO NOT USE */
174 pub struct CollectorMetadataView {
175 pub let id: UInt64
176
177 pub let name: String
178 pub let description: String
179 pub let image: String
180
181 pub let externalUrl: String?
182
183 pub let bgArchitecture: String?
184 pub let bgPanelling: String?
185 pub let bgArchitecturalSupport: String?
186 pub let bgLighting: String?
187 pub let wineStorageContainer: String?
188 pub let wineBottle: String?
189 pub let wineBottleClosure: String?
190 pub let wineBottleGlassware: String?
191
192 pub let setID: UInt64
193 pub let templateID: UInt64
194 pub let serialNumber: UInt64
195 pub let mintingDate: UFix64
196
197 init(
198 id: UInt64,
199 name: String,
200 description: String,
201 image: String,
202 externalUrl: String?,
203 bgArchitecture: String?,
204 bgPanelling: String?,
205 bgArchitecturalSupport: String?,
206 bgLighting: String?,
207 wineStorageContainer: String?,
208 wineBottle: String?,
209 wineBottleClosure: String?,
210 wineBottleGlassware: String?,
211 setID: UInt64,
212 templateID: UInt64,
213 serialNumber: UInt64,
214 mintingDate: UFix64
215 ) {
216 self.id = id
217 self.name = name
218 self.description = description
219 self.image = image
220 self.externalUrl = externalUrl
221 self.bgArchitecture = bgArchitecture
222 self.bgPanelling = bgPanelling
223 self.bgArchitecturalSupport = bgArchitecturalSupport
224 self.bgLighting = bgLighting
225 self.wineStorageContainer = wineStorageContainer
226 self.wineBottle = wineBottle
227 self.wineBottleClosure = wineBottleClosure
228 self.wineBottleGlassware = wineBottleGlassware
229 self.setID = setID
230 self.templateID = templateID
231 self.serialNumber = serialNumber
232 self.mintingDate = mintingDate
233 }
234 }
235
236 // This is an implementation of a custom metadata view for Cuvée Collective
237 pub struct CollectorMetadataViewV2 {
238 pub let id: UInt64
239
240 pub let name: String
241 pub let description: String
242 pub let image: String
243
244 pub let externalUrl: String?
245
246 pub let architecture: String?
247 pub let panelling: String?
248 pub let visualElements: String?
249 pub let ambience: String?
250 pub let wineStorage: String?
251 pub let wineBox: String?
252 pub let bottle: String?
253
254 pub let setID: UInt64
255 pub let templateID: UInt64
256 pub let serialNumber: UInt64
257 pub let mintingDate: UFix64
258
259 init(
260 id: UInt64,
261 name: String,
262 description: String,
263 image: String,
264 externalUrl: String?,
265 architecture: String?,
266 panelling: String?,
267 visualElements: String?,
268 ambience: String?,
269 wineStorage: String?,
270 wineBox: String?,
271 bottle: String?,
272 setID: UInt64,
273 templateID: UInt64,
274 serialNumber: UInt64,
275 mintingDate: UFix64
276 ) {
277 self.id = id
278 self.name = name
279 self.description = description
280 self.image = image
281 self.externalUrl = externalUrl
282 self.architecture = architecture
283 self.panelling = panelling
284 self.visualElements = visualElements
285 self.ambience = ambience
286 self.wineStorage = wineStorage
287 self.wineBox = wineBox
288 self.bottle = bottle
289 self.setID = setID
290 self.templateID = templateID
291 self.serialNumber = serialNumber
292 self.mintingDate = mintingDate
293 }
294 }
295
296 // A resource that represents the Collector NFT
297 pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
298 pub let id: UInt64
299
300 pub let setID: UInt64
301 pub let templateID: UInt64
302 pub let serialNumber: UInt64
303 pub let mintingDate: UFix64
304
305 init(id: UInt64, templateID: UInt64, serialNumber: UInt64) {
306 pre {
307 Collector.getTemplate(id: templateID) != nil: "Template not found"
308 }
309
310 let setID = Collector.getTemplate(id: templateID)!.addedToSet
311
312 self.id = id
313 self.setID = setID
314 self.templateID = templateID
315 self.serialNumber = serialNumber
316 self.mintingDate = getCurrentBlock().timestamp
317 }
318
319 pub fun getViews(): [Type] {
320 return [
321 Type<MetadataViews.Display>(),
322 Type<MetadataViews.Serial>(),
323 Type<MetadataViews.Royalties>(),
324 Type<MetadataViews.ExternalURL>(),
325 Type<MetadataViews.Editions>(),
326 Type<MetadataViews.Traits>(),
327 Type<CollectorMetadataViewV2>(),
328 Type<MetadataViews.NFTCollectionData>(),
329 Type<MetadataViews.NFTCollectionDisplay>()
330 ]
331 }
332
333 pub fun resolveView(_ view: Type): AnyStruct? {
334 switch view {
335 case Type<MetadataViews.Display>():
336 return MetadataViews.Display(
337 name: self.getTemplate().name,
338 description: self.getTemplate().description,
339 thumbnail: MetadataViews.HTTPFile(
340 url: self.getTemplate().image
341 )
342 )
343 case Type<MetadataViews.Serial>():
344 return MetadataViews.Serial(
345 self.serialNumber
346 )
347 case Type<MetadataViews.Royalties>():
348 var royalties: [MetadataViews.Royalty] = []
349 return MetadataViews.Royalties(royalties)
350 case Type<MetadataViews.ExternalURL>():
351 let externalUrl = self.getMetadata()["external_url"] ?? ""
352 return MetadataViews.ExternalURL(
353 externalUrl as! String
354 )
355 case Type<MetadataViews.Editions>():
356 let setData = Collector.SetsData[self.setID]!
357 let editionInfo = MetadataViews.Edition(
358 name: setData.name,
359 number: self.serialNumber,
360 max: setData.maxSize
361 )
362 let editionList: [MetadataViews.Edition] = [editionInfo]
363 return MetadataViews.Editions(
364 editionList
365 )
366 case Type<MetadataViews.Traits>():
367 let excludedTraits = ["external_url", "bottle"]
368 let traitsView = MetadataViews.dictToTraits(dict: self.getMetadata(), excludedNames: excludedTraits)
369
370 // mintingDate is a unix timestamp, we should mark it with a displayType so platforms know how to show it
371 let mintingDateTrait = MetadataViews.Trait(name: "minting_date", value: self.mintingDate, displayType: "Date", rarity: nil)
372 traitsView.addTrait(mintingDateTrait)
373
374 return traitsView
375 case Type<CollectorMetadataViewV2>():
376 let externalUrl = self.getMetadata()["external_url"] ?? ""
377 let architecture = self.getMetadata()["architecture"] ?? ""
378 let panelling = self.getMetadata()["panelling"] ?? ""
379 let visualElements = self.getMetadata()["visual_elements"] ?? ""
380 let ambience = self.getMetadata()["ambience"] ?? ""
381 let wineStorage = self.getMetadata()["wine_storage"] ?? ""
382 let wineBox = self.getMetadata()["wine_box"] ?? ""
383 let bottle = self.getMetadata()["bottle"] ?? ""
384 return CollectorMetadataViewV2(
385 id: self.id,
386 name: self.getTemplate().name,
387 description: self.getTemplate().description,
388 image: self.getTemplate().image,
389 externalUrl: externalUrl as? String,
390 architecture: architecture as? String,
391 panelling: panelling as? String,
392 visualElements: visualElements as? String,
393 ambience: ambience as? String,
394 wineStorage: wineStorage as? String,
395 wineBox: wineBox as? String,
396 bottle: bottle as? String,
397 setID: self.setID,
398 templateID: self.templateID,
399 serialNumber: self.serialNumber,
400 mintingDate: self.mintingDate
401 )
402 case Type<MetadataViews.NFTCollectionData>():
403 return MetadataViews.NFTCollectionData(
404 storagePath: Collector.CollectionStoragePath,
405 publicPath: Collector.CollectionPublicPath,
406 providerPath: /private/CollectorCollection,
407 publicCollection: Type<&Collector.Collection{Collector.NFTCollectionPublic}>(),
408 publicLinkedType: Type<&Collector.Collection{Collector.NFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
409 providerLinkedType: Type<&Collector.Collection{Collector.NFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
410 createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
411 return <- Collector.createEmptyCollection()
412 })
413 )
414 case Type<MetadataViews.NFTCollectionDisplay>():
415 let externalUrl = "https://www.cuveecollective.com/marketplace/collections/collector"
416
417 return MetadataViews.NFTCollectionDisplay(
418 name: "Cuvée Collective - Collector",
419 description: "Our genesis collection for Cuvée Collective, called The Collector. The Collector NFT gives the NFT holder access to all future NFT drops 24 hours before they are open to the public, in addition to core Cuvée Collective benefits to include members-only discord channels, community voting status, concierge services, Sommelier hotline, and airdrops, events, and giveaways.",
420 externalURL: MetadataViews.ExternalURL(
421 url: externalUrl
422 ),
423 squareImage: MetadataViews.Media(
424 file: MetadataViews.HTTPFile(
425 url: "https://assets.test-cuveecollective.com/nfts/collector/nft_collector_1.jpg"
426 ),
427 mediaType: "image/jpeg"
428 ),
429 bannerImage: MetadataViews.Media(
430 file: MetadataViews.HTTPFile(
431 url: "https://assets.cuveecollective.com/nfts/collector/nft_collector_1_banner.jpg"
432 ),
433 mediaType: "image/jpeg"
434 ),
435 socials: {
436 "twitter": MetadataViews.ExternalURL(
437 url: "https://twitter.com/cuveecollective"
438 ),
439 "instagram": MetadataViews.ExternalURL(
440 url: "https://twitter.com/cuveecollectivehq"
441 ),
442 "discord": MetadataViews.ExternalURL(
443 url: "https://cuveecollective.com/discord"
444 )
445 }
446 )
447 }
448
449 return nil
450 }
451
452 pub fun getSetData(): SetData {
453 return Collector.SetsData[self.setID]!
454 }
455
456 pub fun getTemplate(): Template {
457 return Collector.Templates[self.templateID]!
458 }
459
460 pub fun getMetadata(): {String: AnyStruct} {
461 return Collector.Templates[self.templateID]!.getMetadata()
462 }
463 }
464
465 pub resource interface NFTCollectionPublic {
466 pub fun deposit(token: @NonFungibleToken.NFT)
467 pub fun getIDs(): [UInt64]
468 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
469 pub fun borrowCollector(id: UInt64): &Collector.NFT? {
470 post {
471 (result == nil) || (result?.id == id):
472 "Cannot borrow collector reference: the ID of the returned reference is incorrect"
473 }
474 }
475 }
476
477 pub resource Collection: NFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
478 pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
479
480 pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
481 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Missing NFT")
482 emit Withdraw(id: token.id, from: self.owner?.address)
483 return <-token
484 }
485
486 pub fun deposit(token: @NonFungibleToken.NFT) {
487 let token <- token as! @Collector.NFT
488 let id: UInt64 = token.id
489 let oldToken <- self.ownedNFTs[id] <- token
490 emit Deposit(id: id, to: self.owner?.address)
491 destroy oldToken
492 }
493
494 pub fun batchDeposit(collection: @Collection) {
495 let keys = collection.getIDs()
496 for key in keys {
497 self.deposit(token: <-collection.withdraw(withdrawID: key))
498 }
499 destroy collection
500 }
501
502 pub fun getIDs(): [UInt64] {
503 return self.ownedNFTs.keys
504 }
505
506 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
507 let ref = &self.ownedNFTs[id] as &NonFungibleToken.NFT?
508 return ref!
509 }
510
511 pub fun borrowCollector(id: UInt64): &Collector.NFT? {
512 if self.ownedNFTs[id] != nil {
513 let ref = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT?
514 return ref! as! &Collector.NFT
515 } else {
516 return nil
517 }
518 }
519
520 pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
521 let nft = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT?
522 let exampleNFT = nft! as! &Collector.NFT
523 return exampleNFT as &AnyResource{MetadataViews.Resolver}
524 }
525
526 destroy() {
527 destroy self.ownedNFTs
528 }
529
530 init() {
531 self.ownedNFTs <- {}
532 }
533 }
534
535 pub fun createEmptyCollection(): @NonFungibleToken.Collection {
536 return <- create Collection()
537 }
538
539 // A Set is special resource type that contains functions to mint Collector NFTs,
540 // add Templates, update Templates and Set metadata, and lock Sets and Templates.
541 pub resource Set {
542 pub let id: UInt64
543
544 pub var locked: Bool
545 pub var isPublic: Bool
546 pub var nextSerialNumber: UInt64
547
548 access(self) var templateIDs: [UInt64]
549 access(self) var availableTemplateIDs: [UInt64]
550
551 init(name: String, description: String, image: String, metadata: {String: AnyStruct}, maxSize: UInt64?) {
552 pre {
553 metadata.length != 0: "Set metadata cannot be empty"
554 }
555
556 self.id = Collector.nextSetID
557
558 self.locked = false
559 self.isPublic = false
560 self.nextSerialNumber = 1
561
562 self.templateIDs = []
563 self.availableTemplateIDs = []
564
565 Collector.SetsData[self.id] = SetData(
566 id: self.id,
567 name: name,
568 description: description,
569 image: image,
570 metadata: metadata,
571 maxSize: maxSize
572 )
573
574 Collector.nextSetID = Collector.nextSetID + 1
575
576 emit SetCreated(id: self.id, name: name)
577 }
578
579 pub fun updateImage(newImage: String) {
580 pre {
581 self.locked == false: "Cannot update image: set is locked"
582 }
583
584 let oldData = Collector.SetsData[self.id]!
585
586 Collector.SetsData[self.id] = SetData(
587 id: self.id,
588 name: oldData.name,
589 description: oldData.description,
590 image: newImage,
591 metadata: oldData.getMetadata(),
592 maxSize: oldData.maxSize
593 )
594
595 emit SetUpdated(id: self.id, name: oldData.name)
596 }
597
598 pub fun updateMetadata(newMetadata: {String: AnyStruct}) {
599 pre {
600 self.locked == false: "Cannot update metadata: set is locked"
601 newMetadata.length != 0: "New set metadata cannot be empty"
602 }
603
604 let oldData = Collector.SetsData[self.id]!
605
606 Collector.SetsData[self.id] = SetData(
607 id: self.id,
608 name: oldData.name,
609 description: oldData.description,
610 image: oldData.image,
611 metadata: newMetadata,
612 maxSize: oldData.maxSize
613 )
614
615 emit SetUpdated(id: self.id, name: oldData.name)
616 }
617
618 pub fun makePublic() {
619 pre {
620 self.isPublic == false: "Set is already public"
621 }
622
623 self.isPublic = true
624 }
625
626 pub fun makePrivate() {
627 pre {
628 self.isPublic == true: "Set is already private"
629 }
630
631 self.isPublic = false
632 }
633
634 pub fun addTemplate(id: UInt64) {
635 pre {
636 Collector.Templates[id] != nil:
637 "Template doesn't exist"
638 !self.locked:
639 "Cannot add template: set is locked"
640 !self.templateIDs.contains(id):
641 "Cannot add template: template is already added to the set"
642 !(Collector.Templates[id]!.addedToSet != 0):
643 "Cannot add template: template is already added to another set"
644 }
645
646 if let maxSize = Collector.SetsData[self.id]!.maxSize {
647 if UInt64(self.templateIDs.length) >= maxSize {
648 panic("Set is full")
649 }
650 }
651
652 self.templateIDs.append(id)
653 self.availableTemplateIDs.append(id)
654
655 // This function will automatically emit TemplateAddedToSet event
656 Collector.Templates[id]!.markAddedToSet(setID: self.id)
657 }
658
659 pub fun addTemplates(templateIDs: [UInt64]) {
660 for templateID in templateIDs {
661 self.addTemplate(id: templateID)
662 }
663 }
664
665 pub fun lock() {
666 pre {
667 self.locked == false: "Set is already locked"
668 }
669
670 self.locked = true
671 emit SetLocked(id: self.id, name: Collector.SetsData[self.id]!.name)
672 }
673
674 pub fun unlock() {
675 pre {
676 self.locked == true: "Set is already unlocked"
677 }
678
679 self.locked = false
680 emit SetUnlocked(id: self.id, name: Collector.SetsData[self.id]!.name)
681 }
682
683 pub fun mintNFT(): @NFT {
684 let templateID = self.availableTemplateIDs[0]
685
686 let newNFT: @NFT <- create Collector.NFT(id: Collector.totalSupply + 1, templateID: templateID, serialNumber: self.nextSerialNumber)
687
688 Collector.totalSupply = Collector.totalSupply + 1
689 self.nextSerialNumber = self.nextSerialNumber + 1
690 self.availableTemplateIDs.remove(at: 0)
691
692 emit Minted(id: newNFT.id, templateID: newNFT.templateID, setID: newNFT.setID)
693 return <- newNFT
694 }
695
696 pub fun updateTemplateName(id: UInt64, newName: String) {
697 pre {
698 Collector.Templates[id] != nil:
699 "Template doesn't exist"
700 self.templateIDs.contains(id):
701 "Cannot edit template: template is not part of this set"
702 !self.locked:
703 "Cannot edit template: set is locked"
704 }
705
706 // This function will automatically emit TemplateUpdated event
707 Collector.Templates[id]!.updateName(newName: newName)
708 }
709
710 pub fun updateTemplateDescription(id: UInt64, newDescription: String) {
711 pre {
712 Collector.Templates[id] != nil:
713 "Template doesn't exist"
714 self.templateIDs.contains(id):
715 "Cannot edit template: template is not part of this set"
716 !self.locked:
717 "Cannot edit template: set is locked"
718 }
719
720 // This function will automatically emit TemplateUpdated event
721 Collector.Templates[id]!.updateDescription(newDescription: newDescription)
722 }
723
724 pub fun updateTemplateImage(id: UInt64, newImage: String) {
725 pre {
726 Collector.Templates[id] != nil:
727 "Template doesn't exist"
728 self.templateIDs.contains(id):
729 "Cannot edit template: template is not part of this set"
730 !self.locked:
731 "Cannot edit template: set is locked"
732 }
733
734 // This function will automatically emit TemplateUpdated event
735 Collector.Templates[id]!.updateImage(newImage: newImage)
736 }
737
738 pub fun updateTemplateMetadata(id: UInt64, newMetadata: {String: AnyStruct}) {
739 pre {
740 Collector.Templates[id] != nil:
741 "Template doesn't exist"
742 self.templateIDs.contains(id):
743 "Cannot edit template: template is not part of this set"
744 !self.locked:
745 "Cannot edit template: set is locked"
746 }
747
748 // This function will automatically emit TemplateUpdated event
749 Collector.Templates[id]!.updateMetadata(newMetadata: newMetadata)
750 }
751
752 pub fun lockTemplate(id: UInt64) {
753 pre {
754 Collector.Templates[id] != nil:
755 "Template doesn't exist"
756 self.templateIDs.contains(id):
757 "Cannot lock template: template is not part of this set"
758 !self.locked:
759 "Cannot lock template: set is locked"
760 }
761
762 // This function will automatically emit TemplateLocked event
763 Collector.Templates[id]!.lock()
764 }
765
766 pub fun getMetadata(): {String: AnyStruct} {
767 return Collector.SetsData[self.id]!.getMetadata()
768 }
769
770 pub fun getAvailableTemplateIDs(): [UInt64] {
771 return self.availableTemplateIDs
772 }
773 }
774
775 pub resource Admin {
776 pub fun mintNFT(recipient: &{NonFungibleToken.CollectionPublic}, setID: UInt64) {
777 let set = self.borrowSet(id: setID)
778 if (set.getAvailableTemplateIDs().length == 0){
779 panic("Set is empty")
780 }
781 recipient.deposit(token: <- set.mintNFT())
782 }
783
784 pub fun createTemplate(name: String, description: String, image: String, metadata: {String: AnyStruct}): UInt64 {
785 let templateID = Collector.nextTemplateID
786
787 // This function will automatically emit TemplateCreated event
788 Collector.Templates[templateID] = Template(
789 id: templateID,
790 name: name,
791 description: description,
792 image: image,
793 metadata: metadata
794 )
795
796 return templateID
797 }
798
799 pub fun updateTemplateName(id: UInt64, newName: String) {
800 pre {
801 Collector.Templates.containsKey(id) != nil:
802 "Template doesn't exits"
803 }
804
805 // This function will automatically emit TemplateUpdated event
806 Collector.Templates[id]!.updateName(newName: newName)
807 }
808
809 pub fun updateTemplateDescription(id: UInt64, newDescription: String) {
810 pre {
811 Collector.Templates.containsKey(id) != nil:
812 "Template doesn't exits"
813 }
814
815 // This function will automatically emit TemplateUpdated event
816 Collector.Templates[id]!.updateDescription(newDescription: newDescription)
817 }
818
819 pub fun updateTemplateImage(id: UInt64, newImage: String) {
820 pre {
821 Collector.Templates.containsKey(id) != nil:
822 "Template doesn't exits"
823 }
824
825 // This function will automatically emit TemplateUpdated event
826 Collector.Templates[id]!.updateImage(newImage: newImage)
827 }
828
829 pub fun updateTemplateMetadata(id: UInt64, newMetadata: {String: String}) {
830 pre {
831 Collector.Templates.containsKey(id) != nil:
832 "Template doesn't exits"
833 }
834
835 // This function will automatically emit TemplateUpdated event
836 Collector.Templates[id]!.updateMetadata(newMetadata: newMetadata)
837 }
838
839 pub fun lockTemplate(id: UInt64) {
840 pre {
841 Collector.Templates.containsKey(id) != nil:
842 "Template doesn't exits"
843 }
844
845 // This function will automatically emit TemplateLocked event
846 Collector.Templates[id]!.lock()
847 }
848
849 pub fun createSet(name: String, description: String, image: String, metadata: {String: String}, maxSize: UInt64?) {
850 var newSet <- create Set(
851 name: name,
852 description: description,
853 image: image,
854 metadata: metadata,
855 maxSize: maxSize
856 )
857 Collector.sets[newSet.id] <-! newSet
858 }
859
860 pub fun borrowSet(id: UInt64): &Set {
861 pre {
862 Collector.sets[id] != nil: "Cannot borrow set: set doesn't exist"
863 }
864
865 let ref = &Collector.sets[id] as &Set?
866 return ref!
867 }
868
869 pub fun updateSetImage(id: UInt64, newImage: String) {
870 let set = self.borrowSet(id: id)
871 set.updateImage(newImage: newImage)
872 }
873
874 pub fun updateSetMetadata(id: UInt64, newMetadata: {String: AnyStruct}) {
875 let set = self.borrowSet(id: id)
876 set.updateMetadata(newMetadata: newMetadata)
877 }
878 }
879
880 pub fun getTemplate(id: UInt64): Collector.Template? {
881 return self.Templates[id]
882 }
883
884 pub fun getTemplates(): {UInt64: Collector.Template} {
885 return self.Templates
886 }
887
888 pub fun getSetIDs(): [UInt64] {
889 return self.sets.keys
890 }
891
892 pub fun getSetData(id: UInt64): Collector.SetData? {
893 return Collector.SetsData[id]
894 }
895
896 pub fun getSetsData(): {UInt64: Collector.SetData} {
897 return self.SetsData
898 }
899
900 pub fun getSetSize(id: UInt64): UInt64 {
901 pre {
902 self.sets[id] != nil: "Cannot borrow set: set doesn't exist"
903 }
904
905 let set = &self.sets[id] as &Set?
906
907 return set!.nextSerialNumber - 1
908 }
909
910 pub fun getAvailableTemplateIDsInSet(id: UInt64): [UInt64] {
911 pre {
912 self.sets[id] != nil: "Cannot borrow set: set doesn't exist"
913 }
914
915 let set = &self.sets[id] as &Set?
916 return set!.getAvailableTemplateIDs()
917 }
918
919 init() {
920 self.CollectionStoragePath = /storage/CollectorCollection
921 self.CollectionPublicPath = /public/CollectorCollection
922 self.AdminStoragePath = /storage/CollectorAdmin
923 self.AdminPrivatePath = /private/CollectorAdminUpgrade
924
925 self.totalSupply = 0
926 self.nextTemplateID = 1
927 self.nextSetID = 1
928 self.sets <- {}
929
930 self.SetsData = {}
931 self.Templates = {}
932
933 let admin <- create Admin()
934 self.account.save(<-admin, to: self.AdminStoragePath)
935
936 self.account.link<&Collector.Admin>(
937 self.AdminPrivatePath,
938 target: self.AdminStoragePath
939 ) ?? panic("Could not get a capability to the admin")
940
941 emit ContractInitialized()
942 }
943}
944