Smart Contract
Gamisodes
A.20187093790b9aef.Gamisodes
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