Smart Contract

Gamisodes

A.20187093790b9aef.Gamisodes

Deployed

3d ago
Feb 24, 2026, 10:28:48 AM UTC

Dependents

3336 imports
1/*
2    Description: 
3
4    authors:
5
6    INSERT DESCRIPTION HERE
7    
8*/
9
10// import NonFungibleToken from "./NonFungibleToken.cdc"
11// import MetadataViews from "./MetadataViews.cdc"
12// import ViewResolver from "./ViewResolver.cdc"
13
14// for emulator
15// import NonFungibleToken from "0xNonFungibleToken"
16// import MetadataViews from "0xMetadataViews"
17// import ViewResolver from "0xViewResolver"
18
19// for emulator
20// import NonFungibleToken from 0xf8d6e0586b0a20c7
21// import MetadataViews from 0xf8d6e0586b0a20c7
22// import ViewResolver from 0xf8d6e0586b0a20c7
23
24// for tests
25// import NonFungibleToken from NonFungibleToken
26// import MetadataViews from MetadataViews
27
28// for testnet
29// import NonFungibleToken from 0x631e88ae7f1d7c20
30// import MetadataViews from 0x631e88ae7f1d7c20
31// import ViewResolver from 0x631e88ae7f1d7c20
32
33// for mainnet
34import NonFungibleToken from 0x1d7e57aa55817448
35import MetadataViews from 0x1d7e57aa55817448
36import ViewResolver from 0x1d7e57aa55817448
37
38
39access(all) contract Gamisodes: NonFungibleToken {
40
41    // -----------------------------------------------------------------------
42    // Contract Events
43    // -----------------------------------------------------------------------
44
45    // Emitted when the contract is created
46    access(all) event ContractInitialized()
47
48    // Emitted when a new Edition struct is created
49    access(all) event EditionCreated(id: UInt32, name: String, printingLimit: UInt32?)
50
51    // Emitted when Edition Metadata is updated
52    access(all) event EditionMetadaUpdated(editionID: UInt32)
53
54
55    // default royalties
56    access(all) event DefaultRoyaltiesUpdated(name: String, cut: UFix64)
57
58    // remove default royalty
59    access(all) event DefaultRoyaltyRemoved(name: String)
60
61    // royalties for edition
62    access(all) event RoyaltiesForEditionUpdated(editionID: UInt32, name: String, cut: UFix64)
63
64    // remove royalty for edition
65    access(all) event RoyaltiesForEditionRemoved(editionID: UInt32, name: String)
66
67
68    // RevertRoyaltiesForEditionToDefault when the admin clears the specific royalties for that edition, to revert back to default royalties
69    access(all) event RevertRoyaltiesForEditionToDefault(editionID: UInt32)
70
71
72
73    // Emitted when a new item was minted
74    access(all) event ItemMinted(itemID:UInt64, merchantID: UInt32, editionID: UInt32, editionNumber: UInt32)
75
76    // Item related events 
77    //
78    // Emitted when an Item is withdrawn from a Collection
79    access(all) event Withdraw(id: UInt64, from: Address?)
80    // Emitted when an Item is deposited into a Collection
81    access(all) event Deposit(id: UInt64, to: Address?)
82    // Emitted when an Item is destroyed
83    access(all) event ItemDestroyed(id: UInt64)
84
85
86    // Named paths
87    //
88    access(all) let CollectionStoragePath: StoragePath
89    access(all) let CollectionPublicPath: PublicPath
90    access(all) let AdminStoragePath: StoragePath
91
92
93    // -----------------------------------------------------------------------
94    // Gamisodes contract-level fields.
95    // These contain actual values that are stored in the smart contract.
96    // -----------------------------------------------------------------------
97
98    // Variable size dictionary of Editions resources
99    access(self) var editions: @{UInt32: Edition}
100
101
102    // the default royalties
103    access(self) var defaultRoyalties: {String: MetadataViews.Royalty}
104
105    // If a specific NFT requires their own royalties, 
106    // the default royalties can be overwritten in this dictionary.
107    access(all) var royaltiesForSpecificEdition:  {UInt32: {String: MetadataViews.Royalty}}
108
109
110    // The ID that is used to create Admins. 
111    // Every Admins should have a unique identifier.
112    access(all) var nextAdminID: UInt32
113
114    
115
116    // The ID that is used to create Editions. 
117    // Every time an Edition is created, nextEditionID is assigned 
118    // to the edition and then is incremented by 1.
119    access(all) var nextEditionID: UInt32
120
121
122
123    // The total number of NFTs that have been created for this smart contract
124    // Because NFTs can be destroyed, it doesn't necessarily mean that this
125    // reflects the total number of NFTs in existence, just the number that
126    // have been minted to date. Also used as global nft IDs for minting.
127    access(all) var totalSupply: UInt64
128
129
130    // -----------------------------------------------------------------------
131    // Gamisodes contract-level Composite Type definitions
132    // -----------------------------------------------------------------------
133    // These are just *definitions* for Types that this contract
134    // and other accounts can use. These definitions do not contain
135    // actual stored values, but an instance (or object) of one of these Types
136    // can be created by this contract that contains stored values.
137    // -----------------------------------------------------------------------
138   
139
140    // EditionData is a struct definition to have all of the same fields as the Edition resource.
141    // it can be used to publicly read Edition data
142
143    access(all) struct EditionData {  
144        access(all) let editionID: UInt32  
145        access(all) let merchantID: UInt32  
146        access(all) let name: String  
147        access(all) var items: [UInt64]  
148        access(all) var metadata: {String: String}
149        access(all) var numberOfItemsMinted: UInt32
150        access(all) var printingLimit: UInt32?
151        
152        init(editionID: UInt32) {
153
154             if Gamisodes.editions[editionID] == nil {
155                panic("the editionID was not found")
156            }
157            let editionToRead = (&Gamisodes.editions[editionID] as &Edition?)!
158
159            self.editionID = editionID
160            self.metadata = (Gamisodes.editions[editionID]?.metadata ?? {}) 
161            self.merchantID = editionToRead.merchantID
162            self.name = editionToRead.name
163            self.printingLimit = editionToRead.printingLimit
164            self.numberOfItemsMinted=editionToRead.numberOfItemsMinted
165            self.items=Gamisodes.editions[editionID]?.items ?? []
166
167        }
168    }
169    // Edition is a Ressource that holds metadata associated 
170    // with a specific NFT
171    //
172    // NFTs will all reference an Edition as the owner of
173    // its metadata. The Editions are publicly accessible, so anyone can
174    // read the metadata associated with a specific EditionID
175    //
176    access(all) resource Edition {
177
178        // The unique ID for the Edition
179        access(all) let editionID: UInt32
180
181        // The ID of the merchant that owns the edition
182        access(all) let merchantID: UInt32
183
184        // Stores all the metadata about the edition as a string mapping
185        // This is not the long term way NFT metadata will be stored. It's a temporary
186        // construct while we figure out a better way to do metadata.
187        //
188        access(all) let metadata: {String: String}
189
190        // Array of items that are a part of this collection.
191        // When an item is added to the collection, its ID gets appended here.
192        access(contract) var items: [UInt64]
193
194        // The number of items minted in this collection.
195        // When an item is added to the collection, the numberOfItems is incremented by 1
196        // It will be used to identify the editionNumber of an item
197        // if the edition is open (printingLimit=nil), we can keep minting new items
198        // if the edition is limited (printingLimit!=nil), we can keep minting items until we reach printingLimit
199        access(all) var numberOfItemsMinted: UInt32
200
201
202        // the limit of items that can be minted. For open editions, this value should be set to nil.
203        access(all) var printingLimit: UInt32?
204
205        // the name of the edition
206        access(all) var name: String
207
208        init(merchantID: UInt32, metadata: {String: String}, name: String, printingLimit:UInt32?) {
209            pre {
210                metadata.length != 0: "Metadata cannot be empty"
211                name!=nil: "Name is undefined"
212            }
213            self.editionID = Gamisodes.nextEditionID
214            self.merchantID = merchantID
215            self.metadata = metadata
216            self.name = name
217            self.printingLimit = printingLimit
218            self.numberOfItemsMinted=0
219            self.items = []
220
221            
222            // Increment the ID so that it isn't used again
223            Gamisodes.nextEditionID = Gamisodes.nextEditionID + (1 as UInt32)
224
225            emit EditionCreated(id: self.editionID, name: self.name, printingLimit: self.printingLimit)
226        }
227
228
229        // mintItem mints a new Item and returns the newly minted Item
230        // 
231        // Pre-Conditions:
232        // If the edition is limited the number of items minted in the edition must be strictly less than the printing limit
233        //
234        // Returns: The NFT that was minted
235        // 
236        access(all) fun mintItem(): @NFT {
237            pre {
238                (self.numberOfItemsMinted < (self.printingLimit ?? (4294967295 as UInt32)  )): "We have reached the printing limit for this edition"
239            }
240
241            // Gets the number of Itms that have been minted for this Edition
242            // to use as this Item's edition number
243            let numMinted = self.numberOfItemsMinted + (1 as UInt32)
244
245            // Mint the new item
246            let newItem: @NFT <- create NFT(merchantID: self.merchantID, editionID: self.editionID, editionNumber: numMinted)
247
248
249            // Add the Item to the array of items
250            self.items.append(newItem.id)                            
251
252            // Increment the count of Items
253            self.numberOfItemsMinted = numMinted 
254
255            return <-newItem
256        }
257
258        // batchMintItems mints an arbitrary quantity of Items 
259        // and returns them as a Collection
260        // Be sure there are enough 
261        //
262        // Parameters: quantity: The quantity of Items to be minted
263        //
264        // Returns: Collection object that contains all the Items that were minted
265        //
266        access(all) fun batchMintItems(quantity: UInt32): @Collection {
267           
268            pre {
269                ((self.numberOfItemsMinted+quantity)<=(self.printingLimit ?? (4294967295 as UInt32))): "We have reached the printing limit for this edition"
270            }
271
272            let newCollection <- create Collection()
273
274            var i: UInt32 = 0
275            while i < quantity {
276                newCollection.deposit(token: <-self.mintItem())
277                i = i + (1 as UInt32)
278            }
279
280            return <-newCollection
281        }
282
283        // updateMetadata updates the metadata
284        //
285        // Parameters: 
286        //
287        // updates: a dictionary of key - values that is requested to be appended
288        //
289        // suffix: If the metadata already contains an attribute with a given key, this value should still be kept 
290        // for posteriority. Therefore, the old value to be replaced will be stored in a metadata entry with key = key+suffix. 
291        // This can offer some reassurance to the NFT owner that the metadata will never disappear.
292        // 
293        // Returns: the EditionID
294        //
295        access(all) fun updateMetadata(updates: {String:String}, suffix: String): UInt32 {
296           
297
298
299            // prevalidation 
300            // if metadata[key] exists and metadata[key+suffix] exists, we have a clash.
301            for key in updates.keys {
302
303                let newKey = key.concat(suffix)
304
305                if self.metadata[key] != nil && self.metadata[newKey]!=nil {
306                    var errorMsg = "attributes "
307                    errorMsg = errorMsg.concat(key).concat(" and ").concat(newKey).concat(" are already defined")
308                    panic(errorMsg)
309                }
310                    
311
312            }
313
314            // execution
315            for key in updates.keys {
316
317                let newKey = key.concat(suffix)
318
319                if self.metadata[key] != nil {
320                    self.metadata[newKey] = self.metadata[key]    
321                }
322                self.metadata[key] = updates[key]
323                
324            }
325
326
327            emit EditionMetadaUpdated(editionID: self.editionID)
328            
329            // Return the EditionID and return it
330            return self.editionID
331        }
332
333    }
334
335    // The struct representing an NFT Item data
336    access(all) struct ItemData {
337
338
339        // The ID of the merchant 
340        access(all) let merchantID: UInt32
341
342        // The ID of the edition that the NFT comes from
343        access(all) let editionID: UInt32
344
345        // The number of the NFT within the edition
346        access(all) let editionNumber: UInt32
347
348
349
350        init(merchantID: UInt32, editionID: UInt32, editionNumber: UInt32) {
351            self.merchantID = merchantID
352            self.editionID = editionID
353            self.editionNumber = editionNumber
354        }
355
356    }
357
358    // The resource that represents the Item NFTs
359    //
360    access(all) resource NFT: NonFungibleToken.NFT {
361
362        // Global unique item ID
363        access(all) let id: UInt64
364
365        // Struct of the metadata
366        access(all) let data: ItemData
367
368
369        init(merchantID: UInt32, editionID: UInt32, editionNumber: UInt32) {
370            
371            pre{
372                editionID > (0 as UInt32): "editionID cannot be 0"
373                editionNumber > (0 as UInt32): "editionNumber cannot be 0"
374            }
375            // Increment the global Item IDs
376            Gamisodes.totalSupply = Gamisodes.totalSupply + (1 as UInt64)
377
378            self.id = Gamisodes.totalSupply
379
380            // Set the metadata struct
381            self.data = ItemData(merchantID: merchantID, editionID: editionID, editionNumber: editionNumber)
382
383            
384            emit ItemMinted(itemID: self.id, merchantID: merchantID, editionID: editionID, editionNumber: editionNumber)
385        }
386
387        access(all) view fun getData(): ItemData {
388            return self.data
389        }
390
391                /// createEmptyCollection creates an empty Collection
392        /// and returns it to the caller so that they can own NFTs
393        /// @{NonFungibleToken.Collection}
394        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
395            return <-Gamisodes.createEmptyCollection(nftType: Type<@Gamisodes.NFT>())
396        }
397
398
399        access(all) view fun getViews(): [Type] {
400            return [
401                Type<MetadataViews.Display>(),
402                Type<MetadataViews.Royalties>(),
403                Type<MetadataViews.Editions>(),
404                Type<MetadataViews.ExternalURL>(),
405                Type<MetadataViews.NFTCollectionData>(),
406                Type<MetadataViews.NFTCollectionDisplay>(),
407                Type<MetadataViews.Serial>(),
408                Type<MetadataViews.Traits>()
409            ]
410        }
411
412        /// @return An array of Types defining the implemented views. This value will be used by
413    ///         developers to know which parameter to pass to the resolveView() method.
414    ///
415    
416
417
418        access(all) fun resolveView(_ view: Type): AnyStruct? {
419            switch view {
420                case Type<MetadataViews.Display>():
421                    let edition = EditionData(editionID: self.data.editionID);
422                    return MetadataViews.Display(
423                        name: edition.name,
424                        description: edition.metadata["description"] ?? "",
425                        thumbnail: MetadataViews.HTTPFile(
426                            url: edition.metadata["thumbnail"] ?? ""
427                        )
428                    )
429                case Type<MetadataViews.Editions>():
430                    let edition = EditionData(editionID: self.data.editionID);
431                    let maxNumber = edition.printingLimit ?? nil
432                    var max: UInt64? = nil;
433                    if maxNumber != nil {
434                     max = UInt64(maxNumber!)
435                    }
436                      
437
438                    let editionInfo = MetadataViews.Edition(name: edition.name, number: UInt64(self.data.editionNumber), max:max)
439                    let editionList: [MetadataViews.Edition] = [editionInfo]
440                    return MetadataViews.Editions(
441                        editionList
442                    )
443                
444                case Type<MetadataViews.ExternalURL>():
445                    return MetadataViews.ExternalURL("https://gamisodes.com")
446
447
448                case Type<MetadataViews.Royalties>():
449                    let royaltiesDictionary = Gamisodes.royaltiesForSpecificEdition[self.data.editionID] ?? Gamisodes.defaultRoyalties 
450                    var royalties: [MetadataViews.Royalty] = []
451                    for royaltyName in royaltiesDictionary.keys {
452                        royalties.append(royaltiesDictionary[royaltyName]!)
453                    }
454                  return MetadataViews.Royalties(royalties)
455                
456                case Type<MetadataViews.NFTCollectionDisplay>():
457                    return MetadataViews.NFTCollectionDisplay(
458                        name: "Gamisodes",
459                        description: "Gamisodes Contract!",
460                        externalURL: MetadataViews.ExternalURL("https://gamisodes.com"),
461                        squareImage: MetadataViews.Media(
462                            file: MetadataViews.HTTPFile(
463                                url: "https://mint-store-metadata.s3.us-east-2.amazonaws.com/gamisodes/Gamisodes+1+x+1.jpg"
464                            ),
465                            mediaType: "image/jpg"
466                        ),
467                        bannerImage: MetadataViews.Media(
468                            file: MetadataViews.HTTPFile(
469                                url: "https://mint-store-metadata.s3.us-east-2.amazonaws.com/gamisodes/Gamisodes+2+x+1.png"
470                            ),
471                            mediaType: "image/png"
472                        ),
473                        socials: {}
474                    )
475                
476                case Type<MetadataViews.NFTCollectionData>():
477                    return  MetadataViews.NFTCollectionData(
478                        storagePath: Gamisodes.CollectionStoragePath,
479                        publicPath: Gamisodes.CollectionPublicPath,
480                        publicCollection: Type<&Gamisodes.Collection>(),
481                        publicLinkedType: Type<&Gamisodes.Collection>(),
482                        createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
483                            return <-Gamisodes.createEmptyCollection(nftType: Type<@Gamisodes.NFT>())
484                        })
485                    )
486
487
488                case Type<MetadataViews.Traits>():
489                    // exclude essential metadata to keep unique traits
490                    let excludedTraits = ["name", "description", "externalUrl", "squareImage", "squareImageType", "bannerImage", "bannerImageType", "thumbnail", "thumbnailType"]
491
492                    let edition = EditionData(editionID: self.data.editionID);
493                    let metadata = edition.metadata;
494
495                    metadata.insert(key: "editionID", edition.editionID.toString());
496                    metadata.insert(key: "merchantID", edition.merchantID.toString());
497
498                    let traitsView = MetadataViews.dictToTraits(dict: metadata, excludedNames: excludedTraits)
499                    
500                    return traitsView
501
502
503                case Type<MetadataViews.Serial>():
504                    return MetadataViews.Serial(
505                        UInt64(self.data.editionNumber)
506                    )
507                
508
509            }
510
511            return nil
512        }
513
514
515        // If the Item is destroyed, emit an event to indicate 
516        // to outside ovbservers that it has been destroyed
517        // destroy() {
518        //     emit ItemDestroyed(id: self.id)
519        // }
520
521    }
522
523    access(all) view fun getContractViews(resourceType: Type?): [Type] {
524        return []
525    }
526
527     access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
528 
529        return nil
530     }
531
532    // Admin is a special authorization resource that 
533    // allows the owner to perform important functions to modify the 
534    // various aspects of the Editions and Items
535    //
536    access(all) resource Admin {
537
538        access(all) let id: UInt32
539        // createEdition creates a new Edition struct 
540        // and stores it in the Editions dictionary in the contract
541        //
542
543        init(id: UInt32) {
544            self.id = id
545        }
546
547        // createEdition creates a new Edition resource and stores it
548        // in the editions mapping in the contract
549        //
550        // Parameters: 
551        //  merchantID: The ID of the merchant
552        //  metadata: the associated data
553        //  name: The name of the Edition
554        //  printingLimit: We can only mint this quantity of NFTs. If printingLimit is nil there is no limit (theoretically UInt32.max)
555        //
556        access(all) fun createEdition(merchantID: UInt32, metadata: {String: String}, name: String, printingLimit: UInt32?) {
557            // Create the new Edition
558            var newEdition <- create Edition(merchantID:merchantID, metadata: metadata, name: name, printingLimit:printingLimit)
559            let newID = newEdition.editionID
560
561            // Store it in the contract storage
562            Gamisodes.editions[newID] <-! newEdition
563
564        }
565
566
567        // borrowEdition returns a reference to an edition in the Gamisodes
568        // contract so that the admin can call methods on it
569        //
570        // Parameters: editionID: The ID of the Edition that you want to
571        // get a reference to
572        //
573        // Returns: A reference to the Edition with all of the fields
574        // and methods exposed
575        //
576        access(all) fun borrowEdition(editionID: UInt32): &Edition {
577            pre {
578                Gamisodes.editions[editionID] != nil: "Cannot borrow Edition: it does not exist"
579            }
580            
581            // Get a reference to the Edition and return it
582            return (&Gamisodes.editions[editionID] as &Edition?)!
583        }
584
585        // updateEditionMetadata returns a reference to an edition in the Gamisodes
586        // contract so that the admin can call methods on it
587        //
588        // Parameters: 
589        // editionID: The ID of the Edition that you want to update
590        //
591        // updates: a dictionary of key - values that is requested to be appended
592        //
593        // suffix: If the metadata already contains an attribute with a given key, this value should still be kept 
594        // for posteriority. Therefore, the old value to be replaced will be stored in a metadata entry with key = key+suffix. 
595        // This can offer some reassurance to the NFT owner that the metadata will never disappear.
596        // 
597        // Returns: the EditionID
598        //
599        access(all) fun updateEditionMetadata(editionID: UInt32, updates: {String:String}, suffix: String): UInt32 {
600            pre {
601                Gamisodes.editions[editionID] != nil: "Cannot borrow Edition: it does not exist"
602            }
603 
604            let editionRef = &Gamisodes.editions[editionID] as &Edition?
605            editionRef!.updateMetadata(updates: updates, suffix: suffix)
606            
607            // Return the EditionID and return it
608            return editionID
609        }
610
611
612        // set default royalties
613        access(all) fun setDefaultRoyaltyByName(name: String, royalty: MetadataViews.Royalty) {
614            Gamisodes.defaultRoyalties[name] = royalty;
615            // verify total
616            let totalCut = Gamisodes.getDefaultRoyaltyTotalRate()
617            assert(totalCut <= 1.0, message: "Sum of cutInfos multipliers should not be greater than 1.0")
618            emit DefaultRoyaltiesUpdated(name: name, cut: royalty.cut)
619        }
620
621        access(all) fun removeDefaultRoyaltyByName(name: String) {
622
623            if !Gamisodes.defaultRoyalties.containsKey(name) {
624
625                var errorMsg = "Default Royalty with name [" 
626                errorMsg = errorMsg.concat(name).concat("] does not exist")
627                panic(errorMsg)
628            }
629
630            Gamisodes.defaultRoyalties.remove(key: name);
631            emit DefaultRoyaltyRemoved(name: name)
632        }
633
634        // set royalties for edition
635        access(all) fun setEditionRoyaltyByName( editionID: UInt32, name: String, royalty: MetadataViews.Royalty) {
636            
637            if !Gamisodes.royaltiesForSpecificEdition.containsKey(editionID) {
638                Gamisodes.royaltiesForSpecificEdition.insert(key:editionID, {})
639            }
640            //let royaltiesForSpecificEdition = Gamisodes.royaltiesForSpecificEdition[editionID]!
641            //royaltiesForSpecificEdition.insert(key: name, royalty);
642            Gamisodes.royaltiesForSpecificEdition[editionID]!.insert(key: name, royalty);
643             let totalCut = Gamisodes.getEditionRoyaltyTotalRate(editionID:editionID)
644            assert(totalCut <= 1.0, message: "Sum of cutInfos multipliers should not be greater than 1.0")
645
646            emit RoyaltiesForEditionUpdated(editionID: editionID, name: name, cut: royalty.cut)
647        }
648
649        // remove royalty for edition
650        access(all) fun removeEditionRoyaltyByName( editionID: UInt32, name: String) {
651            
652            if !Gamisodes.royaltiesForSpecificEdition.containsKey(editionID) {
653                var errorMsg = "Royalty specific to editionID" 
654                errorMsg = errorMsg.concat(editionID.toString()).concat(" does not exist")
655                panic(errorMsg)
656            }
657            let royaltiesForSpecificEdition = Gamisodes.royaltiesForSpecificEdition[editionID]!
658            if !royaltiesForSpecificEdition.containsKey(name) {
659                var errorMsg = "Royalty specific to editionID" 
660                errorMsg = errorMsg.concat(editionID.toString()).concat(" with the name[").concat(name).concat("] does not exist")
661                panic(errorMsg)
662            }
663            Gamisodes.royaltiesForSpecificEdition[editionID]!.remove(key: name);
664            emit RoyaltiesForEditionRemoved(editionID: editionID, name: name)
665        }
666
667       access(all) fun revertRoyaltiesForEditionToDefault(editionID: UInt32){
668            if !Gamisodes.royaltiesForSpecificEdition.containsKey(editionID){
669                var errorMsg = "Royalty for editionID "
670                errorMsg = errorMsg.concat(editionID.toString()).concat("  does not exist")
671                panic(errorMsg)
672            } 
673                
674
675            Gamisodes.royaltiesForSpecificEdition.remove(key: editionID)
676            emit RevertRoyaltiesForEditionToDefault(editionID: editionID)
677       }
678
679
680
681
682        // createNewAdmin creates a new Admin resource
683        //
684        access(all) fun createNewAdmin(): @Admin {
685            
686
687            let newID = Gamisodes.nextAdminID
688             // Increment the ID so that it isn't used again
689            Gamisodes.nextAdminID = Gamisodes.nextAdminID + (1 as UInt32)
690
691            return <-create Admin(id: newID)
692        }
693
694    }
695
696
697// This is the interface that users can cast their Gamez Collection as
698    // to allow others to deposit Gamezs into their Collection. It also allows for reading
699    // the IDs of Gamezs in the Collection.
700    access(all) resource interface GamisodesCollectionPublic {
701        // access(all) fun deposit(token: @{NonFungibleToken.NFT})
702        // access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection})
703        // access(all) fun getIDs(): [UInt64]
704        // // access(all) fun borrowNFT(id: UInt64): &{NonFungibleToken.NFT}
705        // access(all) fun borrowGamisodes(id: UInt64): &Gamisodes.NFT? {
706        //     // If the result isn't nil, the id of the returned reference
707        //     // should be the same as the argument to the function
708        //     post {
709        //         (result == nil) || (result?.id == id): 
710        //             "Cannot borrow Gamisodes reference: The ID of the returned reference is incorrect"
711        //     }
712        // }
713        
714    }
715
716
717
718    // Collection is a resource that every user who owns NFTs 
719    // will store in their account to manage their NFTS
720    //
721    access(all) resource Collection:  NonFungibleToken.Collection, GamisodesCollectionPublic { 
722        // Dictionary of Gamisodes conforming tokens
723        // NFT is a resource type with a UInt64 ID field
724        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
725        // access(all) var ownedNFTs: @{UInt64: Gamisodes.NFT}
726
727        init() {
728            self.ownedNFTs <- {}
729        }
730
731
732
733
734        // withdraw removes a Gamisodes from the Collection and moves it to the caller
735        //
736        // Parameters: withdrawID: The ID of the NFT 
737        // that is to be removed from the Collection
738        //
739        // returns: @NonFungibleToken.NFT the token that was withdrawn
740        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
741
742            // Remove the nft from the Collection
743            let token <- self.ownedNFTs.remove(key: withdrawID) 
744                ?? panic("Cannot withdraw: Gamisodes does not exist in the collection")
745
746            emit Withdraw(id: token.id, from: self.owner?.address)
747            
748            // Return the withdrawn token
749            return <-token
750        }
751
752        // batchWithdraw withdraws multiple tokens and returns them as a Collection
753        //
754        // Parameters: ids: An array of IDs to withdraw
755        //
756        // Returns: @NonFungibleToken.Collection: A collection that contains
757        //                                        the withdrawn Gamisodes items
758        //
759        access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
760            // Create a new empty Collection
761            var batchCollection <- create Collection()
762            
763            // Iterate through the ids and withdraw them from the Collection
764            for id in ids {
765
766                let token <-self.withdraw(withdrawID: id)
767
768                batchCollection.deposit(token: <-token)
769            }
770            
771            // Return the withdrawn tokens
772            return <-batchCollection
773        }
774
775                /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
776        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
777            let supportedTypes: {Type: Bool} = {}
778            supportedTypes[Type<@Gamisodes.NFT>()] = true
779            return supportedTypes
780        }
781
782        /// Returns whether or not the given type is accepted by the collection
783        /// A collection that can accept any type should just return true by default
784        access(all) view fun isSupportedNFTType(type: Type): Bool {
785            return type == Type<@Gamisodes.NFT>()
786        }
787
788        /// Gets the amount of NFTs stored in the collection
789        access(all) view fun getLength(): Int {
790            return self.ownedNFTs.length
791        }
792
793        // deposit takes a Gamisodes and adds it to the Collections dictionary
794        //
795        // Paramters: token: the NFT to be deposited in the collection
796        //
797        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
798            
799            // Cast the deposited token as a Gamisodes NFT to make sure
800            // it is the correct type
801            let token <- token as! @Gamisodes.NFT
802
803            // Get the token's ID
804            let id = token.id
805
806            // Add the new token to the dictionary
807            let oldToken <- self.ownedNFTs[id] <- token
808
809            // Only emit a deposit event if the Collection 
810            // is in an account's storage
811            if self.owner?.address != nil {
812                emit Deposit(id: id, to: self.owner?.address)
813            }
814
815            // Destroy the empty old token that was "removed"
816            destroy oldToken
817        }
818
819        // batchDeposit takes a Collection object as an argument
820        // and deposits each contained NFT into this Collection
821        access(all) fun batchDeposit(tokens: @{NonFungibleToken.Collection}) {
822
823            // Get an array of the IDs to be deposited
824            let keys = tokens.getIDs()
825
826            // Iterate through the keys in the collection and deposit each one
827            for key in keys {
828                self.deposit(token: <-tokens.withdraw(withdrawID: key))
829            }
830
831            // Destroy the empty Collection
832            destroy tokens
833        }
834
835        // getIDs returns an array of the IDs that are in the Collection
836        access(all) view fun getIDs(): [UInt64] {
837            return self.ownedNFTs.keys
838        }
839
840        // borrowNFT Returns a borrowed reference to a Gamisodes in the Collection
841        // so that the caller can read its ID
842        //
843        // Parameters: id: The ID of the NFT to get the reference for
844        //
845        // Returns: A reference to the NFT
846        //
847        // Note: This only allows the caller to read the ID of the NFT,
848        // not any Gamisodes specific data. Please use borrowGamisodes to 
849        // read Gamisodes data.
850        //
851        // access(all) fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
852        //     return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
853        // }
854
855        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
856            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
857        }
858
859
860
861        // borrowGamisodes returns a borrowed reference to a Gamisodes
862        // so that the caller can read data and call methods from it.
863        // They can use this to read its editionID, editionNumber,
864        // or any edition data associated with it by
865        // getting the editionID and reading those fields from
866        // the smart contract.
867        //
868        // Parameters: id: The ID of the NFT to get the reference for
869        //
870        // Returns: A reference to the NFT
871        // access(all) fun borrowGamisodes(id: UInt64): &Gamisodes.NFT? {
872
873        //     return (&self.ownedNFTs[id] as &Gamisodes.NFT?)
874        //     // if self.ownedNFTs[id] != nil {
875        //     //     let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
876        //     //     return ref as! &Gamisodes.NFT
877        //     // } else {
878        //     //     return nil
879        //     // }
880        // }
881
882        // access(all) fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
883        //     let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
884        //     let GamisodesNFT = nft as! &Gamisodes.NFT
885        //     return GamisodesNFT as &AnyResource{MetadataViews.Resolver}
886        // }
887
888        /// Borrow the view resolver for the specified NFT ID
889        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
890            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
891                return nft as &{ViewResolver.Resolver}
892            }
893            return nil
894        }
895
896        /// createEmptyCollection creates an empty Collection of the same type
897        /// and returns it to the caller
898        /// @return A an empty collection of the same type
899        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
900            return <-Gamisodes.createEmptyCollection(nftType: Type<@Gamisodes.NFT>())
901        }
902
903        // If a transaction destroys the Collection object,
904        // All the NFTs contained within are also destroyed
905        //
906        // destroy() {
907        //     destroy self.ownedNFTs
908        // }
909    }
910
911
912
913    // -----------------------------------------------------------------------
914    // Gamisodes contract-level function definitions
915    // -----------------------------------------------------------------------
916
917    // createEmptyCollection creates a new, empty Collection object so that
918    // a user can store it in their account storage.
919    // Once they have a Collection in their storage, they are able to receive
920    // Gamisodess in transactions.
921    //
922    // access(all) fun createEmptyCollection(): @NonFungibleToken.Collection {
923    //     return <-create Gamisodes.Collection()
924    // }
925    /// createEmptyCollection creates an empty Collection for the specified NFT type
926    /// and returns it to the caller so that they can own NFTs
927    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
928        return <- create Gamisodes.Collection()
929    }
930
931    access(all) fun createEmptyGamisodesCollection(): @Gamisodes.Collection {
932        return <-create Gamisodes.Collection()
933    }
934
935    // getDefaultRoyalties returns the default royalties
936    access(all) fun getDefaultRoyalties(): {String: MetadataViews.Royalty} {
937        return self.defaultRoyalties
938    }
939
940    // getDefaultRoyalties returns the default royalties
941    access(all) fun getDefaultRoyaltyNames(): [String] {
942        return self.defaultRoyalties.keys
943    }
944
945    // getDefaultRoyalties returns the default royalties
946    access(all) fun getDefaultRoyaltyByName(name: String): MetadataViews.Royalty? {
947        return self.defaultRoyalties[name]
948    }
949
950    // getDefaultRoyalties returns the default royalties total rate
951    access(all) fun getDefaultRoyaltyTotalRate(): UFix64 {
952        var cut = 0.0
953        for name in self.defaultRoyalties.keys {
954            cut=cut+self.defaultRoyalties[name]!.cut
955        }
956
957        return cut;
958    }
959
960
961
962
963
964    // getRoyaltiesForEdition returns the royalties set for a specific edition, that overrides the default
965    access(all) fun getEditionRoyalties(editionID: UInt32): {String: MetadataViews.Royalty} {
966        return self.royaltiesForSpecificEdition[editionID] ?? self.defaultRoyalties
967    }
968
969    // getRoyaltiesForEdition returns the royalties set for a specific edition, that overrides the default
970    access(all) fun getEditionRoyaltyNames(editionID: UInt32): [String] {
971        let royalties = Gamisodes.getEditionRoyalties(editionID: editionID)
972        return royalties.keys
973    }
974
975     // getRoyaltiesForEdition returns the royalties set for a specific edition, that overrides the default
976    access(all) fun getEditionRoyaltyByName(editionID: UInt32, name: String): MetadataViews.Royalty {
977        let royaltiesForSpecificEdition = Gamisodes.getEditionRoyalties(editionID: editionID)
978        return royaltiesForSpecificEdition[name]!
979    }
980
981    // getDefaultRoyalties returns the default royalties total rate
982    access(all) fun getEditionRoyaltyTotalRate(editionID: UInt32): UFix64 {
983        let royalties = Gamisodes.getEditionRoyalties(editionID: editionID)
984        var cut = 0.0
985        for name in royalties.keys {
986            cut=cut+royalties[name]!.cut
987        }
988
989        return cut;
990    }
991
992
993    // -----------------------------------------------------------------------
994    // initialization function
995    // -----------------------------------------------------------------------
996    //
997    init() {
998        // Initialize contract fields
999        self.editions <- {}
1000        self.nextEditionID = 1
1001        self.totalSupply = 0
1002        self.defaultRoyalties = {}
1003        self.royaltiesForSpecificEdition = {}
1004
1005
1006        self.CollectionStoragePath = /storage/GamisodesCollection
1007        self.CollectionPublicPath = /public/GamisodesCollection
1008        self.AdminStoragePath = /storage/GamisodesItemAdmin
1009
1010        // Put a new Collection in storage
1011        self.account.storage.save<@Collection>(<- create Collection(), to: self.CollectionStoragePath)
1012
1013        // Create a public capability for the Collection
1014        // self.account.link<&{GamisodesCollectionPublic}>(self.CollectionPublicPath, target: self.CollectionStoragePath)
1015        let collectionCap = self.account.capabilities.storage.issue<&Gamisodes.Collection>(self.CollectionStoragePath)
1016        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
1017
1018        // Put the admin ressource in storage
1019        self.account.storage.save<@Admin>(<- create Admin(id: 1), to: self.AdminStoragePath)
1020        self.nextAdminID = 2
1021
1022        emit ContractInitialized()
1023    }
1024
1025
1026}
1027    
1028