Smart Contract

Pickem

A.20187093790b9aef.Pickem

Deployed

1w ago
Feb 19, 2026, 05:46:13 PM UTC

Dependents

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