Smart Contract

YBees

A.20187093790b9aef.YBees

Deployed

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

Dependents

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