Smart Contract

TheFabricantKapers

A.7752ea736384322f.TheFabricantKapers

Deployed

3h ago
Feb 28, 2026, 08:14:46 PM UTC

Dependents

0 imports
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