Smart Contract

LOSTiNTurismo

A.20187093790b9aef.LOSTiNTurismo

Deployed

16h ago
Feb 28, 2026, 02:29:49 AM UTC

Dependents

0 imports
1/*
2    Description: 
3
4    authors: 
5    
6    This smart contract contains the core functionality for Mint PFP NFT. 
7    
8    MINT is a platform where teams can create a fully branded environment to sell NFTs and launch branded marketplaces. 
9    This will give fans a fully immersive experience as they interact with drops, buy and sell in the marketplace, and 
10    deepen their relationships with the brand.
11
12
13    Enjoy!
14*/
15
16// import NonFungibleToken from "./NonFungibleToken.cdc"
17// import MetadataViews from "./MetadataViews.cdc"
18
19// for tests
20// import NonFungibleToken from "0xNonFungibleToken"
21// import MetadataViews from "0xMetadataViews"
22// import FungibleToken from "0xFungibleToken"
23
24// for testnet
25// import NonFungibleToken from 0x631e88ae7f1d7c20
26// import MetadataViews from 0x631e88ae7f1d7c20
27
28// for mainnet
29import NonFungibleToken from 0x1d7e57aa55817448
30import MetadataViews from 0x1d7e57aa55817448
31
32
33pub contract LOSTiNTurismo: NonFungibleToken {
34
35    // -----------------------------------------------------------------------
36    // MintPFPs contract Events
37    // -----------------------------------------------------------------------
38
39    // Emitted when the MintPFPs contract is created
40    pub event ContractInitialized()
41
42
43    // Emitted when a new item was minted
44    pub event ItemMinted(itemID:UInt64, merchantID: UInt32, name: String)
45
46    // Item related events 
47    //
48    // Emitted when an Item is withdrawn from a Collection
49    pub event Withdraw(id: UInt64, from: Address?)
50    // Emitted when an Item is deposited into a Collection
51    pub event Deposit(id: UInt64, to: Address?)
52    // Emitted when an Item is withdrawn from a Collection
53    pub event ItemMutated(id: UInt64, mutation: ItemData)
54    // Emitted when adding a default royalty recipient
55    pub event DefaultRoyaltyAdded(name: String, rate: UFix64)
56    // Emitted when removing a default royalty recipient
57    pub event DefaultRoyaltyRemoved(name: String)
58    // Emitted when an existing Royalty rate is changed
59    pub event DefaultRoyaltyRateChanged(name: String, previousRate: UFix64, rate: UFix64)
60    // Emitted when adding a royalty for a specific NFT
61    pub event RoyaltyForPFPAdded(tokenID: UInt64, name: String, rate: UFix64)
62    // Emitted when an existing Royalty rate is changed
63    pub event RoyaltyForPFPChanged(tokenID: UInt64, name: String, previousRate: UFix64, rate: UFix64)
64    // Emitted when an existing Royalty rate is changed
65    pub event RoyaltyForPFPRemoved(tokenID: UInt64, name: String)
66    // Emitted when reverting the Royalty rate of a given NFT back to default settings
67    pub event RoyaltyForPFPRevertedToDefault(tokenID: UInt64)
68    // Emitted when an Item is destroyed
69    pub event ItemDestroyed(id: UInt64)
70
71
72    // Named paths
73    //
74    pub let CollectionStoragePath: StoragePath
75    pub let CollectionPublicPath: PublicPath
76    pub let AdminStoragePath: StoragePath
77    pub let MutatorStoragePath: StoragePath
78
79
80    // -----------------------------------------------------------------------
81    // MintPFPs contract-level fields.
82    // These contain actual values that are stored in the smart contract.
83    // -----------------------------------------------------------------------
84
85
86
87    // The ID that is used to create Admins. 
88    // Every Admins should have a unique identifier.
89    pub var nextAdminID: UInt32
90
91    // The ID that is used to create Mutators. 
92    // Every Mutators should have a unique identifier.
93    pub var nextMutatorID: UInt32
94
95    // If ever a mutator goes rouge, we would like to be able to have the option of
96    // locking the mutator's ability to mutate NFTs. Additionally, we would like
97    // to be able to unlock them too.
98    pub var lockedMutators: {UInt32: Bool}
99
100    // The merchant ID (see MintPFPs)
101    pub var merchantID: UInt32
102
103
104    // The total number of MintPFPs NFTs that have been created
105    // Because NFTs can be destroyed, it doesn't necessarily mean that this
106    // reflects the total number of NFTs in existence, just the number that
107    // have been minted to date. Also used as global nft IDs for minting.
108    pub var totalSupply: UInt64
109
110
111    // Mutations are upgrades or modifications of the NFTs' metadata.
112    // These will be store at the contract level, allowing dapps administrators to 
113    // mutate NFTs even after they have been transferred to other wallets.
114    // It also ensures that the original metadata of the NFT will never be deleted
115    // offering some protection to the holder.
116    pub var mutations: {UInt64: ItemData}
117
118
119
120    // the default royalties will be applied to all PFPs unless a specific royalty 
121    // is set for a given PFP
122    pub var defaultRoyalties: {String: MetadataViews.Royalty}
123
124    // If a specific NFT requires their own royalties, 
125    // the default royalties can be overwritten in this dictionary.
126    pub var royaltiesForSpecificPFP: {UInt64: {String: MetadataViews.Royalty}}
127
128    pub var ExternalURL: MetadataViews.ExternalURL
129
130    pub var Socials: {String: MetadataViews.ExternalURL}
131
132    pub var Description: String
133
134    pub var SquareImage: MetadataViews.Media
135
136    pub var BannerImage: MetadataViews.Media
137
138
139
140
141
142    // -----------------------------------------------------------------------
143    // MintPFPs contract-level Composite Type definitions
144    // -----------------------------------------------------------------------
145    // These are just *definitions* for Types that this contract
146    // and other accounts can use. These definitions do not contain
147    // actual stored values, but an instance (or object) of one of these Types
148    // can be created by this contract that contains stored values.
149    // -----------------------------------------------------------------------
150   
151    // The struct representing an NFT Item data
152    pub struct ItemData {
153
154
155        // The ID of the merchant 
156        pub let merchantID: UInt32
157
158        // the name
159        pub let name: String
160
161        // the description
162        pub let description: String
163
164        // The thumbnail
165        pub let thumbnail: String
166
167        // the thumbnail cid (if thumbnailHosting is IPFS )
168        pub let thumbnailCID: String
169
170        // the thumbnail path (if thumbnailHosting is IPFS )
171        pub let thumbnailPathIPFS: String?
172
173        // The mimetype of the thumbnail
174        pub let thumbnailMimeType: String
175
176        // The method of hosting the thumbnail (IPFS | HTTPFile)
177        pub let thumbnailHosting: String
178
179        // the media file
180        pub let mediaURL: String
181
182        // the media cid (if mediaHosting is IPFS )
183        pub let mediaCID: String
184
185        // the media path (if mediaHosting is IPFS )
186        pub let mediaPathIPFS: String?
187
188        // the mimetype
189        pub let mimetype: String
190
191        // the method of hosting the media file (IPFS | HTTPFile)
192        pub let mediaHosting: String
193
194        // the attributes
195        pub let attributes: {String: String}
196
197        // rarity
198        pub let rarity: String
199        
200
201
202
203        init(name: String, description: String, thumbnail: String, thumbnailCID: String, thumbnailPathIPFS: String?, thumbnailMimeType: String, thumbnailHosting: String, mediaURL: String, mediaCID: String, mediaPathIPFS: String?, mimetype: String, mediaHosting: String, attributes: {String: String}, rarity: String) {
204            self.merchantID = LOSTiNTurismo.merchantID
205            self.name = name
206            self.description = description
207            self.thumbnail = thumbnail
208            self.thumbnailMimeType = thumbnailMimeType
209            self.thumbnailCID = thumbnailCID
210            self.thumbnailPathIPFS = thumbnailPathIPFS
211            self.thumbnailHosting = thumbnailHosting
212            self.mediaURL = mediaURL
213            self.mediaCID = mediaCID
214            self.mediaPathIPFS = mediaPathIPFS
215            self.mediaHosting = mediaHosting
216            self.mimetype = mimetype
217            self.attributes = attributes
218            self.rarity = rarity
219        }
220
221    }
222
223    // The resource that represents the Item NFTs
224    //
225    pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
226
227        // Global unique item ID
228        pub let id: UInt64
229
230        // Struct of MintPFPs metadata
231        pub let data: ItemData
232
233
234        init(name: String, description: String, thumbnail: String, thumbnailCID: String, thumbnailPathIPFS: String?, thumbnailMimeType: String, thumbnailHosting: String, mediaURL: String, mediaCID: String, mediaPathIPFS: String?, mimetype: String, mediaHosting: String, attributes: {String: String}, rarity: String) {
235            
236            pre{
237
238            }
239            // Increment the global Item IDs
240            LOSTiNTurismo.totalSupply = LOSTiNTurismo.totalSupply + (1 as UInt64)
241
242            self.id = LOSTiNTurismo.totalSupply
243
244             // Set the metadata struct
245            self.data = ItemData(name: name, description: description, thumbnail: thumbnail, thumbnailCID: thumbnailCID, thumbnailPathIPFS: thumbnailPathIPFS, thumbnailMimeType: thumbnailMimeType, thumbnailHosting: thumbnailHosting, mediaURL: mediaURL, mediaCID: mediaCID, mediaPathIPFS: mediaPathIPFS, mimetype: mimetype, mediaHosting: mediaHosting, attributes: attributes, rarity: rarity)
246
247            emit ItemMinted(itemID: self.id, merchantID:  LOSTiNTurismo.merchantID, name: name)
248            
249        }
250
251        pub fun getSerialNumber(): UInt64 {
252            return self.id;
253        }
254
255        pub fun getOriginalData(): ItemData {
256            return self.data;
257        }
258
259        pub fun getMutation(): ItemData? {
260
261            return LOSTiNTurismo.mutations[self.id];
262        }
263
264        pub fun getData(): ItemData {
265            return self.getMutation() ?? self.getOriginalData(); 
266        }
267
268        pub fun getViews(): [Type] {
269            return [
270                Type<MetadataViews.Display>(),
271                Type<MetadataViews.Royalties>(),
272                Type<MetadataViews.Editions>(),
273                Type<MetadataViews.ExternalURL>(),
274                Type<MetadataViews.NFTCollectionData>(),
275                Type<MetadataViews.NFTCollectionDisplay>(),
276                Type<MetadataViews.Serial>(),
277                Type<MetadataViews.Traits>()
278            ]
279        }
280
281        pub fun resolveView(_ view: Type): AnyStruct? {
282            switch view {
283                case Type<MetadataViews.Display>():
284
285                    var thumbnail: AnyStruct{MetadataViews.File} =  MetadataViews.HTTPFile(
286                            url: self.data.thumbnail
287                        )
288                    if self.data.thumbnailHosting == "IPFS" {
289                        thumbnail =  MetadataViews.IPFSFile(
290                            cid: self.data.thumbnailCID, 
291                            path: self.data.thumbnailPathIPFS
292                        )
293                    }
294                    return MetadataViews.Display(
295                        name: self.data.name,
296                        description: self.data.description,
297                        thumbnail: thumbnail
298                    )
299
300                case Type<MetadataViews.Editions>():
301                    let editionInfo = MetadataViews.Edition(name: self.data.name, number: UInt64(1), max:1)
302                    let editionList: [MetadataViews.Edition] = [editionInfo]
303                    return MetadataViews.Editions(
304                        editionList
305                    )
306
307                case Type<MetadataViews.Royalties>():
308                    let royaltiesDictionary = LOSTiNTurismo.royaltiesForSpecificPFP[self.id] ?? LOSTiNTurismo.defaultRoyalties 
309                    var royalties: [MetadataViews.Royalty] = []
310                    for royaltyName in royaltiesDictionary.keys {
311                        royalties.append(royaltiesDictionary[royaltyName]!)
312                    }
313                  return MetadataViews.Royalties(royalties)
314
315                case Type<MetadataViews.ExternalURL>():
316                    return LOSTiNTurismo.ExternalURL
317                
318                case Type<MetadataViews.NFTCollectionData>():
319                    return  MetadataViews.NFTCollectionData(
320                        storagePath: LOSTiNTurismo.CollectionStoragePath,
321                        publicPath: LOSTiNTurismo.CollectionPublicPath,
322                        providerPath: /private/LOSTiNTurismoCollection,
323                        publicCollection: Type<&LOSTiNTurismo.Collection{LOSTiNTurismo.LOSTiNTurismoCollectionPublic}>(),
324                        publicLinkedType: Type<&LOSTiNTurismo.Collection{LOSTiNTurismoCollectionPublic, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(),
325                        providerLinkedType: Type<&LOSTiNTurismo.Collection{LOSTiNTurismoCollectionPublic,NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(),
326                        createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
327                            return <-LOSTiNTurismo.createEmptyCollection()
328                        })
329                    )
330
331                case Type<MetadataViews.NFTCollectionDisplay>():
332                    let data = self.getData();
333                    let media = MetadataViews.Media(
334                        file: MetadataViews.HTTPFile(
335                            url: data.thumbnail
336                        ),
337                        mediaType: data.thumbnailMimeType
338                    )
339
340                    return MetadataViews.NFTCollectionDisplay(
341                        name: "LOSTiNTurismo",
342                        description: LOSTiNTurismo.Description,
343                        externalURL: LOSTiNTurismo.ExternalURL,
344                        squareImage: LOSTiNTurismo.SquareImage,
345                        bannerImage: LOSTiNTurismo.BannerImage,
346                        socials: LOSTiNTurismo.Socials
347                    )
348
349                case Type<MetadataViews.Traits>():
350                    // exclude mintedTime and foo to show other uses of Traits
351                    let excludedTraits = ["name", "description", "thumbnail", "externalUrl"]
352                    let data = self.getData();
353                    let dict = data.attributes;
354                    let traitsView = MetadataViews.dictToTraits(dict: dict, excludedNames: excludedTraits)
355                    
356                    return traitsView
357
358            }
359            return nil
360        }
361
362
363        // If the Item is destroyed, emit an event to indicate 
364        // to outside ovbservers that it has been destroyed
365        destroy() {
366            emit ItemDestroyed(id: self.id)
367        }
368
369    }
370
371    // Mutator is an authorization resource that allows for the mutations of NFTs 
372    pub resource Mutator {
373
374        pub let id: UInt32
375       
376        init(id: UInt32) {
377            self.id = id
378        }
379
380        // Mutator role should only be able to mutate a NFT
381        pub fun mutatePFP(tokenID: UInt64, name: String, description: String, thumbnail: String, thumbnailCID: String, thumbnailPathIPFS: String?, thumbnailMimeType: String, thumbnailHosting: String, mediaURL: String, mediaCID: String, mediaPathIPFS: String?, mimetype: String, mediaHosting: String, attributes: {String: String}, rarity: String){
382
383           pre{
384                tokenID <= LOSTiNTurismo.totalSupply: "the tokenID does not exist"
385           }
386                if (LOSTiNTurismo.lockedMutators[self.id] != true) {
387                    let mutation = ItemData(name: name, description: description, thumbnail: thumbnail, thumbnailCID: thumbnailCID, thumbnailPathIPFS: thumbnailPathIPFS, thumbnailMimeType: thumbnailMimeType, thumbnailHosting: thumbnailHosting, mediaURL: mediaURL, mediaCID: mediaCID, mediaPathIPFS: mediaPathIPFS, mimetype: mimetype, mediaHosting: mediaHosting, attributes: attributes, rarity: rarity)
388
389                    LOSTiNTurismo.mutations[tokenID] = mutation
390                    emit ItemMutated(id: tokenID, mutation: mutation)
391                }
392                else {
393                    log("Cannot let mutator mutate")
394                }
395        }
396    }
397
398    // Admin is a special authorization resource that 
399    // allows the owner to perform important functions to modify the 
400    // various aspects of the Editions and Items
401    //
402    pub resource Admin {
403
404        pub let id: UInt32
405       
406
407        init(id: UInt32) {
408            self.id = id
409        }
410
411        pub fun setExternalURL(url: String) {
412            LOSTiNTurismo.ExternalURL = MetadataViews.ExternalURL(url);
413        }
414
415        pub fun addSocial(key: String, url: String): {String: MetadataViews.ExternalURL} {
416            LOSTiNTurismo.Socials.insert(key: key, MetadataViews.ExternalURL(url));
417            return LOSTiNTurismo.getSocials();
418        }
419
420        pub fun removeSocial(key: String): {String: MetadataViews.ExternalURL} {
421            LOSTiNTurismo.Socials.remove(key: key);
422            return LOSTiNTurismo.getSocials();
423        }
424
425        pub fun setDescription(description: String) {
426            LOSTiNTurismo.Description = description;
427        }
428
429        pub fun setSquareImage(url: String, mediaType: String) {
430            LOSTiNTurismo.SquareImage = MetadataViews.Media(
431                file: MetadataViews.HTTPFile(
432                    url: url
433                ),
434                mediaType: mediaType
435            );
436        }
437
438        pub fun setBannerImage(url: String, mediaType: String) {
439            LOSTiNTurismo.BannerImage = MetadataViews.Media(
440                file: MetadataViews.HTTPFile(
441                    url: url
442                ),
443                mediaType: mediaType
444            );
445        }
446
447        // createNewAdmin creates a new Admin resource
448        //
449        pub fun createNewAdmin(): @Admin {
450            
451
452            let newID = LOSTiNTurismo.nextAdminID
453             // Increment the ID so that it isn't used again
454            LOSTiNTurismo.nextAdminID = LOSTiNTurismo.nextAdminID + (1 as UInt32)
455
456            return <-create Admin(id: newID)
457        }
458
459        // createNewMutator creates a new Mutator resource
460        pub fun createNewMutator(): @Mutator {
461            
462
463            let newID = LOSTiNTurismo.nextMutatorID
464             // Increment the ID so that it isn't used again
465            LOSTiNTurismo.nextMutatorID = LOSTiNTurismo.nextMutatorID + (1 as UInt32)
466
467            return <-create Mutator(id: newID)
468        }
469
470        // Locks a mutator
471        pub fun lockMutator(id: UInt32): Int{
472            LOSTiNTurismo.lockedMutators.insert(key: id, true);
473            return LOSTiNTurismo.lockedMutators.length;
474        }
475
476        // Unlocks a mutator
477        pub fun unlockMutator(id: UInt32): Int{
478            LOSTiNTurismo.lockedMutators.remove(key: id);
479            return LOSTiNTurismo.lockedMutators.length;
480        }
481
482        pub fun setMerchantID(merchantID: UInt32): UInt32{
483            LOSTiNTurismo.merchantID=merchantID;
484            return LOSTiNTurismo.merchantID;
485        }
486
487
488        pub fun mintPFP(name: String, description: String, thumbnail: String, thumbnailCID: String, thumbnailPathIPFS: String?, thumbnailMimeType: String, thumbnailHosting: String, mediaURL: String, mediaCID: String, mediaPathIPFS: String?, mimetype: String, mediaHosting: String, attributes: {String: String}, rarity: String): @NFT {
489
490             // Mint the new item
491            let newItem: @NFT <- create NFT(name: name, description: description, thumbnail: thumbnail, thumbnailCID: thumbnailCID, thumbnailPathIPFS: thumbnailPathIPFS, thumbnailMimeType: thumbnailMimeType, thumbnailHosting: thumbnailHosting, mediaURL: mediaURL, mediaCID: mediaCID, mediaPathIPFS: mediaPathIPFS, mimetype: mimetype, mediaHosting: mediaHosting, attributes: attributes, rarity: rarity)
492
493
494            return <-newItem
495
496        }
497
498        pub fun mutatePFP(tokenID: UInt64, name: String, description: String, thumbnail: String, thumbnailCID: String, thumbnailPathIPFS: String?, thumbnailMimeType: String, thumbnailHosting: String, mediaURL: String, mediaCID: String, mediaPathIPFS: String?,mimetype: String, mediaHosting: String, attributes: {String: String}, rarity: String){
499
500           pre{
501                tokenID <= LOSTiNTurismo.totalSupply: "the tokenID does not exist"
502           }
503                let mutation = ItemData(name: name, description: description, thumbnail: thumbnail, thumbnailCID: thumbnailCID, thumbnailPathIPFS: thumbnailPathIPFS, thumbnailMimeType: thumbnailMimeType, thumbnailHosting: thumbnailHosting, mediaURL: mediaURL, mediaCID: mediaCID, mediaPathIPFS: mediaPathIPFS, mimetype: mimetype, mediaHosting:mediaHosting, attributes: attributes, rarity: rarity)
504
505                LOSTiNTurismo.mutations[tokenID] = mutation
506                emit ItemMutated(id: tokenID, mutation: mutation)
507        }
508
509
510        // addDefaultRoyalty adds a new default recipient for the cut of the sale
511        //
512        // Parameters: name: the key to store the new royalty
513        //             recipientAddress: the wallet address of the recipient of the cut of the sale
514        //             rate: the percentage of the sale that goes to that recipient
515        //
516        pub fun addDefaultRoyalty(name: String, royalty: MetadataViews.Royalty, rate: UFix64){
517
518            pre {
519                LOSTiNTurismo.defaultRoyalties[name] == nil: "The royalty with that name already exists"
520                rate > 0.0: "Cannot set rate to less than 0%"
521                rate <= 1.0: "Cannot set rate to more than 100%"
522            }
523            LOSTiNTurismo.defaultRoyalties[name] = royalty
524
525            // emit DefaultRoyaltyAdded(name: name, rate: rate)
526
527            
528
529        }
530
531
532        // changeDefaultRoyaltyRate updates a recipient's part of the cut of the sale
533        //
534        // Parameters: name: the key of the recipient to update
535        //             rate: the new percentage of the sale that goes to that recipient
536        //
537        pub fun changeDefaultRoyaltyRate(name: String, rate: UFix64) {
538            pre {
539                LOSTiNTurismo.defaultRoyalties[name] != nil: "The royalty with that name does not exist"
540                rate > 0.0: "Cannot set rate to less than 0%"
541                rate <= 1.0: "Cannot set rate to more than 100%"
542            }
543            let royalty = LOSTiNTurismo.defaultRoyalties[name]!
544            let previousRate = royalty.cut
545            let previousRecipientAddress  = royalty.receiver
546            LOSTiNTurismo.defaultRoyalties[name] = MetadataViews.Royalty(recipientAddress: previousRecipientAddress, cut: UFix64(rate), description: "LOSTiNTurismo Royalties")
547            emit DefaultRoyaltyRateChanged(name: name, previousRate: previousRate, rate: rate)
548        }
549
550        // removeDefaultRoyalty removes a default recipient from the cut of the sale
551        //
552        // Parameters: name: the key to store the royalty to remove
553        pub fun removeDefaultRoyalty(name: String) {
554            pre {
555                LOSTiNTurismo.defaultRoyalties[name] != nil: "The royalty with that name does not exist"
556            }
557            LOSTiNTurismo.defaultRoyalties.remove(key: name)
558            emit DefaultRoyaltyRemoved(name: name)
559        }
560
561
562
563        // addRoyaltyForPFP adds a new recipient for the cut of the sale on a specific PFP
564        //
565        // Parameters: tokenID: the unique ID of the PFP
566        //             name: the key to store the new royalty
567        //             recipientAddress: the wallet address of the recipient of the cut of the sale
568        //             rate: the percentage of the sale that goes to that recipient
569        //
570        pub fun addRoyaltyForPFP(tokenID: UInt64, name: String, royalty: MetadataViews.Royalty, rate: UFix64){
571
572            pre {
573                rate > 0.0: "Cannot set rate to less than 0%"
574                rate <= 1.0: "Cannot set rate to more than 100%"
575            }
576
577            if  LOSTiNTurismo.royaltiesForSpecificPFP.containsKey(tokenID) == false{
578
579                let newEntry: {String: MetadataViews.Royalty}= {}
580                newEntry.insert(key: name, royalty)
581                LOSTiNTurismo.royaltiesForSpecificPFP!.insert(key: tokenID, newEntry)
582                emit RoyaltyForPFPAdded(tokenID: tokenID, name: name, rate: rate)
583                return
584            }
585
586            // the TokenID already has an entry
587
588             if  LOSTiNTurismo.royaltiesForSpecificPFP[tokenID]!.containsKey(name) {
589                 // the entry already exists
590                 panic("The royalty with that name already exists")
591
592             }
593            LOSTiNTurismo.royaltiesForSpecificPFP[tokenID]!.insert(key: name, royalty)
594
595            emit RoyaltyForPFPAdded(tokenID: tokenID, name: name, rate: rate)
596
597            
598
599        }
600
601
602        // changeRoyaltyRateForPFP changes the royalty rate for the sale on a specific PFP
603        //
604        // Parameters: tokenID: the unique ID of the PFP
605        //             name: the key to store the new royalty
606        //             rate: the percentage of the sale that goes to that recipient
607        //
608        pub fun changeRoyaltyRateForPFP(tokenID: UInt64, name: String, rate: UFix64){
609
610            pre {
611                rate > 0.0: "Cannot set rate to less than 0%"
612                rate <= 1.0: "Cannot set rate to more than 100%"
613            }
614
615            let previousRoyalty: MetadataViews.Royalty = LOSTiNTurismo.royaltiesForSpecificPFP[tokenID]![name]!
616
617            let newRoyalty = MetadataViews.Royalty(recipientAddress: previousRoyalty.receiver, cut: UFix64(rate), description: "LOSTiNTurismo Royalties")
618            LOSTiNTurismo.royaltiesForSpecificPFP[tokenID]!.insert(key: name,  newRoyalty);
619
620            emit RoyaltyForPFPChanged(tokenID: tokenID, name: name, previousRate: previousRoyalty.cut, rate: rate)
621
622        }
623
624        // removeRoyaltyForPFP changes the royalty rate for the sale on a specific PFP
625        //
626        // Parameters: tokenID: the unique ID of the PFP
627        //             name: the key to store the royalty to remove
628        //
629        pub fun removeRoyaltyForPFP(tokenID: UInt64, name: String){
630
631            LOSTiNTurismo.royaltiesForSpecificPFP[tokenID]!.remove(key: name);
632            emit RoyaltyForPFPRemoved(tokenID: tokenID, name: name)
633
634        }
635
636
637        // revertRoyaltyForPFPToDefault removes the royalty setttings for the specific PFP
638        // so it uses the default roylaties going forward
639        //
640        // Parameters: tokenID: the unique ID of the PFP
641        //
642        pub fun revertRoyaltyForPFPToDefault(tokenID: UInt64){
643
644            LOSTiNTurismo.royaltiesForSpecificPFP.remove(key: tokenID);
645            emit RoyaltyForPFPRevertedToDefault(tokenID: tokenID)
646
647        }
648
649
650    }
651
652
653
654    // This is the interface that users can cast their MintPFPs Collection as
655    // to allow others to deposit MintPFPs into their Collection. It also allows for reading
656    // the IDs of MintPFPs in the Collection.
657    pub resource interface LOSTiNTurismoCollectionPublic {
658        pub fun deposit(token: @NonFungibleToken.NFT)
659        pub fun batchDeposit(tokens: @NonFungibleToken.Collection)
660        pub fun getIDs(): [UInt64]
661        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
662        pub fun borrowLOSTiNTurismo(id: UInt64): &LOSTiNTurismo.NFT? {
663            // If the result isn't nil, the id of the returned reference
664            // should be the same as the argument to the function
665            post {
666                (result == nil) || (result?.id == id): 
667                    "Cannot borrow PFP reference: The ID of the returned reference is incorrect"
668            }
669        }
670        
671    }
672
673
674
675    // Collection is a resource that every user who owns NFTs 
676    // will store in their account to manage their NFTS
677    //
678    pub resource Collection: LOSTiNTurismoCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection { 
679        // Dictionary of MintPFPs conforming tokens
680        // NFT is a resource type with a UInt64 ID field
681        pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
682
683        init() {
684            self.ownedNFTs <- {}
685        }
686
687
688
689
690        // withdraw removes a MintPFPs from the Collection and moves it to the caller
691        //
692        // Parameters: withdrawID: The ID of the NFT 
693        // that is to be removed from the Collection
694        //
695        // returns: @NonFungibleToken.NFT the token that was withdrawn
696        pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
697
698            // Remove the nft from the Collection
699            let token <- self.ownedNFTs.remove(key: withdrawID) 
700                ?? panic("Cannot withdraw: PFP does not exist in the collection")
701
702            emit Withdraw(id: token.id, from: self.owner?.address)
703            
704            // Return the withdrawn token
705            return <-token
706        }
707
708        // batchWithdraw withdraws multiple tokens and returns them as a Collection
709        //
710        // Parameters: ids: An array of IDs to withdraw
711        //
712        // Returns: @NonFungibleToken.Collection: A collection that contains
713        //                                        the withdrawn MintPFPs items
714        //
715        pub fun batchWithdraw(ids: [UInt64]): @NonFungibleToken.Collection {
716            // Create a new empty Collection
717            var batchCollection <- create Collection()
718            
719            // Iterate through the ids and withdraw them from the Collection
720            for id in ids {
721
722                let token <-self.withdraw(withdrawID: id)
723
724                batchCollection.deposit(token: <-token)
725            }
726            
727            // Return the withdrawn tokens
728            return <-batchCollection
729        }
730
731        // deposit takes a MintPFPs and adds it to the Collections dictionary
732        //
733        // Paramters: token: the NFT to be deposited in the collection
734        //
735        pub fun deposit(token: @NonFungibleToken.NFT) {
736            
737            // Cast the deposited token as a MintPFPs NFT to make sure
738            // it is the correct type
739            let token <- token as! @LOSTiNTurismo.NFT
740
741            // Get the token's ID
742            let id = token.id
743
744            // Add the new token to the dictionary
745            let oldToken <- self.ownedNFTs[id] <- token
746
747            // Only emit a deposit event if the Collection 
748            // is in an account's storage
749            if self.owner?.address != nil {
750                emit Deposit(id: id, to: self.owner?.address)
751            }
752
753            // Destroy the empty old token that was "removed"
754            destroy oldToken
755        }
756
757        // batchDeposit takes a Collection object as an argument
758        // and deposits each contained NFT into this Collection
759        pub fun batchDeposit(tokens: @NonFungibleToken.Collection) {
760
761            // Get an array of the IDs to be deposited
762            let keys = tokens.getIDs()
763
764            // Iterate through the keys in the collection and deposit each one
765            for key in keys {
766                self.deposit(token: <-tokens.withdraw(withdrawID: key))
767            }
768
769            // Destroy the empty Collection
770            destroy tokens
771        }
772
773        // getIDs returns an array of the IDs that are in the Collection
774        pub fun getIDs(): [UInt64] {
775            return self.ownedNFTs.keys
776        }
777
778        // borrowNFT Returns a borrowed reference to a MintPFPs in the Collection
779        // so that the caller can read its ID
780        //
781        // Parameters: id: The ID of the NFT to get the reference for
782        //
783        // Returns: A reference to the NFT
784        //
785        // Note: This only allows the caller to read the ID of the NFT,
786        // not any MintPFPs specific data. Please use borrowLOSTiNTurismos to 
787        // read MintPFPs data.
788        //
789        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
790            return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
791        }
792
793        // borrowLOSTiNTurismo returns a borrowed reference to a MintPFPs
794        // so that the caller can read data and call methods from it.
795        // They can use this to read its editionID, editionNumber,
796        // or any edition data associated with it by
797        // getting the editionID and reading those fields from
798        // the smart contract.
799        //
800        // Parameters: id: The ID of the NFT to get the reference for
801        //
802        // Returns: A reference to the NFT
803        pub fun borrowLOSTiNTurismo(id: UInt64): &LOSTiNTurismo.NFT? {
804            if self.ownedNFTs[id] != nil {
805                let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
806                return ref as! &LOSTiNTurismo.NFT
807            } else {
808                return nil
809            }
810        }
811
812        // Making the collection conform to MetadataViews.Resolver
813        pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
814            let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
815            let LOSTiNTurismoNFT = nft as! &LOSTiNTurismo.NFT
816            return LOSTiNTurismoNFT as &AnyResource{MetadataViews.Resolver}
817        }
818
819        // If a transaction destroys the Collection object,
820        // All the NFTs contained within are also destroyed
821        //
822        destroy() {
823            destroy self.ownedNFTs
824        }
825    }
826
827
828
829    // -----------------------------------------------------------------------
830    // MintPFPs contract-level function definitions
831    // -----------------------------------------------------------------------
832
833    // createEmptyCollection creates a new, empty Collection object so that
834    // a user can store it in their account storage.
835    // Once they have a Collection in their storage, they are able to receive
836    // MintPFPs in transactions.
837    //
838    pub fun createEmptyCollection(): @NonFungibleToken.Collection {
839        return <-create LOSTiNTurismo.Collection()
840    }
841
842    pub fun createEmptyMintPFPsCollection(): @LOSTiNTurismo.Collection {
843        return <-create LOSTiNTurismo.Collection()
844    }
845
846    pub fun getExternalURL(): MetadataViews.ExternalURL {
847        return LOSTiNTurismo.ExternalURL;
848    }
849
850    pub fun getSocials(): {String: MetadataViews.ExternalURL} {
851        return LOSTiNTurismo.Socials;
852    }
853
854    pub fun getDescription(): String {
855        return LOSTiNTurismo.Description
856    }
857
858    pub fun getSquareImage(): MetadataViews.Media {
859        return LOSTiNTurismo.SquareImage;
860    }
861
862    pub fun getBannerImage(): MetadataViews.Media {
863        return LOSTiNTurismo.BannerImage;
864    }
865
866    // Returns all of the locked mutator IDs
867    pub fun getLockedMutators() : {UInt32: Bool} {
868        return LOSTiNTurismo.lockedMutators;
869    }
870
871    // getMerchantID returns the merchant ID
872    pub fun getMerchantID(): UInt32 {
873        return self.merchantID
874    }
875
876
877    // getDefaultRoyalties returns the default royalties
878    pub fun getDefaultRoyalties(): {String: MetadataViews.Royalty} {
879        return self.defaultRoyalties
880    }
881
882    // getDefaultRoyalties returns the default royalties
883    pub fun getDefaultRoyaltyNames(): [String] {
884        return self.defaultRoyalties.keys
885    }
886
887    // getDefaultRoyaltyRate returns a royalty object
888    pub fun getDefaultRoyalty(name: String): MetadataViews.Royalty? {
889            return self.defaultRoyalties[name]
890    }
891
892    // returns the default
893    pub fun getTotalDefaultRoyaltyRate(): UFix64 {
894            var totalRoyalty = 0.0
895            for key in self.defaultRoyalties.keys {
896                let royal = self.defaultRoyalties[key] ?? panic("Royalty does not exist")
897                totalRoyalty = totalRoyalty + royal.cut
898            }
899            return totalRoyalty
900    }
901
902
903    // getRoyaltiesForPFP returns the specific royalties for a PFP or the default royalties
904    pub fun getRoyaltiesForPFP(tokenID: UInt64): {String: MetadataViews.Royalty} {
905        return self.royaltiesForSpecificPFP[tokenID] ?? self.getDefaultRoyalties()
906    }
907
908    //  getRoyaltyNamesForPFP returns the  royalty names for a specific PFP or the default royalty names
909    pub fun getRoyaltyNamesForPFP(tokenID: UInt64): [String] {
910        return self.royaltiesForSpecificPFP[tokenID]?.keys ?? self.getDefaultRoyaltyNames()
911    }
912
913    // getRoyaltyNamesForPFP returns a given royalty for a specific PFP or the default royalty names
914    pub fun getRoyaltyForPFP(tokenID: UInt64, name: String): MetadataViews.Royalty? {
915
916        if  self.royaltiesForSpecificPFP.containsKey(tokenID){
917          let royaltiesForPFP:  {String: MetadataViews.Royalty} = self.royaltiesForSpecificPFP[tokenID]!
918          return royaltiesForPFP[name]!
919        }
920
921        // if no specific royalty is set
922        return self.getDefaultRoyalty(name: name)
923    }
924
925    // getTotalRoyaltyRateForPFP returns the total royalty rate for a give PFP
926    pub fun getTotalRoyaltyRateForPFP(tokenID: UInt64): UFix64 {
927
928       var totalRoyalty = 0.0
929       let royalties = self.getRoyaltiesForPFP(tokenID: tokenID)
930        for key in royalties.keys {
931            let royal = royalties[key] ?? panic("Royalty does not exist")
932            totalRoyalty = totalRoyalty + royal.cut
933        }
934        return totalRoyalty
935    }
936
937    
938
939
940
941
942
943    // -----------------------------------------------------------------------
944    // MintPFPs initialization function
945    // -----------------------------------------------------------------------
946    //
947    init() {
948        // Initialize contract fields
949        
950        self.totalSupply = 0
951        self.merchantID = 1
952        self.mutations = {}
953        self.defaultRoyalties = {}
954        self.royaltiesForSpecificPFP = {}
955        self.lockedMutators = {}
956        self.ExternalURL = MetadataViews.ExternalURL("https://lostin.com")
957        self.Socials = {}
958        self.Description = "The LOST iN Turismo Community. A space where LOST iN readers can interact with each other and the brand. Participation is granted through the purchase of a unique token (NFT) depicting a remixed version of our iconic cover. This token then grants you special access as well as participation in our upcoming editions as we open up to our community. We hope you are as excited as us in this new chapter for our brand and welcome you to join us on this trip."
959        self.SquareImage = MetadataViews.Media(
960            file: MetadataViews.HTTPFile(
961                url: "https://mint-store-metadata.s3.us-east-2.amazonaws.com/lostin/utility-thumbnails/logo_square(2).png"
962            ),
963            mediaType: "image/png"
964        )
965
966        self.BannerImage = MetadataViews.Media(
967            file: MetadataViews.HTTPFile(
968                url: "https://mint-store-metadata.s3.us-east-2.amazonaws.com/lostin/utility-thumbnails/Vuurtoreneiland_Annelore-01.png"
969            ),
970            mediaType: "image/png"
971        )
972
973        self.CollectionStoragePath = /storage/LOSTiNTurismoCollection
974        self.CollectionPublicPath = /public/LOSTiNTurismoCollection
975        self.AdminStoragePath = /storage/LOSTiNTurismoAdmin
976        self.MutatorStoragePath = /storage/LOSTiNTurismoMutator
977
978        // Put a new Collection in storage
979        self.account.save<@Collection>(<- create Collection(), to: self.CollectionStoragePath)
980
981        // Create a public capability for the Collection
982        self.account.link<&{LOSTiNTurismoCollectionPublic}>(self.CollectionPublicPath, target: self.CollectionStoragePath)
983
984        // Put the admin ressource in storage
985        self.account.save<@Admin>(<- create Admin(id: 1), to: self.AdminStoragePath)
986        self.nextAdminID = 2
987
988        // Put the admin ressource in storage
989        self.account.save<@Mutator>(<- create Mutator(id: 1), to: self.MutatorStoragePath)
990        self.nextMutatorID = 2
991
992        emit ContractInitialized()
993    }
994
995
996}
997    
998