Smart Contract
TheFabricantKapers
A.7752ea736384322f.TheFabricantKapers
1import TheFabricantMetadataViewsV2 from 0x7752ea736384322f
2
3import ViewResolver from 0x1d7e57aa55817448
4
5import MetadataViews from 0x1d7e57aa55817448
6
7import NonFungibleToken from 0x1d7e57aa55817448
8
9import TheFabricantNFTStandardV2 from 0x7752ea736384322f
10
11import RevealableV2 from 0x7752ea736384322f
12
13import CoCreatableV2 from 0x7752ea736384322f
14
15import TheFabricantAccessList from 0x7752ea736384322f
16
17import FungibleToken from 0xf233dcee88fe0abe
18
19import FlowToken from 0x1654653399040a61
20
21// Kapers
22// 3 garments
23// 10 materials
24// 10 primary colors
25// 10 secondary colors
26access(all)
27contract TheFabricantKapers: NonFungibleToken, TheFabricantNFTStandardV2, RevealableV2{
28
29 // -----------------------------------------------------------------------
30 // Paths
31 // -----------------------------------------------------------------------
32 access(all)
33 let TheFabricantKapersCollectionStoragePath: StoragePath
34
35 access(all)
36 let TheFabricantKapersCollectionPublicPath: PublicPath
37
38 access(all)
39 let TheFabricantKapersProviderPath: PrivatePath
40
41 access(all)
42 let TheFabricantKapersPublicMinterStoragePath: StoragePath
43
44 access(all)
45 let TheFabricantKapersAdminStoragePath: StoragePath
46
47 access(all)
48 let TheFabricantKapersPublicMinterPublicPath: PublicPath
49
50 // -----------------------------------------------------------------------
51 // Contract Events
52 // -----------------------------------------------------------------------
53 // Event that emitted when the NFT contract is initialized
54 //
55 access(all)
56 event ContractInitialized()
57
58 access(all)
59 event ItemMintedAndTransferred(uuid: UInt64, id: UInt64, name: String, description: String, collection: String, editionNumber: UInt64, originalRecipient: Address, license: MetadataViews.License?, nftMetadataId: UInt64)
60
61 access(all)
62 event DataAllocationCreated(uuid: UInt64, id: UInt64, editionNumber: UInt64, originalRecipient: Address, nftMetadataId: UInt64, dataAllocationString: String)
63
64 access(all)
65 event ItemRevealed(uuid: UInt64, id: UInt64, name: String, description: String, collection: String, editionNumber: UInt64, originalRecipient: Address, license: MetadataViews.License?, nftMetadataId: UInt64, externalURL: MetadataViews.ExternalURL, coCreatable: Bool, coCreator: Address)
66
67 access(all)
68 event TraitRevealed(nftUuid: UInt64, id: UInt64, trait: String)
69
70 access(all)
71 event IsTraitRevealableUpdated(nftUuid: UInt64, id: UInt64, trait: String, isRevealable: Bool)
72
73 access(all)
74 event ItemDestroyed(uuid: UInt64, id: UInt64, name: String, description: String, collection: String)
75
76 access(all)
77 event MintPaymentSplitDeposited(address: Address, price: UFix64, amount: UFix64, nftUuid: UInt64)
78
79 access(all)
80 event PublicMinterCreated(uuid: UInt64, name: String, description: String, collection: String, path: String)
81
82 access(all)
83 event PublicMinterIsOpenAccessChanged(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool)
84
85 access(all)
86 event PublicMinterIsAccessListOnly(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool)
87
88 access(all)
89 event PublicMinterMintingIsOpen(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool)
90
91 access(all)
92 event PublicMinterSetAccessListId(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool, accessListId: UInt64)
93
94 access(all)
95 event PublicMinterSetPaymentAmount(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool, paymentAmount: UFix64)
96
97 access(all)
98 event PublicMinterSetMinterMintLimit(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool, minterMintLimit: UInt64?)
99
100 access(all)
101 event AdminResourceCreated(uuid: UInt64, adminAddress: Address)
102
103 access(all)
104 event AdminPaymentReceiverCapabilityChanged(address: Address, paymentType: Type)
105
106 access(all)
107 event AdminSetMaxSupply(maxSupply: UInt64)
108
109 access(all)
110 event AdminSetAddressMintLimit(addressMintLimit: UInt64)
111
112 access(all)
113 event AdminSetCollectionId(collectionId: String)
114
115 access(all)
116 event AdminSetBaseURI(baseURI: String)
117
118 access(all)
119 event AdminSetIsFreeMintActive(isActive: Bool)
120
121 // Event that is emitted when a token is withdrawn,
122 // indicating the owner of the collection that it was withdrawn from.
123 //
124 // If the collection is not in an account's storage, `from` will be `nil`.
125 //
126 access(all)
127 event Withdraw(id: UInt64, from: Address?)
128
129 // Event that emitted when a token is deposited to a collection.
130 //
131 // It indicates the owner of the collection that it was deposited to.
132 //
133 access(all)
134 event Deposit(id: UInt64, to: Address?)
135
136 // -----------------------------------------------------------------------
137 // Contract State
138 // -----------------------------------------------------------------------
139 // NOTE: This is updated anywhere ownership of the nft is changed - on minting and therefore on deposit
140 access(contract)
141 var nftIdsToOwner:{ UInt64: Address}
142
143 access(contract)
144 var publicMinterPaths:{ UInt64: String}
145
146 // NOTE: this is contract-level so all minters can access it.
147 // Keeps track of the number of times an address has minted
148 access(contract)
149 var addressMintCount:{ Address: UInt64}
150
151 // Receives payment for minting
152 access(contract)
153 var paymentReceiverCap: Capability<&{FungibleToken.Receiver}>?
154
155 access(contract)
156 var nftMetadata:{ UInt64:{ RevealableV2.RevealableMetadata}}
157
158 // Keeps track of characteristic combinations, ensuring unique combos
159 access(contract)
160 var dataAllocations:{ String: UInt64}
161
162 access(contract)
163 var idsToDataAllocations:{ UInt64: String}
164
165 // The total number of tokens of this type in existence
166 // NOTE: All public minters use totalSupply to assign the next
167 // id and edition number. Each public minter has a minterMintLimit property
168 // that defines the max no. of mints a pM can do.
169 access(all)
170 var totalSupply: UInt64
171
172 // NOTE: The max number of NFTs in this collection that will ever be minted
173 // Init as nil if there is no max.
174 access(all)
175 var maxSupply: UInt64?
176
177 // NOTE: Max mints per address
178 access(all)
179 var addressMintLimit: UInt64?
180
181 //NOTE: uuid of collection added to NFT and used by BE
182 access(all)
183 var collectionId: String?
184
185 // Change these dictionaries to represent the characteristics
186 // of the nft that the user can choose from
187 access(contract)
188 var garments:{ UInt64:{ CoCreatableV2.Characteristic}}
189
190 access(contract)
191 var materials:{ UInt64:{ CoCreatableV2.Characteristic}}
192
193 access(contract)
194 var primaryColors:{ UInt64:{ CoCreatableV2.Characteristic}}
195
196 access(contract)
197 var secondaryColors:{ UInt64:{ CoCreatableV2.Characteristic}}
198
199 access(contract)
200 var baseTokenURI: String?
201
202 access(contract)
203 var isFreeMintActive: Bool
204
205 // {mintAddress: nftId}
206 access(contract)
207 var claimedFreeMints:{ Address: UInt64}
208
209 // -----------------------------------------------------------------------
210 // RevealableV2 Metadata Struct
211 // -----------------------------------------------------------------------
212 access(all)
213 struct RevealableMetadata: RevealableV2.RevealableMetadata{
214
215 //NOTE: totalSupply value of attached NFT, therefore edition number.
216 access(all)
217 let id: UInt64
218
219 // NOTE: !IMPORTANT! nftUuid is the uuid of the associated nft.
220 // This RevealableMetadata struct should be stored in the nftMetadata dict under this
221 // value. This is because the uuid is used across contracts for identification purposes
222 access(all)
223 let nftUuid: UInt64 // uuid of NFT
224
225
226 // NOTE: Name of NFT. Will most likely be the last node in the collection value.
227 // eg XXories Original.
228 // Will be combined with the edition number on the application
229 // Doesn't include the edition number.
230 access(all)
231 var name: String
232
233 access(all)
234 var description: String //Display
235
236
237 // NOTE: Thumbnail, which is needed for the Display view, should be set using one of the
238 // media properties
239 //access(all) let thumbnail: String //Display
240 access(all)
241 let collection: String // Name of collection eg The Fabricant > Season 3 > Wholeland > XXories Originals
242
243
244 // Stores the metadata that describes this particular creation,
245 // but is not part of a characteristic eg mainImage, video etc
246 access(all)
247 var metadata:{ String: AnyStruct}
248
249 // This is where the user-chosen characteristics live. This represents
250 // the data that in older contracts, would've been separate NFTs.
251 access(all)
252 var characteristics:{ String:{ CoCreatableV2.Characteristic}}
253
254 access(all)
255 var rarity: UFix64?
256
257 access(all)
258 var rarityDescription: String?
259
260 // NOTE: Media is not implemented in the struct because MetadataViews.Medias
261 // is not mutable, so can't be updated. In addition, each
262 // NFT collection might have a different number of image/video properties.
263 // Instead, the NFT should implement a function that rolls up the props
264 // into a MetadataViews.Medias struct
265 //access(all) let media: MetadataViews.Medias //Media
266 access(all)
267 let license: MetadataViews.License? //License
268
269
270 access(all)
271 let externalURL: MetadataViews.ExternalURL //ExternalURL
272
273
274 access(all)
275 let coCreatable: Bool
276
277 access(all)
278 let coCreator: Address
279
280 access(all)
281 var isRevealed: Bool?
282
283 // id and editionNumber might not be the same in the nft...
284 access(all)
285 let editionNumber: UInt64 //Edition
286
287
288 access(all)
289 let maxEditionNumber: UInt64?
290
291 access(all)
292 let royalties: MetadataViews.Royalties //Royalty
293
294
295 access(all)
296 let royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties
297
298 access(contract)
299 var revealableTraits:{ String: Bool}
300
301 access(all)
302 fun getRevealableTraits():{ String: Bool}{
303 return self.revealableTraits
304 }
305
306 //NOTE: Customise
307 //NOTE: This should be updated for each campaign contract!
308 // Called by the Admin to reveal the traits for this NFT.
309 // Should contain a switch function that knows how to modify
310 // the properties of this struct. Should check that the trait
311 // being revealed is allowed to be modified.
312 access(contract)
313 fun revealTraits(traits: [{RevealableV2.RevealableTrait}]){
314 assert(TheFabricantKapers.baseTokenURI != nil, message: "The base URI must be set before reveal can take place")
315 var i = 0
316 while i < traits.length{
317 let revealableTrait = traits[i]
318 let traitName = revealableTrait.name
319 let traitValue = revealableTrait.value
320 switch traitName{
321 case "mainImage":
322 assert(self.checkRevealableTrait(traitName: traitName)!, message: "Unrevealable trait passed in - please ensure trait can be revealed: ".concat(traitName))
323 self.updateMetadata(key: traitName, value: traitValue)
324 case "video":
325 assert(self.checkRevealableTrait(traitName: traitName)!, message: "Unrevealable trait passed in - please ensure trait can be revealed: ".concat(traitName))
326 self.updateMetadata(key: traitName, value: traitValue)
327 default:
328 panic("Unrevealable trait passed in - please ensure trait can be revealed: ".concat(traitName))
329 }
330 i = i + 1
331 }
332 //NOTE: Customise
333 // Some collections may allow users to partially reveal their items. In this case,
334 // it may not be appropriate to set isRevealed to true yet.
335 self.isRevealed = true
336 }
337
338 access(self)
339 fun constructAssetPath(): String{
340
341 // Get CharcteristicIds
342 let garmentId = ((self.characteristics["garment"] as{ CoCreatableV2.Characteristic}?)!).id
343 let materialId = ((self.characteristics["material"] as{ CoCreatableV2.Characteristic}?)!).id
344 let primaryColorId = ((self.characteristics["primaryColor"] as!{ CoCreatableV2.Characteristic}?)!).id
345 let secondaryColorId = ((self.characteristics["secondaryColor"] as!{ CoCreatableV2.Characteristic}?)!).id
346 return "/".concat(garmentId.toString()).concat("_").concat(materialId.toString()).concat("_").concat(primaryColorId.toString()).concat("_").concat(secondaryColorId.toString())
347 }
348
349 access(contract)
350 fun updateMetadata(key: String, value: AnyStruct){
351 self.metadata[key] = value
352 }
353
354 // Called by the nft owner to modify if a trait can be
355 // revealed or not - used to revoke admin access
356 access(all)
357 fun updateIsTraitRevealable(key: String, value: Bool){
358 self.revealableTraits[key] = value
359 }
360
361 access(all)
362 fun checkRevealableTrait(traitName: String): Bool?{
363 if let revealable = self.revealableTraits[traitName]{
364 return revealable
365 }
366 return nil
367 }
368
369 init(id: UInt64, nftUuid: UInt64, name: String, description: String, collection: String, metadata:{ String: AnyStruct}, characteristics:{ String:{ CoCreatableV2.Characteristic}}, license: MetadataViews.License?, externalURL: MetadataViews.ExternalURL, coCreatable: Bool, coCreator: Address, editionNumber: UInt64, maxEditionNumber: UInt64?, revealableTraits:{ String: Bool}, royalties: MetadataViews.Royalties, royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties){
370 self.id = id
371 self.nftUuid = nftUuid
372 self.name = name
373 self.description = description
374 self.collection = collection
375 self.metadata = metadata
376 self.characteristics = characteristics
377 self.rarity = nil
378 self.rarityDescription = nil
379 self.license = license
380 self.externalURL = externalURL
381 self.coCreatable = coCreatable
382 self.coCreator = coCreator
383 //NOTE: Customise
384 // This should be nil if the nft can't be revealed!
385 self.isRevealed = true
386 self.editionNumber = editionNumber
387 self.maxEditionNumber = maxEditionNumber
388 self.revealableTraits = revealableTraits
389 self.royalties = royalties
390 self.royaltiesTFMarketplace = royaltiesTFMarketplace
391 }
392 }
393
394 // -----------------------------------------------------------------------
395 // Trait Struct
396 // -----------------------------------------------------------------------
397 // Used by txs to target traits/characteristics to be revealed
398 access(all)
399 struct Trait: RevealableV2.RevealableTrait{
400 access(all)
401 let name: String
402
403 access(all)
404 let value: AnyStruct
405
406 init(name: String, value: AnyStruct){
407 self.name = name
408 self.value = value
409 }
410 }
411
412 // -----------------------------------------------------------------------
413 // Characteristic Struct
414 // -----------------------------------------------------------------------
415 access(all)
416 struct Characteristic: CoCreatableV2.Characteristic{
417 access(all)
418 var id: UInt64
419
420 access(all)
421 var version: UFix64
422
423 // This is the name that will be used for the trait, so
424 // will be displayed on external MPs etc. Should be capitalised
425 // and spaced eg "Garment Name"
426 access(all)
427 var traitName: String
428
429 // eg characteristicType = garmentData
430 access(all)
431 var characteristicType: String
432
433 access(all)
434 var characteristicDescription: String
435
436 access(all)
437 var designerName: String?
438
439 access(all)
440 var designerDescription: String?
441
442 access(all)
443 var designerAddress: Address?
444
445 // Value is the name of the selected characteristic
446 // For example, for a garment, this might be "Adventurer Top"
447 access(all)
448 var value: AnyStruct
449
450 access(all)
451 var rarity: MetadataViews.Rarity?
452
453 access(all)
454 var media: MetadataViews.Medias?
455
456 init(id: UInt64, traitName: String, characteristicType: String, characteristicDescription: String, designerName: String?, designerDescription: String?, designerAddress: Address?, value: AnyStruct, rarity: MetadataViews.Rarity?, media: MetadataViews.Medias?){
457 self.id = id
458 //NOTE: Customise according to the
459 // CoCreatableV2 contract version
460 self.version = 2.0
461 self.traitName = traitName
462 self.characteristicType = characteristicType
463 self.characteristicDescription = characteristicDescription
464 self.designerName = designerName
465 self.designerDescription = designerDescription
466 self.designerAddress = designerAddress
467 self.value = value
468 self.rarity = rarity
469 self.media = media
470 }
471
472 // NOTE: Customise
473 //Helper function that converts to traits for MetadataViews
474 access(all)
475 fun convertToTraits(): [MetadataViews.Trait]{
476 let traits: [MetadataViews.Trait] = []
477 let idTrait = MetadataViews.Trait(name: self.traitName.concat(" ID"), value: self.id, displayType: "Number", rarity: nil)
478 traits.append(idTrait)
479 if self.designerName != nil{
480 let designerNameTrait = MetadataViews.Trait(name: self.traitName.concat(" Designer"), value: self.designerName, displayType: "String", rarity: nil)
481 traits.append(designerNameTrait)
482 }
483 if self.designerDescription != nil{
484 let designerDescriptionTrait = MetadataViews.Trait(name: self.traitName.concat(" Designer Description"), value: self.designerDescription, displayType: "String", rarity: nil)
485 traits.append(designerDescriptionTrait)
486 }
487 let valueRarity = self.rarity != nil ? self.rarity : nil
488 // If the designer name is nil, then the trait wasn't designed and it must be a color.
489 // Therefore we give it a name of "XYZ Value"
490 let valueTrait = MetadataViews.Trait(name: self.designerName != nil ? self.traitName.concat(" Name") : self.traitName.concat(" Value"), value: self.value, displayType: "String", rarity: valueRarity)
491 traits.append(valueTrait)
492 return traits
493 }
494
495 //NOTE: Customise
496 access(contract)
497 fun updateCharacteristic(key: String, value: AnyStruct){}
498 // Can be implemented if needed.
499 // Should be a switch statement like revealTrait() in Metadata struct
500 }
501
502 // -----------------------------------------------------------------------
503 // NFT Resource
504 // -----------------------------------------------------------------------
505 // Restricted scope for borrowTheFabricantKapers() in Collection.
506 // Ensures that the returned NFT ref is read only.
507 access(all)
508 resource interface PublicNFT{
509 access(all)
510 fun getFullName(): String
511
512 access(all)
513 fun getEditions(): MetadataViews.Editions
514
515 access(all)
516 fun getMedias(): MetadataViews.Medias
517
518 access(all)
519 fun getTraits(): MetadataViews.Traits?
520
521 access(all)
522 view fun getRarity(): MetadataViews.Rarity?
523
524 access(all)
525 fun getExternalRoyalties(): MetadataViews.Royalties
526
527 access(all)
528 fun getTFRoyalties(): TheFabricantMetadataViewsV2.Royalties
529
530 access(all)
531 fun getMetadata():{ String: AnyStruct}
532
533 access(all)
534 fun getCharacteristics():{ String:{ CoCreatableV2.Characteristic}}?
535
536 access(all)
537 fun getDisplay(): MetadataViews.Display
538
539 access(all)
540 fun getCollectionData(): MetadataViews.NFTCollectionData
541
542 access(all)
543 fun getCollectionDisplay(): MetadataViews.NFTCollectionDisplay
544
545 access(all)
546 fun getNFTView(): MetadataViews.NFTView
547
548 access(all)
549 fun getViews(): [Type]
550
551 access(all)
552 fun resolveView(_ view: Type): AnyStruct?
553 }
554
555 access(all)
556 resource NFT: TheFabricantNFTStandardV2.TFNFT, NonFungibleToken.NFT, ViewResolver.Resolver, PublicNFT{
557 access(all)
558 let id: UInt64
559
560 //access(all) let uuid: UInt64 //Display, Serial, <- uuid is set automatically so no need to add
561 // NOTE: Ensure that the name for the nft is correct. This
562 // will be shown to users. It should not include the edition number.
563 access(contract)
564 let collectionId: String
565
566 access(contract)
567 let editionNumber: UInt64 //Edition
568
569
570 access(contract)
571 let maxEditionNumber: UInt64?
572
573 access(contract)
574 let originalRecipient: Address
575
576 access(contract)
577 let license: MetadataViews.License?
578
579 access(contract)
580 let nftMetadataId: UInt64
581
582 access(all)
583 fun getFullName(): String{
584 return ((TheFabricantKapers.nftMetadata[self.nftMetadataId]!).name!).concat(" #".concat(self.editionNumber.toString()))
585 }
586
587 // NOTE: This is important for Edition view
588 access(all)
589 fun getEditionName(): String{
590 return (TheFabricantKapers.nftMetadata[self.nftMetadataId]!).collection
591 }
592
593 access(all)
594 fun getEditions(): MetadataViews.Editions{
595 // NOTE: In this case, id == edition number
596 let edition = MetadataViews.Edition(name: (TheFabricantKapers.nftMetadata[self.nftMetadataId]!).collection, number: self.editionNumber, max: TheFabricantKapers.maxSupply)
597 return MetadataViews.Editions([edition])
598 }
599
600 //NOTE: Customise
601 //NOTE: This will be different for each campaign, determined by how
602 // many media files there are and their keys in metadata! Pay attention
603 // to where the media files are stored and therefore accessed
604 // NOTE: DOUBLE CHECK THE fileType IS CORRECT!!!
605 access(all)
606 fun getMedias(): MetadataViews.Medias{
607 let nftMetadata = TheFabricantKapers.nftMetadata[self.id]!
608 let mainImage = nftMetadata.metadata["mainImage"]! as! String
609 // NOTE: This assumes that when the garment characteristic is created
610 // in the update_garment_char tx, the value property is created as a dictionary
611 let video = nftMetadata.metadata["video"]! as! String
612 let mainImageMedia = MetadataViews.Media(file: MetadataViews.HTTPFile(url: mainImage), mediaType: "image/png")
613 let videoMedia = MetadataViews.Media(file: MetadataViews.HTTPFile(url: video), mediaType: "video/mp4")
614 return MetadataViews.Medias([mainImageMedia, videoMedia])
615 }
616
617 // NOTE: Customise
618 access(all)
619 fun getImages():{ String: String}{
620 let nftMetadata = TheFabricantKapers.nftMetadata[self.id]!
621 let mainImage = nftMetadata.metadata["mainImage"]! as! String
622 return{ "mainImage": mainImage}
623 }
624
625 // NOTE: Customise
626 access(all)
627 fun getVideos():{ String: String}{
628 let nftMetadata = TheFabricantKapers.nftMetadata[self.id]!
629 let mainVideo = nftMetadata.metadata["video"]! as! String
630 return{ "mainVideo": mainVideo}
631 }
632
633 // NOTE: Customise
634 // What are the traits that you want external marketplaces
635 // to display?
636 access(all)
637 fun getTraits(): MetadataViews.Traits?{
638 let characteristics = (TheFabricantKapers.nftMetadata[self.id]!).characteristics
639 let garmentCharacteristic = characteristics["garment"]!
640 let garmentTraits = garmentCharacteristic.convertToTraits()
641 let materialCharacteristic = characteristics["material"]!
642 let materialTraits = materialCharacteristic.convertToTraits()
643 let primaryColorCharacteristic = characteristics["primaryColor"]!
644 let primaryColorTraits = primaryColorCharacteristic.convertToTraits()
645 let secondaryColorsCharacteristic = characteristics["secondaryColor"]!
646 let secondaryColorTraits = secondaryColorsCharacteristic.convertToTraits()
647 let concatenatedArrays = garmentTraits.concat(materialTraits).concat(primaryColorTraits).concat(secondaryColorTraits)
648 return MetadataViews.Traits(concatenatedArrays)
649 }
650
651 access(all)
652 view fun getRarity(): MetadataViews.Rarity?{
653 return nil
654 }
655
656 access(all)
657 fun getExternalRoyalties(): MetadataViews.Royalties{
658 let nftMetadata = TheFabricantKapers.nftMetadata[self.id]!
659 return nftMetadata.royalties
660 }
661
662 access(all)
663 fun getTFRoyalties(): TheFabricantMetadataViewsV2.Royalties{
664 let nftMetadata = TheFabricantKapers.nftMetadata[self.id]!
665 return nftMetadata.royaltiesTFMarketplace
666 }
667
668 access(all)
669 fun getMetadata():{ String: AnyStruct}{
670 return (TheFabricantKapers.nftMetadata[self.id]!).metadata
671 }
672
673 //NOTE: This is not a CoCreatableV2 NFT, so no characteristics are present
674 access(all)
675 fun getCharacteristics():{ String:{ CoCreatableV2.Characteristic}}?{
676 return (TheFabricantKapers.nftMetadata[self.id]!).characteristics
677 }
678
679 access(all)
680 fun getRevealableTraits():{ String: Bool}?{
681 return (TheFabricantKapers.nftMetadata[self.id]!).getRevealableTraits()
682 }
683
684 //NOTE: The first file in medias will be the thumbnail.
685 // Maybe put a file type check in here to ensure it is
686 // an image?
687 access(all)
688 fun getDisplay(): MetadataViews.Display{
689 return MetadataViews.Display(name: self.getFullName(), description: (TheFabricantKapers.nftMetadata[self.nftMetadataId]!).description, thumbnail: self.getMedias().items[0].file)
690 }
691
692 access(all)
693 fun getCollectionData(): MetadataViews.NFTCollectionData{
694 return MetadataViews.NFTCollectionData(storagePath: TheFabricantKapers.TheFabricantKapersCollectionStoragePath, publicPath: TheFabricantKapers.TheFabricantKapersCollectionPublicPath, publicCollection: Type<&TheFabricantKapers.Collection>(), publicLinkedType: Type<&TheFabricantKapers.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{
695 return <-TheFabricantKapers.createEmptyCollection(nftType: Type<@TheFabricantKapers.Collection>())
696 })
697 }
698
699 //NOTE: Customise
700 // NOTE: Update this function with the collection display image
701 // and TF socials
702 access(all)
703 fun getCollectionDisplay(): MetadataViews.NFTCollectionDisplay{
704 let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://leela.mypinata.cloud/ipfs/QmVPbbS7zVEyBGARHSVX8KcYBZ7bqV48koT2ibBFbL4iYy/BULLISH_KAPER_01.png"), mediaType: "image/png")
705 let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://leela.mypinata.cloud/ipfs/QmVPbbS7zVEyBGARHSVX8KcYBZ7bqV48koT2ibBFbL4iYy/VENICE_SCENE1_SHOT3_2K0530-studio.png"), mediaType: "image/png")
706 return MetadataViews.NFTCollectionDisplay(name: self.getEditionName(), description: "Inspired by club culture in the 90s and 2000s, the Kapers are headgear to get you moving up to speed. Next to the XXories, they form a part of the overall look of the collection and we want you to co-create them with us.", externalURL: (TheFabricantKapers.nftMetadata[self.id]!).externalURL, squareImage: squareImage, bannerImage: bannerImage, socials:{ "twitter": MetadataViews.ExternalURL("https://twitter.com/thefabricant"), "instagram": MetadataViews.ExternalURL("https://www.instagram.com/the_fab_ric_ant/"), "facebook": MetadataViews.ExternalURL("https://www.facebook.com/thefabricantdesign/"), "artstation": MetadataViews.ExternalURL("https://www.artstation.com/thefabricant"), "behance": MetadataViews.ExternalURL("https://www.behance.net/thefabricant"), "linkedin": MetadataViews.ExternalURL("https://www.linkedin.com/company/the-fabricant"), "sketchfab": MetadataViews.ExternalURL("https://sketchfab.com/thefabricant"), "clolab": MetadataViews.ExternalURL("https://www.clo3d.com/en/clollab/thefabricant"), "tiktok": MetadataViews.ExternalURL("@digital_fashion"), "discord": MetadataViews.ExternalURL("https://discord.com/channels/692039738751713280/778601303013195836")})
707 }
708
709 access(all)
710 fun getNFTView(): MetadataViews.NFTView{
711 return MetadataViews.NFTView(id: self.id, uuid: self.uuid, display: self.getDisplay(), externalURL: (TheFabricantKapers.nftMetadata[self.id]!).externalURL, collectionData: self.getCollectionData(), collectionDisplay: self.getCollectionDisplay(), royalties: (TheFabricantKapers.nftMetadata[self.id]!).royalties, traits: self.getTraits())
712 }
713
714 access(all)
715 view fun getViews(): [Type]{
716 let viewArray: [Type] = [Type<TheFabricantMetadataViewsV2.TFNFTIdentifierV1>(), Type<TheFabricantMetadataViewsV2.TFNFTSimpleView>(), Type<MetadataViews.NFTView>(), Type<MetadataViews.Display>(), Type<MetadataViews.Editions>(), Type<MetadataViews.Serial>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.Medias>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Traits>()]
717 return viewArray
718 }
719
720 access(all)
721 fun resolveView(_ view: Type): AnyStruct?{
722 switch view{
723 case Type<TheFabricantMetadataViewsV2.TFNFTIdentifierV1>():
724 return TheFabricantMetadataViewsV2.TFNFTIdentifierV1(uuid: self.uuid, id: self.id, name: self.getFullName(), collection: (TheFabricantKapers.nftMetadata[self.nftMetadataId]!).collection, editions: self.getEditions(), address: (self.owner!).address, originalRecipient: self.originalRecipient)
725 case Type<TheFabricantMetadataViewsV2.TFNFTSimpleView>():
726 return TheFabricantMetadataViewsV2.TFNFTSimpleView(uuid: self.uuid, id: self.id, name: self.getFullName(), description: (TheFabricantKapers.nftMetadata[self.nftMetadataId]!).description, collection: (TheFabricantKapers.nftMetadata[self.nftMetadataId]!).collection, collectionId: TheFabricantKapers.collectionId!, metadata: self.getMetadata(), media: self.getMedias(), images: self.getImages(), videos: self.getVideos(), externalURL: (TheFabricantKapers.nftMetadata[self.id]!).externalURL, rarity: self.getRarity(), traits: self.getTraits(), characteristics: self.getCharacteristics(), coCreatable: (TheFabricantKapers.nftMetadata[self.id]!).coCreatable, coCreator: (TheFabricantKapers.nftMetadata[self.id]!).coCreator, isRevealed: (TheFabricantKapers.nftMetadata[self.id]!).isRevealed, editions: self.getEditions(), originalRecipient: self.originalRecipient, royalties: (TheFabricantKapers.nftMetadata[self.id]!).royalties, royaltiesTFMarketplace: (TheFabricantKapers.nftMetadata[self.id]!).royaltiesTFMarketplace, revealableTraits: self.getRevealableTraits(), address: (self.owner!).address)
727 case Type<MetadataViews.NFTView>():
728 return self.getNFTView()
729 case Type<MetadataViews.Display>():
730 return self.getDisplay()
731 case Type<MetadataViews.Editions>():
732 return self.getEditions()
733 case Type<MetadataViews.Serial>():
734 return self.id
735 case Type<MetadataViews.Royalties>():
736 return TheFabricantKapers.nftMetadata[self.id]?.royalties
737 case Type<MetadataViews.Medias>():
738 return self.getMedias()
739 case Type<MetadataViews.License>():
740 return self.license
741 case Type<MetadataViews.ExternalURL>():
742 return TheFabricantKapers.nftMetadata[self.id]?.externalURL
743 case Type<MetadataViews.NFTCollectionData>():
744 return self.getCollectionData()
745 case Type<MetadataViews.NFTCollectionDisplay>():
746 return self.getCollectionDisplay()
747 case Type<MetadataViews.Rarity>():
748 return self.getRarity()
749 case Type<MetadataViews.Traits>():
750 return self.getTraits()
751 }
752 return nil
753 }
754
755 access(all)
756 fun updateIsTraitRevealable(key: String, value: Bool){
757 let nftMetadata = TheFabricantKapers.nftMetadata[self.id]!
758 nftMetadata.updateIsTraitRevealable(key: key, value: value)
759 TheFabricantKapers.nftMetadata[self.id] = nftMetadata
760 emit IsTraitRevealableUpdated(nftUuid: nftMetadata.nftUuid, id: nftMetadata.id, trait: key, isRevealable: value)
761 }
762
763 access(all)
764 fun createEmptyCollection(): @{NonFungibleToken.Collection}{
765 return <-create Collection()
766 }
767
768 init(originalRecipient: Address, license: MetadataViews.License?){
769 assert(TheFabricantKapers.collectionId != nil, message: "Ensure that Admin has set collectionId in the contract")
770 TheFabricantKapers.totalSupply = TheFabricantKapers.totalSupply + 1
771 self.id = TheFabricantKapers.totalSupply
772 self.collectionId = TheFabricantKapers.collectionId!
773
774 // NOTE: Customise
775 // The edition number may need to be different to id
776 // for some campaigns
777 self.editionNumber = self.id
778 self.maxEditionNumber = TheFabricantKapers.maxSupply
779 self.originalRecipient = originalRecipient
780 self.license = license
781 self.nftMetadataId = self.id
782 }
783 }
784
785 // -----------------------------------------------------------------------
786 // Collection Resource
787 // -----------------------------------------------------------------------
788 access(all)
789 resource interface TheFabricantKapersCollectionPublic{
790 access(all)
791 fun borrowTheFabricantKapers(id: UInt64): &TheFabricantKapers.NFT?
792
793 access(all)
794 fun deposit(token: @{NonFungibleToken.NFT})
795
796 access(all)
797 fun getIDs(): [UInt64]
798
799 access(all)
800 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
801
802 access(all)
803 view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?
804 }
805
806 access(all)
807 resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, TheFabricantKapersCollectionPublic, ViewResolver.ResolverCollection{
808
809 // Dictionary to hold the NFTs in the Collection
810 access(all)
811 var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
812
813 access(all)
814 view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{
815 let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
816 let TheFabricantKapers = nft as! &TheFabricantKapers.NFT
817 return TheFabricantKapers as &{ViewResolver.Resolver}
818 }
819
820 // withdraw removes an NFT from the collection and moves it to the caller
821 access(NonFungibleToken.Withdraw)
822 fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{
823 // Remove the nft from the Collection
824 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Cannot withdraw: NFT does not exist in the collection")
825 emit Withdraw(id: token.id, from: self.owner?.address)
826
827 // Return the withdrawn token
828 return <-token
829 }
830
831 // deposit takes an NFT and adds it to the collections dictionary
832 // and adds the ID to the id array
833 access(all)
834 fun deposit(token: @{NonFungibleToken.NFT}){
835 // By ensuring self.owner.address is not nil we keep the nftIdsToOwner dict
836 // up to date.
837 pre{
838 self.owner?.address != nil:
839 "The Collection resource must be stored in a users account"
840 }
841
842 // Cast the deposited token as NFT to make sure
843 // it is the correct type
844 let token <- token as! @NFT
845
846 // Get the token's ID
847 let id = token.id
848
849 // Add the new token to the dictionary
850 let oldToken <- self.ownedNFTs[id] <- token
851 TheFabricantKapers.nftIdsToOwner[id] = (self.owner!).address
852 emit Deposit(id: id, to: self.owner?.address)
853
854 // Destroy the empty old token that was "removed"
855 destroy oldToken
856 }
857
858 // getIDs returns an array of the IDs that are in the collection
859 access(all)
860 view fun getIDs(): [UInt64]{
861 return self.ownedNFTs.keys
862 }
863
864 // Returns a borrowed reference to an NFT in the collection
865 // so that the caller can read data and call methods from it
866 access(all)
867 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{
868 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
869 }
870
871 access(all)
872 fun borrowTheFabricantKapers(id: UInt64): &TheFabricantKapers.NFT?{
873 if self.ownedNFTs[id] != nil{
874 // Create an authorized reference to allow downcasting
875 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
876 return ref as! &TheFabricantKapers.NFT
877 }
878 return nil
879 }
880
881 access(all)
882 view fun getSupportedNFTTypes():{ Type: Bool}{
883 panic("implement me")
884 }
885
886 access(all)
887 view fun isSupportedNFTType(type: Type): Bool{
888 panic("implement me")
889 }
890
891 access(all)
892 fun createEmptyCollection(): @{NonFungibleToken.Collection}{
893 return <-create Collection()
894 }
895
896 // If a transaction destroys the Collection object,
897 // All the NFTs contained within are also destroyed!
898 //
899 init(){
900 self.ownedNFTs <-{}
901 }
902 }
903
904 // -----------------------------------------------------------------------
905 // Admin Resource
906 // -----------------------------------------------------------------------
907 access(all)
908 resource Admin{
909 access(all)
910 fun setPublicReceiverCap(paymentReceiverCap: Capability<&{FungibleToken.Receiver}>){
911 TheFabricantKapers.paymentReceiverCap = paymentReceiverCap
912 emit AdminPaymentReceiverCapabilityChanged(address: paymentReceiverCap.address, paymentType: paymentReceiverCap.getType())
913 }
914
915 access(all)
916 fun setBaseURI(baseURI: String){
917 TheFabricantKapers.baseTokenURI = baseURI
918 emit AdminSetBaseURI(baseURI: baseURI)
919 }
920
921 // The max supply determines the maximum number of NFTs that can be minted from this contract
922 access(all)
923 fun setMaxSupply(maxSupply: UInt64){
924 TheFabricantKapers.maxSupply = maxSupply
925 emit AdminSetMaxSupply(maxSupply: maxSupply)
926 }
927
928 access(all)
929 fun setAddressMintLimit(addressMintLimit: UInt64){
930 TheFabricantKapers.addressMintLimit = addressMintLimit
931 emit AdminSetAddressMintLimit(addressMintLimit: addressMintLimit)
932 }
933
934 access(all)
935 fun setCollectionId(collectionId: String){
936 TheFabricantKapers.collectionId = collectionId
937 emit AdminSetCollectionId(collectionId: collectionId)
938 }
939
940 access(all)
941 fun setIsFreeMintActive(isActive: Bool){
942 TheFabricantKapers.isFreeMintActive = isActive
943 emit AdminSetIsFreeMintActive(isActive: isActive)
944 }
945
946 //NOTE: Customise
947 // mint not:
948 // maxSupply has been hit √
949 // minting isn't open (!isOpen) √
950 // combo has been minted √
951 // mint if:
952 // openAccess √
953 // OR address on access list √
954 // Output:
955 // NFT √
956 // nftMetadata √
957 // update mints per address √
958 //NOTE: !Used for CC payments via MoonPay!
959 access(all)
960 fun distributeDirectlyViaAccessList(receiver: &{NonFungibleToken.CollectionPublic}, publicMinterPathString: String, garmentId: UInt64, materialId: UInt64, primaryColorId: UInt64, secondaryColorId: UInt64){
961 pre{
962 TheFabricantKapers.paymentReceiverCap != nil:
963 "Payment Receiver Cap must be set for minting!"
964 TheFabricantKapers.isFreeMintActive != true:
965 "Cannot mint via CC if free mint is active, please use mintUsingAccessList"
966 !TheFabricantKapers.dataAllocations.containsKey(TheFabricantKapers.constructDataAllocationString(garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)):
967 "Combination of characteristics already exists, please choose again"
968 }
969
970 // Ensure that the maximum supply of nfts for this contract has not been hit
971 if TheFabricantKapers.maxSupply != nil{
972 assert(TheFabricantKapers.totalSupply + 1 <= TheFabricantKapers.maxSupply!, message: "Max supply for NFTs has been hit")
973 }
974
975 // Get the publicMinter details so we can apply all the correct props to the NFT
976 //NOTE: Therefore relies on a pM having been created
977 let publicPath = PublicPath(identifier: publicMinterPathString) ?? panic("Failed to construct public path from path string: ".concat(publicMinterPathString))
978 let publicMinterCap = getAccount((self.owner!).address).capabilities.get<&TheFabricantKapers.PublicMinter>(publicPath).borrow() ?? panic("Couldn't get publicMinter ref or pathString is wrong: ".concat(publicMinterPathString))
979 let publicMinterDetails = publicMinterCap.getPublicMinterDetails()
980
981 //Confirm that minting is open on the publicMinter
982 let isOpen = publicMinterDetails["isOpen"] as! Bool?
983 assert(isOpen!, message: "Minting is not open!")
984 let isOpenAccess = publicMinterDetails["isOpenAccess"] as! Bool?
985 let accessListId = publicMinterDetails["accessListId"] as! UInt64?
986
987 // Check that it is NOT openAccess and free mint simultaneously
988 if isOpenAccess! && TheFabricantKapers.isFreeMintActive{
989 panic("There is no public free mint for this collection, free mint is for presale")
990 }
991
992 //Check that the address has access via the access list. If isOpenAccess, then anyone can mint.
993 if !isOpenAccess!{
994 assert(TheFabricantAccessList.checkAccessForAddress(accessListDetailsId: accessListId!, address: (receiver.owner!).address), message: "User address is not on the access list and so cannot mint.")
995 }
996
997 // Create the NFT
998 let license = publicMinterDetails["license"] as! MetadataViews.License?
999 let nft <- create NFT(originalRecipient: (receiver.owner!).address, license: license)
1000 let name = publicMinterDetails["name"] as! String?
1001 let collection = publicMinterDetails["collection"] as! String?
1002 let externalURL = publicMinterDetails["externalURL"] as! MetadataViews.ExternalURL?
1003 let coCreatable = publicMinterDetails["coCreatable"] as! Bool?
1004 let revealableTraits = publicMinterDetails["revealableTraits"] as!{ String: Bool}?
1005
1006 //garment.desc is to be used for the NFT desc
1007 let garment = TheFabricantKapers.garments[garmentId]
1008 let garmentValue = (garment!).value as!{ String: String}
1009 let garmentDescription = (garment!).characteristicDescription
1010
1011 // -- External Royalties
1012 let externalPublicMinterRoyalties = publicMinterDetails["royalties"] as! MetadataViews.Royalties?
1013 let externalRoyaltiesArray = (externalPublicMinterRoyalties!).getRoyalties()
1014 let externalRoyalties = MetadataViews.Royalties(externalRoyaltiesArray)
1015
1016 //-- TF MP Royalties
1017 // NOTE: Internal royalties are not added to as there is no CoCreator royalty for this collection
1018 let internalPublicMinterRoyalties = publicMinterDetails["royaltiesTFMarketplace"] as! TheFabricantMetadataViewsV2.Royalties?
1019 let internalPublicMinterRoyaltiesArray = (internalPublicMinterRoyalties!).getRoyalties()
1020 let internalRoyalties = TheFabricantMetadataViewsV2.Royalties(internalPublicMinterRoyaltiesArray)
1021
1022 // -- Characteristics
1023
1024 // Create Characteristics struct
1025 let characteristics = TheFabricantKapers.createCharacteristics(garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)
1026
1027 //Update data allocation. This prevents people from minting the same combination
1028 TheFabricantKapers.updateDataAllocations(nftId: nft.id, garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)
1029
1030 //Create the nftMetadata
1031 TheFabricantKapers.createNftMetadata(id: nft.id, nftUuid: nft.uuid, name: name!, description: garmentDescription, collection: collection!, characteristics: characteristics, license: nft.license, externalURL: externalURL!, coCreatable: coCreatable!, coCreator: (receiver.owner!).address, editionNumber: nft.editionNumber, maxEditionNumber: nft.maxEditionNumber, revealableTraits: revealableTraits!, royalties: externalRoyalties, royaltiesTFMarketplace: internalRoyalties)
1032
1033 //NOTE: Event is emitted here and not in nft init because
1034 // data is split between RevealableMetadata and nft,
1035 // so not all event data is accessible during nft init
1036 emit ItemMintedAndTransferred(uuid: nft.uuid, id: nft.id, name: (TheFabricantKapers.nftMetadata[nft.nftMetadataId]!).name, description: (TheFabricantKapers.nftMetadata[nft.nftMetadataId]!).description, collection: (TheFabricantKapers.nftMetadata[nft.nftMetadataId]!).collection, editionNumber: nft.editionNumber, originalRecipient: nft.originalRecipient, license: nft.license, nftMetadataId: nft.nftMetadataId)
1037 emit DataAllocationCreated(uuid: nft.uuid, id: nft.id, editionNumber: nft.editionNumber, originalRecipient: nft.originalRecipient, nftMetadataId: nft.nftMetadataId, dataAllocationString: TheFabricantKapers.idsToDataAllocations[nft.nftMetadataId]!)
1038 receiver.deposit(token: <-nft)
1039
1040 // Increment the number of mints that an address has
1041 if TheFabricantKapers.addressMintCount[(receiver.owner!).address] != nil{
1042 TheFabricantKapers.addressMintCount[(receiver.owner!).address] = TheFabricantKapers.addressMintCount[(receiver.owner!).address]! + 1
1043 } else{
1044 TheFabricantKapers.addressMintCount[(receiver.owner!).address] = 1
1045 }
1046 }
1047
1048 // NOTE: It is in the public minter that you would create the restrictions
1049 // for minting.
1050 access(all)
1051 fun createPublicMinter(name: String, description: String, collection: String, license: MetadataViews.License?, externalURL: MetadataViews.ExternalURL, coCreatable: Bool, revealableTraits:{ String: Bool}, minterMintLimit: UInt64?, royalties: MetadataViews.Royalties, royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties, paymentAmount: UFix64, paymentType: Type, paymentSplit: MetadataViews.Royalties?, typeRestrictions: [Type], accessListId: UInt64){
1052 pre{
1053 TheFabricantKapers.baseTokenURI != nil:
1054 "Please set a baseURI before creating a minter"
1055 TheFabricantKapers.materials.length != 0:
1056 "Please add materials to the contract"
1057 TheFabricantKapers.garments.length != 0:
1058 "Please add garments to the contract"
1059 TheFabricantKapers.secondaryColors.length != 0:
1060 "Please add secondaryColors to the contract"
1061 TheFabricantKapers.primaryColors.length != 0:
1062 "Please add primaryColors to the contract"
1063 }
1064 let publicMinter: @TheFabricantKapers.PublicMinter <- create PublicMinter(name: name, description: description, collection: collection, license: license, externalURL: externalURL, coCreatable: coCreatable, revealableTraits: revealableTraits, minterMintLimit: minterMintLimit, royalties: royalties, royaltiesTFMarketplace: royaltiesTFMarketplace, paymentAmount: paymentAmount, paymentType: paymentType, paymentSplit: paymentSplit, typeRestrictions: typeRestrictions, accessListId: accessListId)
1065
1066 // Save path: name_collection_uuid
1067 // Link the Public Minter to a Public Path of the admin account
1068 let publicMinterStoragePath = StoragePath(identifier: publicMinter.path)
1069 let publicMinterPublicPath = PublicPath(identifier: publicMinter.path)
1070 TheFabricantKapers.account.storage.save(<-publicMinter, to: publicMinterStoragePath!)
1071 }
1072
1073 access(all)
1074 fun revealTraits(nftMetadataId: UInt64, traits: [{RevealableV2.RevealableTrait}]){
1075 let nftMetadata = TheFabricantKapers.nftMetadata[nftMetadataId]! as! TheFabricantKapers.RevealableMetadata
1076 nftMetadata.revealTraits(traits: traits)
1077 TheFabricantKapers.nftMetadata[nftMetadataId] = nftMetadata
1078
1079 // Event should be emitted in resource, not struct
1080 var i = 1
1081 while i < traits.length{
1082 let traitName = traits[i].name
1083 let traitValue = traits[i].value
1084 emit TraitRevealed(nftUuid: nftMetadata.nftUuid, id: nftMetadata.id, trait: traitName)
1085 i = i + 1
1086 }
1087 emit ItemRevealed(uuid: nftMetadata.nftUuid, id: nftMetadata.id, name: nftMetadata.name, description: nftMetadata.description, collection: nftMetadata.collection, editionNumber: nftMetadata.editionNumber, originalRecipient: nftMetadata.coCreator, license: nftMetadata.license, nftMetadataId: nftMetadata.id, externalURL: nftMetadata.externalURL, coCreatable: nftMetadata.coCreatable, coCreator: nftMetadata.coCreator)
1088 }
1089
1090 // NOTE: Customise
1091 access(all)
1092 fun updateGarments(shapes:{ UInt64:{ CoCreatableV2.Characteristic}}){
1093 var i: UInt64 = 0
1094 let keys = shapes.keys
1095 let values = shapes.values
1096 while i < UInt64(shapes.length){
1097 TheFabricantKapers.garments[keys[i]] = values[i]
1098 i = i + 1
1099 }
1100 }
1101
1102 // NOTE: Customise
1103 access(all)
1104 fun emptyGarments(){
1105 TheFabricantKapers.garments ={}
1106 }
1107
1108 // NOTE: Customise
1109 access(all)
1110 fun updateMaterials(materials:{ UInt64:{ CoCreatableV2.Characteristic}}){
1111 var i: UInt64 = 0
1112 let keys = materials.keys
1113 let values = materials.values
1114 while i < UInt64(materials.length){
1115 TheFabricantKapers.materials[keys[i]] = values[i]
1116 i = i + 1
1117 }
1118 }
1119
1120 // NOTE: Customise
1121 access(all)
1122 fun emptyMaterials(){
1123 TheFabricantKapers.materials ={}
1124 }
1125
1126 // NOTE: Customise
1127 access(all)
1128 fun updatePrimaryColors(primaryColors:{ UInt64:{ CoCreatableV2.Characteristic}}){
1129 var i: UInt64 = 0
1130 let keys = primaryColors.keys
1131 let values = primaryColors.values
1132 while i < UInt64(primaryColors.length){
1133 TheFabricantKapers.primaryColors[keys[i]] = values[i]
1134 i = i + 1
1135 }
1136 }
1137
1138 // NOTE: Customise
1139 access(all)
1140 fun emptyPrimaryColors(){
1141 TheFabricantKapers.primaryColors ={}
1142 }
1143
1144 // NOTE: Customise
1145 access(all)
1146 fun updateSecondaryColors(secondaryColors:{ UInt64:{ CoCreatableV2.Characteristic}}){
1147 var i: UInt64 = 0
1148 let keys = secondaryColors.keys
1149 let values = secondaryColors.values
1150 while i < UInt64(secondaryColors.length){
1151 TheFabricantKapers.secondaryColors[keys[i]] = values[i]
1152 i = i + 1
1153 }
1154 }
1155
1156 // NOTE: Customise
1157 access(all)
1158 fun emptySecondaryColors(){
1159 TheFabricantKapers.secondaryColors ={}
1160 }
1161
1162 init(adminAddress: Address){
1163 emit AdminResourceCreated(uuid: self.uuid, adminAddress: adminAddress)
1164 }
1165 }
1166
1167 // -----------------------------------------------------------------------
1168 // PublicMinter Resource
1169 // -----------------------------------------------------------------------
1170 // NOTE: The public minter is exposed via a capability to allow the public
1171 // to mint the NFT so long as they meet the criteria.
1172 // It is in the public minter that the various mint functions would be exposed
1173 // such as paid mint etc.
1174 // Every contract has to manage its own minting via the PublicMinter.
1175 //NOTE: Customise
1176 // Update the mint functions
1177 access(all)
1178 resource interface Minter{
1179 access(all)
1180 fun mintUsingAccessList(receiver: &{NonFungibleToken.CollectionPublic}, payment: @{FungibleToken.Vault}, garmentId: UInt64, materialId: UInt64, primaryColorId: UInt64, secondaryColorId: UInt64)
1181
1182 access(all)
1183 fun getPublicMinterDetails():{ String: AnyStruct}
1184 }
1185
1186 access(all)
1187 resource PublicMinter: TheFabricantNFTStandardV2.TFNFTPublicMinter, Minter{
1188 access(all)
1189 var path: String
1190
1191 access(all)
1192 var isOpen: Bool
1193
1194 access(all)
1195 var isAccessListOnly: Bool
1196
1197 access(all)
1198 var isOpenAccess: Bool
1199
1200 // NOTE: Remove these as required and update the NFT props and
1201 // resolveView to reflect this, so that views that this nft
1202 // does not display are not provided
1203 // Name of nft, not campaign. This will be combined with the edition number
1204 access(all)
1205 let name: String
1206
1207 access(all)
1208 let description: String
1209
1210 access(all)
1211 let collection: String
1212
1213 access(all)
1214 let license: MetadataViews.License?
1215
1216 access(all)
1217 let externalURL: MetadataViews.ExternalURL
1218
1219 access(all)
1220 let coCreatable: Bool
1221
1222 access(all)
1223 let revealableTraits:{ String: Bool}
1224
1225 // NOTE: The max number of mints this pM can do (eg multiple NFTs, a different minter for each one. Each NFT has a max number of mints allowed).
1226 access(all)
1227 var minterMintLimit: UInt64?
1228
1229 access(all)
1230 var numberOfMints: UInt64
1231
1232 access(all)
1233 let royalties: MetadataViews.Royalties
1234
1235 access(all)
1236 let royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties
1237
1238 access(all)
1239 var paymentAmount: UFix64
1240
1241 access(all)
1242 let paymentType: Type
1243
1244 // paymentSplit: How much each address gets paid on minting of NFT
1245 access(all)
1246 let paymentSplit: MetadataViews.Royalties?
1247
1248 access(all)
1249 var typeRestrictions: [Type]?
1250
1251 access(all)
1252 var accessListId: UInt64
1253
1254 access(all)
1255 fun changeIsOpenAccess(isOpenAccess: Bool){
1256 self.isOpenAccess = isOpenAccess
1257 emit PublicMinterIsOpenAccessChanged(uuid: self.uuid, name: self.name, description: self.description, collection: self.collection, path: self.path, isOpenAccess: self.isOpenAccess, isAccessListOnly: self.isAccessListOnly, isOpen: self.isOpen)
1258 }
1259
1260 access(all)
1261 fun changeIsAccessListOnly(isAccessListOnly: Bool){
1262 self.isAccessListOnly = isAccessListOnly
1263 emit PublicMinterIsAccessListOnly(uuid: self.uuid, name: self.name, description: self.description, collection: self.collection, path: self.path, isOpenAccess: self.isOpenAccess, isAccessListOnly: self.isAccessListOnly, isOpen: self.isOpen)
1264 }
1265
1266 access(all)
1267 fun changeMintingIsOpen(isOpen: Bool){
1268 self.isOpen = isOpen
1269 emit PublicMinterMintingIsOpen(uuid: self.uuid, name: self.name, description: self.description, collection: self.collection, path: self.path, isOpenAccess: self.isOpenAccess, isAccessListOnly: self.isAccessListOnly, isOpen: self.isOpen)
1270 }
1271
1272 access(all)
1273 fun setAccessListId(accessListId: UInt64){
1274 self.accessListId = accessListId
1275 emit PublicMinterSetAccessListId(uuid: self.uuid, name: self.name, description: self.description, collection: self.collection, path: self.path, isOpenAccess: self.isOpenAccess, isAccessListOnly: self.isAccessListOnly, isOpen: self.isOpen, accessListId: self.accessListId)
1276 }
1277
1278 access(all)
1279 fun setPaymentAmount(amount: UFix64){
1280 self.paymentAmount = amount
1281 emit PublicMinterSetPaymentAmount(uuid: self.uuid, name: self.name, description: self.description, collection: self.collection, path: self.path, isOpenAccess: self.isOpenAccess, isAccessListOnly: self.isAccessListOnly, isOpen: self.isOpen, paymentAmount: self.paymentAmount)
1282 }
1283
1284 access(all)
1285 fun setMinterMintLimit(minterMintLimit: UInt64){
1286 self.minterMintLimit = minterMintLimit
1287 emit PublicMinterSetMinterMintLimit(uuid: self.uuid, name: self.name, description: self.description, collection: self.collection, path: self.path, isOpenAccess: self.isOpenAccess, isAccessListOnly: self.isAccessListOnly, isOpen: self.isOpen, minterMintLimit: self.minterMintLimit)
1288 }
1289
1290 // The owner of the pM can access this via borrow in tx.
1291 access(all)
1292 fun updateTypeRestrictions(types: [Type]){
1293 self.typeRestrictions = types
1294 }
1295
1296 //NOTE: Customise
1297 // mint not:
1298 // maxMint for this address has been hit √
1299 // maxSupply has been hit √
1300 // minting isn't open (!isOpen) √
1301 // payment is insufficient √
1302 // maxSupply is hit √
1303 // minterMintLimit is hit √
1304 // mint if:
1305 // openAccess √
1306 // OR address on access list √
1307 // Output:
1308 // NFT √
1309 // nftMetadata √
1310 // Characteristics √
1311 // adds to dataAllocations dicts √
1312 // update mints per address √
1313 access(all)
1314 fun mintUsingAccessList(receiver: &{NonFungibleToken.CollectionPublic}, payment: @{FungibleToken.Vault}, garmentId: UInt64, materialId: UInt64, primaryColorId: UInt64, secondaryColorId: UInt64){
1315 pre{
1316 self.isOpen:
1317 "Minting is not currently open!"
1318 !TheFabricantKapers.dataAllocations.containsKey(TheFabricantKapers.constructDataAllocationString(garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)):
1319 "Combination of characteristics already exists, please choose again"
1320 payment.isInstance(self.paymentType):
1321 "payment vault is not requested fungible token"
1322 payment.balance == (TheFabricantKapers.isFreeMintActive ? 0.0 : self.paymentAmount):
1323 "Incorrect payment amount provided for minting"
1324 TheFabricantKapers.paymentReceiverCap != nil:
1325 "Payment Receiver Cap must be set for minting!"
1326 }
1327 post{
1328 receiver.getIDs().length == before(receiver.getIDs().length) + 1:
1329 "Minted NFT must be deposited into Collection"
1330 }
1331 // Total number of mints by this pM
1332 self.numberOfMints = self.numberOfMints + 1
1333
1334 // Free mint checks
1335 if TheFabricantKapers.isFreeMintActive{
1336 assert(payment.balance == 0.0, message: "Payment amount must be 0.0 for free mint")
1337 assert(!TheFabricantKapers.claimedFreeMints.containsKey((receiver.owner!).address), message: "Only one free mint per address is allowed")
1338 TheFabricantKapers.claimedFreeMints[(receiver.owner!).address] = TheFabricantKapers.totalSupply + 1
1339 }
1340
1341 // Ensure that minterMintLimit for this pM has not been hit
1342 if self.minterMintLimit != nil{
1343 assert(self.numberOfMints <= self.minterMintLimit!, message: "Maximum number of mints for this public minter has been hit")
1344 }
1345
1346 // Ensure that the maximum supply of nfts for this contract has not been hit
1347 if TheFabricantKapers.maxSupply != nil{
1348 assert(TheFabricantKapers.totalSupply + 1 <= TheFabricantKapers.maxSupply!, message: "Max supply for NFTs has been hit")
1349 }
1350
1351 // Ensure user hasn't minted more NFTs from this contract than allowed
1352 if TheFabricantKapers.addressMintLimit != nil{
1353 if TheFabricantKapers.addressMintCount[(receiver.owner!).address] != nil{
1354 assert(TheFabricantKapers.addressMintCount[(receiver.owner!).address]! < TheFabricantKapers.addressMintLimit!, message: "User has already minted the maximum allowance per address!")
1355 }
1356 }
1357
1358 // Check that it is NOT openAccess and free mint simultaneously
1359 if self.isOpenAccess && TheFabricantKapers.isFreeMintActive{
1360 panic("There is no public free mint for this collection, free mint is for presale")
1361 }
1362
1363 // Check that the address has access via the access list. If isOpenAccess, then anyone can mint.
1364 if !self.isOpenAccess{
1365 assert(TheFabricantAccessList.checkAccessForAddress(accessListDetailsId: self.accessListId, address: (receiver.owner!).address), message: "User address is not on the access list and so cannot mint.")
1366 }
1367
1368 // Settle Payment
1369 if let _paymentSplit = self.paymentSplit{
1370 var i = 0
1371 let splits = _paymentSplit.getRoyalties()
1372 while i < splits.length{
1373 // If it is a free mint, skip allocating payment splits
1374 if payment.balance == 0.0 && TheFabricantKapers.isFreeMintActive{
1375 i = i + 1
1376 break
1377 }
1378 let split = splits[i]
1379 let receiver = split.receiver
1380 let cut = split.cut
1381 let paymentAmount = self.paymentAmount * cut
1382 if let wallet = receiver.borrow(){
1383 let pay <- payment.withdraw(amount: paymentAmount)
1384 emit MintPaymentSplitDeposited(address: (wallet.owner!).address, price: self.paymentAmount, amount: pay.balance, nftUuid: self.uuid)
1385 wallet.deposit(from: <-pay)
1386 }
1387 i = i + 1
1388 }
1389 }
1390 if payment.balance != 0.0 || payment.balance == 0.0{
1391 // pay rest to TF
1392 emit MintPaymentSplitDeposited(address: (TheFabricantKapers.paymentReceiverCap!).address, price: self.paymentAmount, amount: payment.balance, nftUuid: self.uuid)
1393 }
1394 (( // Deposit has to occur outside of above if statement as resource must be moved or destroyed
1395 TheFabricantKapers.paymentReceiverCap!).borrow()!).deposit(from: <-payment)
1396 let nft <- create NFT(originalRecipient: (receiver.owner!).address, license: self.license)
1397
1398 //garment.desc is to be used for the NFT desc
1399 let garment = TheFabricantKapers.garments[garmentId]
1400 let garmentValue = (garment!).value as!{ String: String}
1401 let garmentDescription = (garment!).characteristicDescription
1402
1403 // - External Royalties
1404 let externalPublicMinterRoyalties = self.royalties.getRoyalties()
1405 let externalRoyalties = MetadataViews.Royalties(externalPublicMinterRoyalties)
1406
1407 // -- TF Internal Royalties
1408 // NOTE: Internal royalties are not added to as there is no CoCreator royalty for this collection
1409 let internalPublicMinterRoyalties = self.royaltiesTFMarketplace.getRoyalties()
1410 let internalRoyalties = TheFabricantMetadataViewsV2.Royalties( internalPublicMinterRoyalties)
1411
1412 //-- Characteristics
1413
1414 // Create Characteristics struct
1415 let characteristics = TheFabricantKapers.createCharacteristics(garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)
1416
1417 //Update data allocation. This prevents people from minting the same combination
1418 TheFabricantKapers.updateDataAllocations(nftId: nft.id, garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)
1419 TheFabricantKapers.createNftMetadata(id: nft.id, nftUuid: nft.uuid, name: self.name, description: garmentDescription, collection: self.collection, characteristics: characteristics, license: nft.license, externalURL: self.externalURL, coCreatable: self.coCreatable, coCreator: (receiver.owner!).address, editionNumber: nft.editionNumber, maxEditionNumber: nft.maxEditionNumber, revealableTraits: self.revealableTraits, royalties: externalRoyalties, royaltiesTFMarketplace: internalRoyalties)
1420
1421 //NOTE: Event is emitted here and not in nft init because
1422 // data is split between RevealableMetadata and nft,
1423 // so not all event data is accessible during nft init
1424 emit ItemMintedAndTransferred(uuid: nft.uuid, id: nft.id, name: (TheFabricantKapers.nftMetadata[nft.nftMetadataId]!).name, description: (TheFabricantKapers.nftMetadata[nft.nftMetadataId]!).description, collection: (TheFabricantKapers.nftMetadata[nft.nftMetadataId]!).collection, editionNumber: nft.editionNumber, originalRecipient: nft.originalRecipient, license: self.license, nftMetadataId: nft.nftMetadataId)
1425 emit DataAllocationCreated(uuid: nft.uuid, id: nft.id, editionNumber: nft.editionNumber, originalRecipient: nft.originalRecipient, nftMetadataId: nft.nftMetadataId, dataAllocationString: TheFabricantKapers.idsToDataAllocations[nft.nftMetadataId]!)
1426 receiver.deposit(token: <-nft)
1427
1428 // Increment the number of mints that an address has
1429 if TheFabricantKapers.addressMintCount[(receiver.owner!).address] != nil{
1430 TheFabricantKapers.addressMintCount[(receiver.owner!).address] = TheFabricantKapers.addressMintCount[(receiver.owner!).address]! + 1
1431 } else{
1432 TheFabricantKapers.addressMintCount[(receiver.owner!).address] = 1
1433 }
1434 }
1435
1436 access(all)
1437 fun getPublicMinterDetails():{ String: AnyStruct}{
1438 let ret:{ String: AnyStruct} ={}
1439 ret["name"] = self.name
1440 ret["uuid"] = self.uuid
1441 ret["path"] = self.path
1442 ret["isOpen"] = self.isOpen
1443 ret["isAccessListOnly"] = self.isAccessListOnly
1444 ret["isOpenAccess"] = self.isOpenAccess
1445 ret["description"] = self.description
1446 ret["collection"] = self.collection
1447 ret["collectionId"] = TheFabricantKapers.collectionId
1448 ret["license"] = self.license
1449 ret["externalURL"] = self.externalURL
1450 ret["coCreatable"] = self.coCreatable
1451 ret["revealableTraits"] = self.revealableTraits
1452 ret["minterMintLimit"] = self.minterMintLimit
1453 ret["numberOfMints"] = self.numberOfMints
1454 ret["royalties"] = self.royalties
1455 ret["royaltiesTFMarketplace"] = self.royaltiesTFMarketplace
1456 ret["paymentAmount"] = self.paymentAmount
1457 ret["paymentType"] = self.paymentType
1458 ret["paymentSplit"] = self.paymentSplit
1459 ret["typeRestrictions"] = self.typeRestrictions
1460 ret["accessListId"] = self.accessListId
1461 return ret
1462 }
1463
1464 init(name: String, description: String, collection: String, license: MetadataViews.License?, externalURL: MetadataViews.ExternalURL, coCreatable: Bool, revealableTraits:{ String: Bool}, minterMintLimit: UInt64?, royalties: MetadataViews.Royalties, royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties, paymentAmount: UFix64, paymentType: Type, paymentSplit: MetadataViews.Royalties?, typeRestrictions: [Type], accessListId: UInt64){
1465
1466 // Create and save path: name_collection_uuid
1467 let pathString = "TheFabricantNFTPublicMinter_TheFabricantKapers_".concat(self.uuid.toString())
1468 TheFabricantKapers.publicMinterPaths[self.uuid] = pathString
1469 self.path = pathString
1470 self.isOpen = false
1471 self.isAccessListOnly = true
1472 self.isOpenAccess = false
1473 self.name = name
1474 self.description = description
1475 self.collection = collection
1476 self.license = license
1477 self.externalURL = externalURL
1478 self.coCreatable = coCreatable
1479 self.revealableTraits = revealableTraits
1480 self.minterMintLimit = minterMintLimit
1481 self.numberOfMints = 0
1482 self.royalties = royalties
1483 self.royaltiesTFMarketplace = royaltiesTFMarketplace
1484 self.paymentAmount = paymentAmount
1485 self.paymentType = paymentType
1486 self.paymentSplit = paymentSplit
1487 self.typeRestrictions = typeRestrictions
1488 self.accessListId = accessListId
1489 emit PublicMinterCreated(uuid: self.uuid, name: name, description: description, collection: collection, path: self.path)
1490 }
1491 }
1492
1493 // -----------------------------------------------------------------------
1494 // Private Utility Functions
1495 // -----------------------------------------------------------------------
1496 //NOTE: Customise
1497 // This function generates the metadata for the minted nft.
1498 access(contract)
1499 fun createNftMetadata(id: UInt64, nftUuid: UInt64, name: String, description: String, collection: String, characteristics:{ String:{ CoCreatableV2.Characteristic}}, license: MetadataViews.License?, externalURL: MetadataViews.ExternalURL, coCreatable: Bool, coCreator: Address, editionNumber: UInt64, maxEditionNumber: UInt64?, revealableTraits:{ String: Bool}, royalties: MetadataViews.Royalties, royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties){
1500 pre{
1501 TheFabricantKapers.baseTokenURI != nil:
1502 "Ensure baseUri is set before minting!"
1503 }
1504
1505 //Instant reveal
1506 let dataAllocation = TheFabricantKapers.idsToDataAllocations[id]!
1507 let metadata ={ "mainImage": (TheFabricantKapers.baseTokenURI!).concat("/").concat(dataAllocation).concat(".png"), "video": (TheFabricantKapers.baseTokenURI!).concat("/").concat(dataAllocation).concat(".mp4")}
1508 let mD = RevealableMetadata(id: id, nftUuid: nftUuid, name: name, description: description, collection: collection, metadata: metadata, characteristics: characteristics, license: license, externalURL: externalURL, coCreatable: coCreatable, coCreator: coCreator, editionNumber: editionNumber, maxEditionNumber: maxEditionNumber, revealableTraits: revealableTraits, royalties: royalties, royaltiesTFMarketplace: royaltiesTFMarketplace)
1509 TheFabricantKapers.nftMetadata[id] = mD
1510 }
1511
1512 //NOTE: Customise
1513 // This function generates the characteristics data for the minted nft.
1514 access(contract)
1515 fun createCharacteristics(garmentId: UInt64, materialId: UInt64, primaryColorId: UInt64, secondaryColorId: UInt64):{ String:{ CoCreatableV2.Characteristic}}{
1516 let dict:{ String:{ CoCreatableV2.Characteristic}} ={}
1517 let garment = TheFabricantKapers.garments[garmentId] ?? panic("A garment with this id does not exist")
1518 dict["garment"] = garment
1519 let material = TheFabricantKapers.materials[materialId] ?? panic("A material with this id does not exist")
1520 dict["material"] = material
1521 let primaryColor = TheFabricantKapers.primaryColors[primaryColorId] ?? panic("A primary color with this id does not exist")
1522 dict["primaryColor"] = primaryColor
1523 let secondaryColor = TheFabricantKapers.secondaryColors[secondaryColorId] ?? panic("A secondary color with this id does not exist")
1524 dict["secondaryColor"] = secondaryColor
1525 return dict
1526 }
1527
1528 access(self)
1529 fun nftsCanBeUsedForMint(receiver: &{NonFungibleToken.CollectionPublic}, refs: [&{NonFungibleToken.NFT}], typeRestrictions: [Type]): Bool{
1530 assert(typeRestrictions.length != 0, message: "There are no typerestrictions for this promotion")
1531 var i = 0
1532 while i < refs.length{
1533 if typeRestrictions.contains(refs[i].getType()) && (receiver.owner!).address == (refs[i].owner!).address{
1534 return true
1535 }
1536 i = i + 1
1537 }
1538 return false
1539 }
1540
1541 // Used to create the string Ids for checking if combo is taken
1542 // garment_material_primaryColor_secondaryColor
1543 access(contract)
1544 view fun constructDataAllocationString(garmentId: UInt64, materialId: UInt64, primaryColorId: UInt64, secondaryColorId: UInt64): String{
1545 return "gar".concat(garmentId.toString()).concat("_mat").concat(materialId.toString()).concat("_primary").concat(primaryColorId.toString()).concat("_secondary").concat(secondaryColorId.toString())
1546 }
1547
1548 access(contract)
1549 fun updateDataAllocations(nftId: UInt64, garmentId: UInt64, materialId: UInt64, primaryColorId: UInt64, secondaryColorId: UInt64){
1550 let allocationString = self.constructDataAllocationString(garmentId: garmentId, materialId: materialId, primaryColorId: primaryColorId, secondaryColorId: secondaryColorId)
1551 self.dataAllocations[allocationString] = nftId
1552 self.idsToDataAllocations[nftId] = allocationString
1553 }
1554
1555 // -----------------------------------------------------------------------
1556 // Public Utility Functions
1557 // -----------------------------------------------------------------------
1558 // createEmptyCollection creates an empty Collection
1559 // and returns it to the caller so that they can own NFTs
1560 access(all)
1561 fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{
1562 return <-create Collection()
1563 }
1564
1565 access(all)
1566 fun getPublicMinterPaths():{ UInt64: String}{
1567 return TheFabricantKapers.publicMinterPaths
1568 }
1569
1570 access(all)
1571 fun getNftIdsToOwner():{ UInt64: Address}{
1572 return TheFabricantKapers.nftIdsToOwner
1573 }
1574
1575 access(all)
1576 fun getMaxSupply(): UInt64?{
1577 return TheFabricantKapers.maxSupply
1578 }
1579
1580 access(all)
1581 fun getTotalSupply(): UInt64{
1582 return TheFabricantKapers.totalSupply
1583 }
1584
1585 access(all)
1586 fun getCollectionId(): String?{
1587 return TheFabricantKapers.collectionId
1588 }
1589
1590 access(all)
1591 fun getNftMetadatas():{ UInt64:{ RevealableV2.RevealableMetadata}}{
1592 return self.nftMetadata
1593 }
1594
1595 access(all)
1596 fun getDataAllocations():{ String: UInt64}{
1597 return self.dataAllocations
1598 }
1599
1600 access(all)
1601 fun getAllIdsToDataAllocations():{ UInt64: String}{
1602 return self.idsToDataAllocations
1603 }
1604
1605 access(all)
1606 fun getIdToDataAllocation(id: UInt64): String?{
1607 return self.idsToDataAllocations[id]
1608 }
1609
1610 access(all)
1611 fun getAllCharacteristics(): AnyStruct{
1612 let res ={ "garments": TheFabricantKapers.garments, "materials": TheFabricantKapers.materials, "primaryColors": TheFabricantKapers.primaryColors, "secondaryColors": TheFabricantKapers.secondaryColors}
1613 return res
1614 }
1615
1616 access(all)
1617 fun getBaseUri(): String?{
1618 return TheFabricantKapers.baseTokenURI
1619 }
1620
1621 access(all)
1622 fun getIsFreeMintActive(): Bool{
1623 return TheFabricantKapers.isFreeMintActive
1624 }
1625
1626 access(all)
1627 fun getPaymentCap(): Address?{
1628 return TheFabricantKapers.paymentReceiverCap?.address
1629 }
1630
1631 access(all)
1632 fun getClaimedFreeMints():{ Address: UInt64}{
1633 return TheFabricantKapers.claimedFreeMints
1634 }
1635
1636 access(all)
1637 view fun getContractViews(resourceType: Type?): [Type]{
1638 return []
1639 }
1640
1641 access(all)
1642 view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct?{
1643 return nil
1644 }
1645
1646 // -----------------------------------------------------------------------
1647 // Contract Init
1648 // -----------------------------------------------------------------------
1649 init(){
1650 self.totalSupply = 0
1651 self.maxSupply = nil
1652 self.publicMinterPaths ={}
1653 self.collectionId = nil
1654 self.nftIdsToOwner ={}
1655 self.addressMintCount ={}
1656 self.paymentReceiverCap = nil
1657 self.nftMetadata ={}
1658 self.dataAllocations ={}
1659 self.idsToDataAllocations ={}
1660 self.addressMintLimit = nil
1661 self.garments ={}
1662 self.materials ={}
1663 self.primaryColors ={}
1664 self.secondaryColors ={}
1665 self.baseTokenURI = nil
1666 self.isFreeMintActive = false
1667 self.claimedFreeMints ={}
1668 self.TheFabricantKapersCollectionStoragePath = /storage/TheFabricantKapersCollectionStoragePath
1669 self.TheFabricantKapersCollectionPublicPath = /public/TheFabricantKapersCollectionPublicPath
1670 self.TheFabricantKapersProviderPath = /private/TheFabricantKapersProviderPath
1671 self.TheFabricantKapersAdminStoragePath = /storage/TheFabricantKapersAdminStoragePath
1672 self.TheFabricantKapersPublicMinterStoragePath = /storage/TheFabricantKapersPublicMinterStoragePath
1673 self.TheFabricantKapersPublicMinterPublicPath = /public/TheFabricantKapersPublicMinterPublicPath
1674 self.account.storage.save(<-create Admin(adminAddress: self.account.address), to: self.TheFabricantKapersAdminStoragePath)
1675 emit ContractInitialized()
1676 }
1677}
1678