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