Smart Contract

OpenLockerIncBoneYardHuskyzClub

A.20187093790b9aef.OpenLockerIncBoneYardHuskyzClub

Deployed

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