Smart Contract

RTLStoreItem

A.9d1d0d0c82bf1c59.RTLStoreItem

Deployed

1w ago
Feb 16, 2026, 12:28:19 AM UTC

Dependents

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