Smart Contract

Wine

A.fb27085fbb495d1d.Wine

Deployed

2h ago
Feb 28, 2026, 09:13:55 PM UTC

Dependents

0 imports
1// SPDX-License-Identifier: UNLICENSED
2
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5
6pub contract Wine: NonFungibleToken {
7  // Emitted when the Wine 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 Wine 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 maxSupply: UInt64
63
64    pub var locked: Bool
65    pub var addedToSet: UInt64
66
67    access(self) var metadata: {String: AnyStruct}
68
69    init(id: UInt64, name: String, description: String, image: String, maxSupply: UInt64, metadata: {String: AnyStruct}){
70      pre {
71        maxSupply > 0: "Supply must be more than zero"
72        metadata.length != 0: "New template metadata cannot be empty"
73      }
74
75      self.id = id
76      self.name = name
77      self.description = description
78      self.image = image
79      self.metadata = metadata
80      self.maxSupply = maxSupply
81      self.locked = false
82      self.addedToSet = 0
83
84      Wine.nextTemplateID = Wine.nextTemplateID + 1
85
86      emit TemplateCreated(id: self.id, name: self.name)
87    }
88
89    pub fun updateName(newName: String) {
90      pre {
91        self.locked == false: "Cannot update name: template is locked"
92      }
93
94      self.name = newName
95      emit TemplateUpdated(id: self.id, name: self.name)
96    }
97
98    pub fun updateDescription(newDescription: String) {
99      pre {
100        self.locked == false: "Cannot update description: template is locked"
101      }
102
103      self.description = newDescription
104      emit TemplateUpdated(id: self.id, name: self.name)
105    }
106
107    pub fun updateImage(newImage: String) {
108      pre {
109        self.locked == false: "Cannot update image: template is locked"
110      }
111
112      self.image = newImage
113      emit TemplateUpdated(id: self.id, name: self.name)
114    }
115
116    pub fun updateMaxSupply(newMaxSupply: UInt64) {
117      pre {
118        self.locked == false: "Cannot update image: template is locked"
119        self.maxSupply > newMaxSupply: "Cannot reduce max supply"
120      }
121
122      self.maxSupply = newMaxSupply
123      emit TemplateUpdated(id: self.id, name: self.name)
124    }
125
126    pub fun updateMetadata(newMetadata: {String: AnyStruct}) {
127      pre {
128        self.locked == false: "Cannot update metadata: template is locked"
129        newMetadata.length != 0: "New template metadata cannot be empty"
130      }
131
132      self.metadata = newMetadata
133      emit TemplateUpdated(id: self.id, name: self.name)
134    }
135    
136    pub fun markAddedToSet(setID: UInt64) {
137      pre {
138        self.addedToSet == 0: "Template is already to a set"
139      }
140
141      self.addedToSet = setID
142
143      let setName = Wine.SetsData[setID]!.name
144      emit TemplateAddedToSet(id: self.id, name: self.name, setID: setID, setName: setName)
145    }
146
147    pub fun lock() {      
148      pre {
149        self.locked == false: "Template is already locked"
150      }
151
152      self.locked = true
153      emit TemplateLocked(id: self.id, name: self.name)
154    }
155
156    pub fun getMetadata(): {String: AnyStruct} {
157      return self.metadata
158    }
159  }
160
161  // An SetData is a Struct that holds data associated with a specific Set
162  pub struct SetData {
163    pub let id: UInt64
164
165    pub var name: String
166    pub var description: String
167    pub var image: String
168
169    access(self) var metadata: {String: AnyStruct}
170
171    init(id: UInt64, name: String, description: String, image: String, metadata: {String: AnyStruct}) {
172      self.id = id
173      self.name = name
174      self.description = description
175      self.image = image
176      self.metadata = metadata
177    }
178
179    pub fun getMetadata(): {String: AnyStruct} {
180      return self.metadata
181    }
182  }
183
184  // A resource that represents the Wine NFT
185  pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
186    pub let id: UInt64
187
188    pub let setID: UInt64
189    pub let templateID: UInt64
190    pub let editionNumber: UInt64
191    pub let serialNumber: UInt64
192    pub let mintingDate: UFix64
193
194    init(id: UInt64, templateID: UInt64, editionNumber: UInt64, serialNumber: UInt64) {
195      pre {
196        Wine.getTemplate(id: templateID) != nil: "Template not found"
197      }
198
199      let setID = Wine.getTemplate(id: templateID)!.addedToSet
200
201      self.id = id
202      self.setID = setID
203      self.templateID = templateID
204      self.editionNumber = editionNumber
205      self.serialNumber = serialNumber
206      self.mintingDate = getCurrentBlock().timestamp
207    }
208
209    pub fun getViews(): [Type] {
210      return [
211        Type<MetadataViews.Display>(),
212        Type<MetadataViews.Serial>(),
213        Type<MetadataViews.Royalties>(),
214        Type<MetadataViews.ExternalURL>(),
215        Type<MetadataViews.Editions>(),
216        Type<MetadataViews.Traits>(),
217        Type<MetadataViews.NFTCollectionData>(),
218        Type<MetadataViews.NFTCollectionDisplay>()
219      ]
220    }
221
222    pub fun resolveView(_ view: Type): AnyStruct? {
223      switch view {
224        case Type<MetadataViews.Display>():
225        let nftName = self.getTemplate().name.concat(" #").concat(self.editionNumber.toString())
226          return MetadataViews.Display(
227            name: nftName,
228            description: self.getTemplate().description,
229            thumbnail: MetadataViews.HTTPFile(
230              url: self.getTemplate().image
231            )
232          )
233        case Type<MetadataViews.Serial>():
234          return MetadataViews.Serial(
235            self.serialNumber
236          )
237        case Type<MetadataViews.Royalties>():
238          var royalties: [MetadataViews.Royalty] = []
239          return MetadataViews.Royalties(royalties)
240        case Type<MetadataViews.ExternalURL>():
241          return MetadataViews.ExternalURL(self.getExternalUrl())
242        case Type<MetadataViews.Editions>():
243          let template = Wine.Templates[self.templateID]!
244          let editionInfo = MetadataViews.Edition(
245            name: template.name,
246            number: self.editionNumber,
247            max: template.maxSupply
248          )
249          let editionList: [MetadataViews.Edition] = [editionInfo]
250          return MetadataViews.Editions(
251            editionList
252          )
253        case Type<MetadataViews.Traits>():
254          let excludedTraits = ["external_base_url"]
255          let traitsView = MetadataViews.dictToTraits(dict: self.getMetadata(), excludedNames: excludedTraits)
256
257          // mintingDate is a unix timestamp, we should mark it with a displayType so platforms know how to show it
258          let mintingDateTrait = MetadataViews.Trait(name: "minting_date", value: self.mintingDate, displayType: "Date", rarity: nil)
259          traitsView.addTrait(mintingDateTrait)
260
261          return traitsView
262        case Type<MetadataViews.NFTCollectionData>():
263          return MetadataViews.NFTCollectionData(
264            storagePath: Wine.CollectionStoragePath,
265            publicPath: Wine.CollectionPublicPath,
266            providerPath: /private/WineCollection,
267            publicCollection: Type<&Wine.Collection{Wine.NFTCollectionPublic}>(),
268            publicLinkedType: Type<&Wine.Collection{Wine.NFTCollectionPublic, NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, MetadataViews.ResolverCollection}>(),
269            providerLinkedType: Type<&Wine.Collection{Wine.NFTCollectionPublic, NonFungibleToken.CollectionPublic, NonFungibleToken.Provider, MetadataViews.ResolverCollection}>(),
270            createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
271              return <- Wine.createEmptyCollection()
272            })
273          )
274        case Type<MetadataViews.NFTCollectionDisplay>():
275          let setData = Wine.SetsData[self.setID]!
276          let squareImageUrl = setData.getMetadata()["image.media_type"] as! String?
277          return MetadataViews.NFTCollectionDisplay(
278            name: setData.name,
279            description: setData.description,
280            externalURL: MetadataViews.ExternalURL(
281              url: (setData.getMetadata()["external_url"] as! String?)!
282            ),
283            squareImage: MetadataViews.Media(
284              file: MetadataViews.HTTPFile(
285                url: setData.image
286              ),
287              mediaType: squareImageUrl ?? "image/jpeg"
288            ),
289            bannerImage: MetadataViews.Media(
290              file: MetadataViews.HTTPFile(
291                url: (setData.getMetadata()["banner_image.url"] as! String?)!
292              ),
293              mediaType: (setData.getMetadata()["banner_image.media_type"] as! String?)!
294            ),
295            socials: {
296              "twitter": MetadataViews.ExternalURL(
297                url: "https://twitter.com/cuveecollective"
298              ),
299              "instagram": MetadataViews.ExternalURL(
300                url: "https://twitter.com/cuveecollectivehq"
301              ),
302              "discord": MetadataViews.ExternalURL(
303                url: "https://cuveecollective.com/discord"
304              )
305            }
306          )
307      }
308
309      return nil
310    }
311
312    pub fun getSetData(): SetData {
313      return Wine.SetsData[self.setID]!
314    }
315
316    pub fun getTemplate(): Template {
317      return Wine.Templates[self.templateID]!
318    }
319
320    pub fun getMetadata(): {String: AnyStruct} {
321      return Wine.Templates[self.templateID]!.getMetadata()
322    }
323    
324    pub fun getExternalUrl(): String {
325      let template = self.getTemplate()
326      let extBaseUrl = template.getMetadata()["external_base_url"] as! String?
327      return extBaseUrl!.concat("/").concat(template.id.toString())
328    }
329  }
330
331  pub resource interface NFTCollectionPublic {
332    pub fun deposit(token: @NonFungibleToken.NFT)
333    pub fun getIDs(): [UInt64]
334    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
335    pub fun borrowWine(id: UInt64): &Wine.NFT? {
336      post {
337        (result == nil) || (result?.id == id):
338          "Cannot borrow wine reference: the ID of the returned reference is incorrect"
339      }
340    }
341  }
342
343  pub resource Collection: NFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
344    pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
345
346    pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
347      let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Missing NFT")
348      emit Withdraw(id: token.id, from: self.owner?.address)
349      return <-token
350    }
351
352    pub fun deposit(token: @NonFungibleToken.NFT) {
353      let token <- token as! @Wine.NFT
354      let id: UInt64 = token.id
355      let oldToken <- self.ownedNFTs[id] <- token
356      emit Deposit(id: id, to: self.owner?.address)
357      destroy oldToken
358    }
359
360    pub fun batchDeposit(collection: @Collection) {
361      let keys = collection.getIDs()
362      for key in keys {
363        self.deposit(token: <-collection.withdraw(withdrawID: key))
364      }
365      destroy collection
366    }
367
368    pub fun getIDs(): [UInt64] {
369      return self.ownedNFTs.keys
370    }
371
372    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
373      let ref = &self.ownedNFTs[id] as &NonFungibleToken.NFT?
374      return ref!
375    }
376
377    pub fun borrowWine(id: UInt64): &Wine.NFT? {
378      if self.ownedNFTs[id] != nil {
379        let ref = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT?
380        return ref! as! &Wine.NFT
381      } else {
382        return nil
383      }
384    }
385
386    pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
387      let nft = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT?
388      let wineNFT = nft! as! &Wine.NFT
389      return wineNFT as &AnyResource{MetadataViews.Resolver}
390    }
391
392    destroy() {
393      destroy self.ownedNFTs
394    }
395
396    init() {
397      self.ownedNFTs <- {}
398    }
399  }
400  
401  pub fun createEmptyCollection(): @NonFungibleToken.Collection {
402    return <- create Collection()
403  }
404
405  // A Set is special resource type that contains functions to mint Wine NFTs, 
406  // add Templates, update Templates and Set metadata, and lock Sets and Templates.
407  pub resource Set {
408    pub let id: UInt64
409
410    pub var locked: Bool
411    pub var isPublic: Bool
412    pub var nextSerialNumber: UInt64
413
414    access(self) var templateIDs: [UInt64]
415    access(self) var templateSupplies: {UInt64: UInt64}
416
417    init(name: String, description: String, image: String, metadata: {String: AnyStruct}) {
418      pre {
419        metadata.length != 0: "Set metadata cannot be empty"
420      }
421
422      self.id = Wine.nextSetID
423
424      self.locked = false
425      self.isPublic = false
426      self.nextSerialNumber = 1
427
428      self.templateIDs = []
429      self.templateSupplies = {}
430
431      Wine.SetsData[self.id] = SetData(
432        id: self.id,
433        name: name,
434        description: description,
435        image: image,
436        metadata: metadata
437      )
438
439      Wine.nextSetID = Wine.nextSetID + 1
440
441      emit SetCreated(id: self.id, name: name)
442    }
443
444    pub fun updateImage(newImage: String) {
445      pre {
446        self.locked == false: "Cannot update image: set is locked"
447      }
448
449      let oldData = Wine.SetsData[self.id]!
450
451      Wine.SetsData[self.id] = SetData(
452        id: self.id,
453        name: oldData.name,
454        description: oldData.description,
455        image: newImage,
456        metadata: oldData.getMetadata()
457      )
458
459      emit SetUpdated(id: self.id, name: oldData.name)
460    }
461
462    pub fun updateMetadata(newMetadata: {String: AnyStruct}) {
463      pre {
464        self.locked == false: "Cannot update metadata: set is locked"
465        newMetadata.length != 0: "New set metadata cannot be empty"
466      }
467
468      let oldData = Wine.SetsData[self.id]!
469
470      Wine.SetsData[self.id] = SetData(
471        id: self.id,
472        name: oldData.name,
473        description: oldData.description,
474        image: oldData.image,
475        metadata: newMetadata
476      )
477
478      emit SetUpdated(id: self.id, name: oldData.name)
479    }
480
481    pub fun makePublic() {
482      pre {
483        self.isPublic == false: "Set is already public"
484      }
485
486      self.isPublic = true
487    }
488
489    pub fun makePrivate() {
490      pre {
491        self.isPublic == true: "Set is already private"
492      }
493
494      self.isPublic = false
495    }
496
497    pub fun addTemplate(id: UInt64) {
498      pre {
499        Wine.Templates[id] != nil:
500          "Template doesn't exist"
501        !self.locked:
502          "Cannot add template: set is locked"
503        !self.templateIDs.contains(id):
504          "Cannot add template: template is already added to the set"
505        !(Wine.Templates[id]!.addedToSet != 0):
506          "Cannot add template: template is already added to another set"
507      }
508
509      self.templateIDs.append(id)
510      self.templateSupplies[id] = 0
511
512      // This function will automatically emit TemplateAddedToSet event
513      Wine.Templates[id]!.markAddedToSet(setID: self.id)
514    }
515
516    pub fun addTemplates(templateIDs: [UInt64]) {
517      for templateID in templateIDs {
518        self.addTemplate(id: templateID)
519      }
520    }
521
522    pub fun lock() {
523      pre {
524        self.locked == false: "Set is already locked"
525      }
526
527      self.locked = true
528      emit SetLocked(id: self.id, name: Wine.SetsData[self.id]!.name)
529    }
530
531    pub fun unlock() {
532      pre {
533        self.locked == true: "Set is already unlocked"
534      }
535
536      self.locked = false
537      emit SetUnlocked(id: self.id, name: Wine.SetsData[self.id]!.name)
538    }
539
540    pub fun mintNFT(templateID: UInt64): @NFT {
541      let nextEditionNumber = self.templateSupplies[templateID]! + 1
542
543      if nextEditionNumber >= Wine.Templates[templateID]!.maxSupply {
544        panic("Supply unavailable")
545      }
546
547      let newNFT: @NFT <- create Wine.NFT(
548        id: Wine.totalSupply + 1,
549        templateID: templateID,
550        editionNumber: nextEditionNumber,
551        serialNumber: self.nextSerialNumber
552      )
553      
554      Wine.totalSupply = Wine.totalSupply + 1
555      self.nextSerialNumber = self.nextSerialNumber + 1
556      self.templateSupplies[templateID] = self.templateSupplies[templateID]! + 1
557
558      emit Minted(id: newNFT.id, templateID: newNFT.templateID, setID: newNFT.setID)
559      return <- newNFT
560    }
561
562    pub fun updateTemplateName(id: UInt64, newName: String) {
563      pre {
564        Wine.Templates[id] != nil:
565          "Template doesn't exist"
566        self.templateIDs.contains(id):
567          "Cannot edit template: template is not part of this set"
568        !self.locked:
569          "Cannot edit template: set is locked"
570      }
571
572      // This function will automatically emit TemplateUpdated event
573      Wine.Templates[id]!.updateName(newName: newName)
574    }
575
576    pub fun updateTemplateDescription(id: UInt64, newDescription: String) {
577      pre {
578        Wine.Templates[id] != nil:
579          "Template doesn't exist"
580        self.templateIDs.contains(id):
581          "Cannot edit template: template is not part of this set"
582        !self.locked:
583          "Cannot edit template: set is locked"
584      }
585
586      // This function will automatically emit TemplateUpdated event
587      Wine.Templates[id]!.updateDescription(newDescription: newDescription)
588    }
589
590    pub fun updateTemplateImage(id: UInt64, newImage: String) {
591      pre {
592        Wine.Templates[id] != nil:
593          "Template doesn't exist"
594        self.templateIDs.contains(id):
595          "Cannot edit template: template is not part of this set"
596        !self.locked:
597          "Cannot edit template: set is locked"
598      }
599
600      // This function will automatically emit TemplateUpdated event
601      Wine.Templates[id]!.updateImage(newImage: newImage)
602    }
603
604    pub fun updateTemplateMaxSupply(id: UInt64, newMaxSupply: UInt64) {
605      pre {
606        Wine.Templates[id] != nil:
607          "Template doesn't exist"
608        self.templateIDs.contains(id):
609          "Cannot edit template: template is not part of this set"
610        !self.locked:
611          "Cannot edit template: set is locked"
612      }
613
614      // This function will automatically emit TemplateUpdated event
615      Wine.Templates[id]!.updateMaxSupply(newMaxSupply: newMaxSupply)
616    }
617
618    pub fun updateTemplateMetadata(id: UInt64, newMetadata: {String: AnyStruct}) {
619      pre {
620        Wine.Templates[id] != nil:
621          "Template doesn't exist"
622        self.templateIDs.contains(id):
623          "Cannot edit template: template is not part of this set"
624        !self.locked:
625          "Cannot edit template: set is locked"
626      }
627
628      // This function will automatically emit TemplateUpdated event
629      Wine.Templates[id]!.updateMetadata(newMetadata: newMetadata)
630    }
631
632    pub fun lockTemplate(id: UInt64) {
633      pre {
634        Wine.Templates[id] != nil:
635          "Template doesn't exist"
636        self.templateIDs.contains(id):
637          "Cannot lock template: template is not part of this set"
638        !self.locked:
639          "Cannot lock template: set is locked"
640      }
641
642      // This function will automatically emit TemplateLocked event
643      Wine.Templates[id]!.lock()
644    }
645
646    pub fun getMetadata(): {String: AnyStruct} {
647      return Wine.SetsData[self.id]!.getMetadata()
648    }
649
650    pub fun getTemplateIDs(): [UInt64] {
651      return self.templateIDs
652    }
653  }
654
655  pub resource Admin {
656    pub fun mintNFT(recipient: &{NonFungibleToken.CollectionPublic}, setID: UInt64, templateID: UInt64) {
657      let set = self.borrowSet(id: setID)
658      if (set.getTemplateIDs().length == 0){
659        panic("Set is empty")
660      }
661      recipient.deposit(token: <- set.mintNFT(templateID: templateID))
662    }
663
664    pub fun createTemplate(name: String, description: String, image: String, maxSupply: UInt64, metadata: {String: AnyStruct}): UInt64 {
665      let templateID = Wine.nextTemplateID
666
667      // This function will automatically emit TemplateCreated event
668      Wine.Templates[templateID] = Template(
669        id: templateID,
670        name: name,
671        description: description,
672        image: image,
673        maxSupply: maxSupply,
674        metadata: metadata
675      )
676
677      return templateID
678    }
679
680    pub fun updateTemplateName(id: UInt64, newName: String) {
681      pre {
682        Wine.Templates.containsKey(id) != nil:
683          "Template doesn't exits"
684      }
685
686      // This function will automatically emit TemplateUpdated event
687      Wine.Templates[id]!.updateName(newName: newName)
688    }
689
690    pub fun updateTemplateDescription(id: UInt64, newDescription: String) {
691      pre {
692        Wine.Templates.containsKey(id) != nil:
693          "Template doesn't exits"
694      }
695
696      // This function will automatically emit TemplateUpdated event
697      Wine.Templates[id]!.updateDescription(newDescription: newDescription)
698    }
699
700    pub fun updateTemplateImage(id: UInt64, newImage: String) {
701      pre {
702        Wine.Templates.containsKey(id) != nil:
703          "Template doesn't exits"
704      }
705
706      // This function will automatically emit TemplateUpdated event
707      Wine.Templates[id]!.updateImage(newImage: newImage)
708    }
709
710    pub fun updateTemplateMaxSupply(id: UInt64, newMaxSupply: UInt64) {
711      pre {
712        Wine.Templates.containsKey(id) != nil:
713          "Template doesn't exits"
714      }
715
716      // This function will automatically emit TemplateUpdated event
717      Wine.Templates[id]!.updateMaxSupply(newMaxSupply: newMaxSupply)
718    }
719
720    pub fun updateTemplateMetadata(id: UInt64, newMetadata: {String: String}) {
721      pre {
722        Wine.Templates.containsKey(id) != nil:
723          "Template doesn't exits"
724      }
725
726      // This function will automatically emit TemplateUpdated event
727      Wine.Templates[id]!.updateMetadata(newMetadata: newMetadata)
728    }
729
730    pub fun lockTemplate(id: UInt64) {
731      pre {
732        Wine.Templates.containsKey(id) != nil:
733          "Template doesn't exits"
734      }
735
736      // This function will automatically emit TemplateLocked event
737      Wine.Templates[id]!.lock()
738    }
739
740    pub fun createSet(name: String, description: String, image: String, metadata: {String: String}) {
741      var newSet <- create Set(
742        name: name,
743        description: description,
744        image: image,
745        metadata: metadata
746      )
747      Wine.sets[newSet.id] <-! newSet
748    }
749
750    pub fun borrowSet(id: UInt64): &Set {
751      pre {
752        Wine.sets[id] != nil: "Cannot borrow set: set doesn't exist"
753      }
754      
755      let ref = &Wine.sets[id] as &Set?
756      return ref!
757    }
758
759    pub fun updateSetImage(id: UInt64, newImage: String) {
760      let set = self.borrowSet(id: id)
761      set.updateImage(newImage: newImage)
762    }
763
764    pub fun updateSetMetadata(id: UInt64, newMetadata: {String: AnyStruct}) {
765      let set = self.borrowSet(id: id)
766      set.updateMetadata(newMetadata: newMetadata)
767    }
768  }
769
770  pub fun getTemplate(id: UInt64): Wine.Template? {
771    return self.Templates[id]
772  }
773
774  pub fun getTemplates(): {UInt64: Wine.Template} {
775    return self.Templates
776  }
777
778  pub fun getSetIDs(): [UInt64] {
779    return self.sets.keys
780  }
781
782  pub fun getSetData(id: UInt64): Wine.SetData? {
783    return Wine.SetsData[id]
784  }
785
786  pub fun getSetsData(): {UInt64: Wine.SetData} {
787    return self.SetsData
788  }
789
790  pub fun getSetSize(id: UInt64): UInt64 {
791    pre {
792      self.sets[id] != nil: "Cannot borrow set: set doesn't exist"
793    }
794
795    let set = &self.sets[id] as &Set?
796
797    return set!.nextSerialNumber - 1
798  }
799
800  pub fun getTemplateIDsInSet(id: UInt64): [UInt64] {
801    pre {
802      self.sets[id] != nil: "Cannot borrow set: set doesn't exist"
803    }
804
805    let set = &self.sets[id] as &Set?
806    return set!.getTemplateIDs()
807  }
808
809  init() {
810    self.CollectionStoragePath = /storage/WineCollection
811    self.CollectionPublicPath = /public/WineCollection
812    self.AdminStoragePath = /storage/WineAdmin
813    self.AdminPrivatePath = /private/WineAdminUpgrade
814
815    self.totalSupply = 0
816    self.nextTemplateID = 1
817    self.nextSetID = 1
818    self.sets <- {}
819
820    self.SetsData = {}
821    self.Templates = {}
822
823    let admin <- create Admin()
824    self.account.save(<-admin, to: self.AdminStoragePath)
825
826    self.account.link<&Wine.Admin>(
827      self.AdminPrivatePath,
828      target: self.AdminStoragePath
829    ) ?? panic("Could not get a capability to the admin")
830
831    emit ContractInitialized()
832  }
833}
834