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