Smart Contract

HighsnobietyNotInParis

A.7752ea736384322f.HighsnobietyNotInParis

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
19access(all)
20contract HighsnobietyNotInParis: NonFungibleToken, TheFabricantNFTStandardV2, RevealableV2{ 
21	
22	// -----------------------------------------------------------------------
23	// Paths
24	// -----------------------------------------------------------------------
25	access(all)
26	let HighsnobietyNotInParisCollectionStoragePath: StoragePath
27	
28	access(all)
29	let HighsnobietyNotInParisCollectionPublicPath: PublicPath
30	
31	access(all)
32	let HighsnobietyNotInParisProviderStoragePath: PrivatePath
33	
34	access(all)
35	let HighsnobietyNotInParisPublicMinterStoragePath: StoragePath
36	
37	access(all)
38	let HighsnobietyNotInParisAdminStoragePath: StoragePath
39	
40	access(all)
41	let HighsnobietyNotInParisPublicMinterPublicPath: PublicPath
42	
43	// -----------------------------------------------------------------------
44	// Contract Events
45	// -----------------------------------------------------------------------
46	// Event that emitted when the NFT contract is initialized
47	//
48	access(all)
49	event ContractInitialized()
50	
51	access(all)
52	event ItemMintedAndTransferred(uuid: UInt64, id: UInt64, name: String, description: String, collection: String, editionNumber: UInt64, originalRecipient: Address, license: MetadataViews.License?, nftMetadataId: UInt64)
53	
54	access(all)
55	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)
56	
57	access(all)
58	event TraitRevealed(nftUuid: UInt64, id: UInt64, trait: String)
59	
60	access(all)
61	event IsTraitRevealableV2Updated(nftUuid: UInt64, id: UInt64, trait: String, isRevealableV2: Bool)
62	
63	access(all)
64	event MintPaymentSplitDeposited(address: Address, price: UFix64, amount: UFix64, nftUuid: UInt64)
65	
66	access(all)
67	event ItemDestroyed(uuid: UInt64, id: UInt64, name: String, description: String, collection: String)
68	
69	access(all)
70	event PublicMinterCreated(uuid: UInt64, name: String, description: String, collection: String, path: String)
71	
72	access(all)
73	event PublicMinterIsOpenAccessChanged(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool)
74	
75	access(all)
76	event PublicMinterIsAccessListOnly(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool)
77	
78	access(all)
79	event PublicMinterMintingIsOpen(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool)
80	
81	access(all)
82	event PublicMinterSetAccessListId(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool, accessListId: UInt64)
83	
84	access(all)
85	event PublicMinterSetPaymentAmount(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool, paymentAmount: UFix64)
86	
87	access(all)
88	event PublicMinterSetMinterMintLimit(uuid: UInt64, name: String, description: String, collection: String, path: String, isOpenAccess: Bool, isAccessListOnly: Bool, isOpen: Bool, minterMintLimit: UInt64?)
89	
90	access(all)
91	event AdminResourceCreated(uuid: UInt64, adminAddress: Address)
92	
93	access(all)
94	event AdminPaymentReceiverCapabilityChanged(address: Address, paymentType: Type)
95	
96	access(all)
97	event AdminSetMaxSupply(maxSupply: UInt64)
98	
99	access(all)
100	event AdminSetAddressMintLimit(addressMintLimit: UInt64)
101	
102	access(all)
103	event AdminSetCollectionId(collectionId: String)
104	
105	access(all)
106	event AdminSetBaseURI(baseURI: String)
107	
108	// Event that is emitted when a token is withdrawn,
109	// indicating the owner of the collection that it was withdrawn from.
110	//
111	// If the collection is not in an account's storage, `from` will be `nil`.
112	//
113	access(all)
114	event Withdraw(id: UInt64, from: Address?)
115	
116	// Event that emitted when a token is deposited to a collection.
117	//
118	// It indicates the owner of the collection that it was deposited to.
119	//
120	access(all)
121	event Deposit(id: UInt64, to: Address?)
122	
123	// -----------------------------------------------------------------------
124	// Contract State
125	// -----------------------------------------------------------------------
126	// NOTE: This is updated anywhere ownership of the nft is changed - on minting and therefore on deposit
127	access(contract)
128	var nftIdsToOwner:{ UInt64: Address}
129	
130	access(contract)
131	var publicMinterPaths:{ UInt64: String}
132	
133	// NOTE: this is contract-level so all minters can access it.
134	// Keeps track of the number of times an address has minted
135	access(contract)
136	var addressMintCount:{ Address: UInt64}
137	
138	// Receives payment for minting
139	access(contract)
140	var paymentReceiverCap: Capability<&{FungibleToken.Receiver}>?
141	
142	access(contract)
143	var nftMetadata:{ UInt64:{ RevealableV2.RevealableMetadata}}
144	
145	// The total number of tokens of this type in existence
146	// NOTE: All public minters use totalSupply to assign the next
147	// id and edition number. Each public minter has a minterMintLimit property
148	// that defines the max no. of mints a pM can do. 
149	access(all)
150	var totalSupply: UInt64
151	
152	// NOTE: The max number of NFTs in this collection that will ever be minted
153	// Init as nil if there is no max. 
154	access(all)
155	var maxSupply: UInt64?
156	
157	// NOTE: Max mints per address
158	access(all)
159	var addressMintLimit: UInt64?
160	
161	//NOTE: uuid of collection added to NFT and used by BE
162	access(all)
163	var collectionId: String?
164	
165	access(contract)
166	var baseTokenURI: String?
167	
168	// -----------------------------------------------------------------------
169	// RevealableV2 Metadata Struct
170	// -----------------------------------------------------------------------
171	access(all)
172	struct RevealableMetadata: RevealableV2.RevealableMetadata{ 
173		
174		//NOTE: totalSupply value of attached NFT, therefore edition number. 
175		access(all)
176		let id: UInt64
177		
178		// NOTE: !IMPORTANT! nftUuid is the uuid of the associated nft.
179		// This RevealableMetadata struct should be stored in the nftMetadata dict under this
180		// value. This is because the uuid is used across contracts for identification purposes
181		access(all)
182		let nftUuid: UInt64 // uuid of NFT
183		
184		
185		// NOTE: Name of NFT. 
186		// Will be combined with the edition number on the application
187		// Doesn't include the edition number.
188		access(all)
189		var name: String
190		
191		access(all)
192		var description: String //Display
193		
194		
195		// NOTE: Thumbnail, which is needed for the Display view, should be set using one of the
196		// media properties
197		//access(all) let thumbnail: String //Display
198		access(all)
199		let collection: String // Name of collection eg The Fabricant > Season 3 > Wholeland > XXories Originals
200		
201		
202		// Stores the metadata that describes this particular creation,
203		// but is not part of a characteristic eg mainImage, video etc
204		access(all)
205		var metadata:{ String: AnyStruct}
206		
207		// This is where the user-chosed characteristics live. This represents
208		// the data that in older contracts, would've been separate NFTs.		
209		access(all)
210		var characteristics:{ String:{ CoCreatableV2.Characteristic}}
211		
212		access(all)
213		var rarity: UFix64?
214		
215		access(all)
216		var rarityDescription: String?
217		
218		// NOTE: Media is not implemented in the struct because MetadataViews.Medias
219		// is not mutable, so can't be updated. In addition, each 
220		// NFT collection might have a different number of image/video properties.
221		// Instead, the NFT should implement a function that rolls up the props
222		// into a MetadataViews.Medias struct
223		//access(all) let media: MetadataViews.Medias //Media
224		access(all)
225		let license: MetadataViews.License? //License
226		
227		
228		access(all)
229		let externalURL: MetadataViews.ExternalURL //ExternalURL
230		
231		
232		access(all)
233		let coCreatable: Bool
234		
235		access(all)
236		let coCreator: Address
237		
238		access(all)
239		var isRevealed: Bool?
240		
241		// id and editionNumber might not be the same in the nft...
242		access(all)
243		let editionNumber: UInt64 //Edition
244		
245		
246		access(all)
247		let maxEditionNumber: UInt64?
248		
249		access(all)
250		let royalties: MetadataViews.Royalties //Royalty
251		
252		
253		access(all)
254		let royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties
255		
256		access(contract)
257		var revealableTraits:{ String: Bool}
258		
259		access(all)
260		fun getRevealableTraits():{ String: Bool}{ 
261			return self.revealableTraits
262		}
263		
264		//NOTE: Customise
265		//NOTE: This should be updated for each campaign contract!
266		// Called by the Admin to reveal the traits for this NFT.
267		// Should contain a switch function that knows how to modify
268		// the properties of this struct. Should check that the trait
269		// being revealed is allowed to be modified.
270		access(contract)
271		fun revealTraits(traits: [{RevealableV2.RevealableTrait}]){ 
272			var i = 0
273			while i < traits.length{ 
274				let RevealableTrait = traits[i]
275				let traitName = RevealableTrait.name
276				let traitValue = RevealableTrait.value
277				switch traitName{ 
278					case "mainImage":
279						assert(self.checkRevealableTrait(traitName: traitName)!, message: "UnRevealableV2 trait passed in - please ensure trait can be revealed: ".concat(traitName))
280						self.updateMetadata(key: traitName, value: traitValue)
281					case "video":
282						assert(self.checkRevealableTrait(traitName: traitName)!, message: "UnRevealableV2 trait passed in - please ensure trait can be revealed: ".concat(traitName))
283						self.updateMetadata(key: traitName, value: traitValue)
284					default:
285						panic("UnRevealableV2 trait passed in - please ensure trait can be revealed: ".concat(traitName))
286				}
287				i = i + 1
288			}
289			self.isRevealed = true
290		}
291		
292		access(contract)
293		fun updateMetadata(key: String, value: AnyStruct){ 
294			self.metadata[key] = value
295		}
296		
297		// Called by the nft owner to modify if a trait can be 
298		// revealed or not - used to revoke admin access
299		access(all)
300		fun updateIsTraitRevealable(key: String, value: Bool){ 
301			self.revealableTraits[key] = value
302		}
303		
304		access(all)
305		fun checkRevealableTrait(traitName: String): Bool?{ 
306			if let RevealableV2 = self.revealableTraits[traitName]{ 
307				return RevealableV2
308			}
309			return nil
310		}
311		
312		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){ 
313			self.id = id
314			self.nftUuid = nftUuid
315			self.name = name
316			self.description = description
317			self.collection = collection
318			self.metadata = metadata
319			self.characteristics = characteristics
320			self.rarity = nil
321			self.rarityDescription = nil
322			self.license = license
323			self.externalURL = externalURL
324			self.coCreatable = coCreatable
325			self.coCreator = coCreator
326			//NOTE: Customise
327			// This should be nil if the nft can't be revealed!
328			self.isRevealed = true
329			self.editionNumber = editionNumber
330			self.maxEditionNumber = maxEditionNumber
331			self.revealableTraits = revealableTraits
332			self.royalties = royalties
333			self.royaltiesTFMarketplace = royaltiesTFMarketplace
334		}
335	}
336	
337	// -----------------------------------------------------------------------
338	// Trait Struct
339	// -----------------------------------------------------------------------
340	// Used by txs to target traits/characteristics to be revealed
341	access(all)
342	struct Trait: RevealableV2.RevealableTrait{ 
343		access(all)
344		let name: String
345		
346		access(all)
347		let value: AnyStruct
348		
349		init(name: String, value: AnyStruct){ 
350			self.name = name
351			self.value = value
352		}
353	}
354	
355	// -----------------------------------------------------------------------
356	// NFT Resource
357	// -----------------------------------------------------------------------
358	// Restricted scope for borrowHighsnobietyNotInParis() in Collection.
359	// Ensures that the returned NFT ref is read only.
360	access(all)
361	resource interface PublicNFT{ 
362		access(all)
363		fun getFullName(): String
364		
365		access(all)
366		fun getEditions(): MetadataViews.Editions
367		
368		access(all)
369		fun getMedias(): MetadataViews.Medias
370		
371		access(all)
372		fun getTraits(): MetadataViews.Traits?
373		
374		access(all)
375		view fun getRarity(): MetadataViews.Rarity?
376		
377		access(all)
378		fun getExternalRoyalties(): MetadataViews.Royalties
379		
380		access(all)
381		fun getTFRoyalties(): TheFabricantMetadataViewsV2.Royalties
382		
383		access(all)
384		fun getMetadata():{ String: AnyStruct}
385		
386		access(all)
387		fun getCharacteristics():{ String:{ CoCreatableV2.Characteristic}}?
388		
389		access(all)
390		fun getDisplay(): MetadataViews.Display
391		
392		access(all)
393		fun getCollectionData(): MetadataViews.NFTCollectionData
394		
395		access(all)
396		fun getCollectionDisplay(): MetadataViews.NFTCollectionDisplay
397		
398		access(all)
399		fun getNFTView(): MetadataViews.NFTView
400		
401		access(all)
402		fun getViews(): [Type]
403		
404		access(all)
405		fun resolveView(_ view: Type): AnyStruct?
406	}
407	
408	access(all)
409	resource NFT: TheFabricantNFTStandardV2.TFNFT, NonFungibleToken.NFT, ViewResolver.Resolver, PublicNFT{ 
410		access(all)
411		let id: UInt64
412		
413		// NOTE: Ensure that the name for the nft is correct. This 
414		// will be shown to users. It should not include the edition number.
415		access(contract)
416		let collectionId: String
417		
418		access(contract)
419		let editionNumber: UInt64 //Edition
420		
421		
422		access(contract)
423		let maxEditionNumber: UInt64?
424		
425		access(contract)
426		let originalRecipient: Address
427		
428		access(contract)
429		let license: MetadataViews.License?
430		
431		access(contract)
432		let nftMetadataId: UInt64
433		
434		access(all)
435		fun getFullName(): String{ 
436			return ((HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).name!).concat(" #".concat(self.editionNumber.toString()))
437		}
438		
439		// NOTE: This is important for Edition view
440		access(all)
441		fun getEditionName(): String{ 
442			return (HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).collection
443		}
444		
445		access(all)
446		fun getEditions(): MetadataViews.Editions{ 
447			// NOTE: In this case, id == edition number
448			let edition = MetadataViews.Edition(name: (HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).collection, number: self.editionNumber, max: HighsnobietyNotInParis.maxSupply)
449			return MetadataViews.Editions([edition])
450		}
451		
452		//NOTE: Customise
453		//NOTE: This will be different for each campaign, determined by how
454		// many media files there are and their keys in metadata! Pay attention
455		// to where the media files are stored and therefore accessed
456		access(all)
457		fun getMedias(): MetadataViews.Medias{ 
458			let nftMetadata = HighsnobietyNotInParis.nftMetadata[self.id]!
459			let mainImage = nftMetadata.metadata["mainImage"]! as! String
460			// NOTE: This assumes that when the shoeShape characteristic is created
461			// in the update_shoe_shapes_char tx, the value property is created as a dictionary
462			let video = nftMetadata.metadata["video"]! as! String
463			let mainImageMedia = MetadataViews.Media(file: MetadataViews.HTTPFile(url: mainImage), mediaType: "image/png")
464			let videoMedia = MetadataViews.Media(file: MetadataViews.HTTPFile(url: video), mediaType: "video/mp4")
465			return MetadataViews.Medias([mainImageMedia, videoMedia])
466		}
467		
468		// NOTE: Customise
469		access(all)
470		fun getImages():{ String: String}{ 
471			let nftMetadata = HighsnobietyNotInParis.nftMetadata[self.id]!
472			let mainImage = nftMetadata.metadata["mainImage"]! as! String
473			return{ "mainImage": mainImage}
474		}
475		
476		// NOTE: Customise
477		access(all)
478		fun getVideos():{ String: String}{ 
479			let nftMetadata = HighsnobietyNotInParis.nftMetadata[self.id]!
480			let mainVideo = nftMetadata.metadata["video"]! as! String
481			return{ "mainVideo": mainVideo}
482		}
483		
484		// NOTE: Customise
485		// What are the traits that you want external marketplaces
486		// to display?
487		access(all)
488		fun getTraits(): MetadataViews.Traits?{ 
489			return nil
490		}
491		
492		access(all)
493		view fun getRarity(): MetadataViews.Rarity?{ 
494			return nil
495		}
496		
497		access(all)
498		fun getExternalRoyalties(): MetadataViews.Royalties{ 
499			let nftMetadata = HighsnobietyNotInParis.nftMetadata[self.id]!
500			return nftMetadata.royalties
501		}
502		
503		access(all)
504		fun getTFRoyalties(): TheFabricantMetadataViewsV2.Royalties{ 
505			let nftMetadata = HighsnobietyNotInParis.nftMetadata[self.id]!
506			return nftMetadata.royaltiesTFMarketplace
507		}
508		
509		access(all)
510		fun getMetadata():{ String: AnyStruct}{ 
511			return (HighsnobietyNotInParis.nftMetadata[self.id]!).metadata
512		}
513		
514		//NOTE: This is not a CoCreatableV2 NFT, so no characteristics are present
515		access(all)
516		fun getCharacteristics():{ String:{ CoCreatableV2.Characteristic}}?{ 
517			return nil
518		}
519		
520		access(all)
521		fun getRevealableTraits():{ String: Bool}?{ 
522			return (HighsnobietyNotInParis.nftMetadata[self.id]!).getRevealableTraits()
523		}
524		
525		//NOTE: The first file in medias will be the thumbnail.
526		// Maybe put a file type check in here to ensure it is 
527		// an image?
528		access(all)
529		fun getDisplay(): MetadataViews.Display{ 
530			return MetadataViews.Display(name: self.getFullName(), description: (HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).description, thumbnail: self.getMedias().items[0].file)
531		}
532		
533		access(all)
534		fun getCollectionData(): MetadataViews.NFTCollectionData{ 
535			return MetadataViews.NFTCollectionData(storagePath: HighsnobietyNotInParis.HighsnobietyNotInParisCollectionStoragePath, publicPath: HighsnobietyNotInParis.HighsnobietyNotInParisCollectionPublicPath, publicCollection: Type<&HighsnobietyNotInParis.Collection>(), publicLinkedType: Type<&HighsnobietyNotInParis.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
536					return <-HighsnobietyNotInParis.createEmptyCollection(nftType: Type<@HighsnobietyNotInParis.Collection>())
537				})
538		}
539		
540		//NOTE: Customise
541		// NOTE: Update this function with the collection display image
542		// and TF socials
543		access(all)
544		fun getCollectionDisplay(): MetadataViews.NFTCollectionDisplay{ 
545			let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://highsnobiety-collection-display.s3.eu-central-1.amazonaws.com/images/HS_squareImage.png"), mediaType: "image/png")
546			let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://highsnobiety-collection-display.s3.eu-central-1.amazonaws.com/images/HS_bannerImage.png"), mediaType: "image/png")
547			return MetadataViews.NFTCollectionDisplay(name: self.getEditionName(), description: "Not in Paris is the most extensive and impactful flagship event in Highsnobiety\u{2019}s calendar and is a celebration of all things fashion, culture and tech. For the Not in Paris 2023 edition, The Fabricant has created a series of digital fashion collectibles in collaboration with Highsnobiety.", externalURL: (HighsnobietyNotInParis.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")})
548		}
549		
550		access(all)
551		fun getNFTView(): MetadataViews.NFTView{ 
552			return MetadataViews.NFTView(id: self.id, uuid: self.uuid, display: self.getDisplay(), externalURL: (HighsnobietyNotInParis.nftMetadata[self.id]!).externalURL, collectionData: self.getCollectionData(), collectionDisplay: self.getCollectionDisplay(), royalties: (HighsnobietyNotInParis.nftMetadata[self.id]!).royalties, traits: self.getTraits())
553		}
554		
555		access(all)
556		view fun getViews(): [Type]{ 
557			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>()]
558			return viewArray
559		}
560		
561		access(all)
562		fun resolveView(_ view: Type): AnyStruct?{ 
563			switch view{ 
564				case Type<TheFabricantMetadataViewsV2.TFNFTIdentifierV1>():
565					return TheFabricantMetadataViewsV2.TFNFTIdentifierV1(uuid: self.uuid, id: self.id, name: self.getFullName(), collection: (HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).collection, editions: self.getEditions(), address: (self.owner!).address, originalRecipient: self.originalRecipient)
566				case Type<TheFabricantMetadataViewsV2.TFNFTSimpleView>():
567					return TheFabricantMetadataViewsV2.TFNFTSimpleView(uuid: self.uuid, id: self.id, name: self.getFullName(), description: (HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).description, collection: (HighsnobietyNotInParis.nftMetadata[self.nftMetadataId]!).collection, collectionId: HighsnobietyNotInParis.collectionId!, metadata: self.getMetadata(), media: self.getMedias(), images: self.getImages(), videos: self.getVideos(), externalURL: (HighsnobietyNotInParis.nftMetadata[self.id]!).externalURL, rarity: self.getRarity(), traits: self.getTraits(), characteristics: self.getCharacteristics(), coCreatable: (HighsnobietyNotInParis.nftMetadata[self.id]!).coCreatable, coCreator: (HighsnobietyNotInParis.nftMetadata[self.id]!).coCreator, isRevealed: (HighsnobietyNotInParis.nftMetadata[self.id]!).isRevealed, editions: self.getEditions(), originalRecipient: self.originalRecipient, royalties: (HighsnobietyNotInParis.nftMetadata[self.id]!).royalties, royaltiesTFMarketplace: (HighsnobietyNotInParis.nftMetadata[self.id]!).royaltiesTFMarketplace, revealableTraits: self.getRevealableTraits(), address: (self.owner!).address)
568				case Type<MetadataViews.NFTView>():
569					return self.getNFTView()
570				case Type<MetadataViews.Display>():
571					return self.getDisplay()
572				case Type<MetadataViews.Editions>():
573					return self.getEditions()
574				case Type<MetadataViews.Serial>():
575					return self.id
576				case Type<MetadataViews.Royalties>():
577					return HighsnobietyNotInParis.nftMetadata[self.id]?.royalties
578				case Type<MetadataViews.Medias>():
579					return self.getMedias()
580				case Type<MetadataViews.License>():
581					return self.license
582				case Type<MetadataViews.ExternalURL>():
583					return HighsnobietyNotInParis.nftMetadata[self.id]?.externalURL
584				case Type<MetadataViews.NFTCollectionData>():
585					return self.getCollectionData()
586				case Type<MetadataViews.NFTCollectionDisplay>():
587					return self.getCollectionDisplay()
588				case Type<MetadataViews.Rarity>():
589					return self.getRarity()
590				case Type<MetadataViews.Traits>():
591					return self.getTraits()
592			}
593			return nil
594		}
595		
596		access(all)
597		fun updateIsTraitRevealable(key: String, value: Bool){ 
598			let nftMetadata = HighsnobietyNotInParis.nftMetadata[self.id]!
599			nftMetadata.updateIsTraitRevealable(key: key, value: value)
600			HighsnobietyNotInParis.nftMetadata[self.id] = nftMetadata
601			emit IsTraitRevealableV2Updated(nftUuid: nftMetadata.nftUuid, id: nftMetadata.id, trait: key, isRevealableV2: value)
602		}
603		
604		access(all)
605		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
606			return <-create Collection()
607		}
608		
609		init(originalRecipient: Address, license: MetadataViews.License?){ 
610			assert(HighsnobietyNotInParis.collectionId != nil, message: "Ensure that Admin has set collectionId in the contract")
611			HighsnobietyNotInParis.totalSupply = HighsnobietyNotInParis.totalSupply + 1
612			self.id = HighsnobietyNotInParis.totalSupply
613			self.collectionId = HighsnobietyNotInParis.collectionId!
614			
615			// NOTE: Customise
616			// The edition number may need to be different to id
617			// for some campaigns
618			self.editionNumber = self.id
619			self.maxEditionNumber = HighsnobietyNotInParis.maxSupply
620			self.originalRecipient = originalRecipient
621			self.license = license
622			self.nftMetadataId = self.id
623		}
624	}
625	
626	// -----------------------------------------------------------------------
627	// Collection Resource
628	// -----------------------------------------------------------------------
629	access(all)
630	resource interface HighsnobietyNotInParisCollectionPublic{ 
631		access(all)
632		fun borrowHighsnobietyNotInParis(id: UInt64): &HighsnobietyNotInParis.NFT?
633		
634		access(all)
635		fun deposit(token: @{NonFungibleToken.NFT})
636		
637		access(all)
638		fun getIDs(): [UInt64]
639		
640		access(all)
641		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
642
643		access(all)
644		view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?
645	}
646	
647	access(all)
648	resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, HighsnobietyNotInParisCollectionPublic, ViewResolver.ResolverCollection{ 
649		
650		// Dictionary to hold the NFTs in the Collection
651		access(all)
652		var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
653		
654		access(all)
655		view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
656			let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
657			let HighsnobietyNotInParis = nft as! &HighsnobietyNotInParis.NFT
658			return HighsnobietyNotInParis as &{ViewResolver.Resolver}
659		}
660		
661		// withdraw removes an NFT from the collection and moves it to the caller
662		access(NonFungibleToken.Withdraw)
663		fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
664			// Remove the nft from the Collection
665			let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("Cannot withdraw: NFT does not exist in the collection")
666			emit Withdraw(id: token.id, from: self.owner?.address)
667			
668			// Return the withdrawn token
669			return <-token
670		}
671		
672		// deposit takes an NFT and adds it to the collections dictionary
673		// and adds the ID to the id array
674		access(all)
675		fun deposit(token: @{NonFungibleToken.NFT}){ 
676			// By ensuring self.owner.address is not nil we keep the nftIdsToOwner dict 
677			// up to date.
678			pre{ 
679				self.owner?.address != nil:
680					"The Collection resource must be stored in a users account"
681			}
682			
683			// Cast the deposited token as  NFT to make sure
684			// it is the correct type
685			let token <- token as! @NFT
686			
687			// Get the token's ID
688			let id = token.id
689			
690			// Add the new token to the dictionary
691			let oldToken <- self.ownedNFTs[id] <- token
692			HighsnobietyNotInParis.nftIdsToOwner[id] = (self.owner!).address
693			emit Deposit(id: id, to: self.owner?.address)
694			
695			// Destroy the empty old token that was "removed"
696			destroy oldToken
697		}
698		
699		// getIDs returns an array of the IDs that are in the collection
700		access(all)
701		view fun getIDs(): [UInt64]{ 
702			return self.ownedNFTs.keys
703		}
704		
705		// Returns a borrowed reference to an NFT in the collection
706		// so that the caller can read data and call methods from it
707		access(all)
708		view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
709			return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
710		}
711		
712		access(all)
713		fun borrowHighsnobietyNotInParis(id: UInt64): &HighsnobietyNotInParis.NFT?{ 
714			if self.ownedNFTs[id] != nil{ 
715				// Create an authorized reference to allow downcasting
716				let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
717				return ref as! &HighsnobietyNotInParis.NFT
718			}
719			return nil
720		}
721		
722		access(all)
723		view fun getSupportedNFTTypes():{ Type: Bool}{ 
724			panic("implement me")
725		}
726		
727		access(all)
728		view fun isSupportedNFTType(type: Type): Bool{ 
729			panic("implement me")
730		}
731		
732		access(all)
733		fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
734			return <-create Collection()
735		}
736		
737		// If a transaction destroys the Collection object,
738		// All the NFTs contained within are also destroyed!
739		//
740		init(){ 
741			self.ownedNFTs <-{} 
742		}
743	}
744	
745	// -----------------------------------------------------------------------
746	// Admin Resource
747	// -----------------------------------------------------------------------
748	access(all)
749	resource Admin{ 
750		access(all)
751		fun setPublicReceiverCap(paymentReceiverCap: Capability<&{FungibleToken.Receiver}>){ 
752			HighsnobietyNotInParis.paymentReceiverCap = paymentReceiverCap
753			emit AdminPaymentReceiverCapabilityChanged(address: paymentReceiverCap.address, paymentType: paymentReceiverCap.getType())
754		}
755		
756		access(all)
757		fun setBaseURI(baseURI: String){ 
758			HighsnobietyNotInParis.baseTokenURI = baseURI
759			emit AdminSetBaseURI(baseURI: baseURI)
760		}
761		
762		// The max supply determines the maximum number of NFTs that can be minted from this contract
763		access(all)
764		fun setMaxSupply(maxSupply: UInt64){ 
765			HighsnobietyNotInParis.maxSupply = maxSupply
766			emit AdminSetMaxSupply(maxSupply: maxSupply)
767		}
768		
769		access(all)
770		fun setAddressMintLimit(addressMintLimit: UInt64){ 
771			HighsnobietyNotInParis.addressMintLimit = addressMintLimit
772			emit AdminSetAddressMintLimit(addressMintLimit: addressMintLimit)
773		}
774		
775		access(all)
776		fun setCollectionId(collectionId: String){ 
777			HighsnobietyNotInParis.collectionId = collectionId
778			emit AdminSetCollectionId(collectionId: collectionId)
779		}
780		
781		//NOTE: Customise
782		// mint not:
783		// maxSupply has been hit √
784		// minting isn't open (!isOpen) √
785		// mint if:
786		// openAccess √
787		// OR address on access list √
788		// Output:
789		// NFT √
790		// nftMetadata √
791		// update mints per address √
792		//NOTE: !Used for CC payments via MoonPay!
793		access(all)
794		fun distributeDirectlyViaAccessList(receiver: &{NonFungibleToken.CollectionPublic}, publicMinterPathString: String, variantId: UInt64){ 
795			
796			// Ensure that the maximum supply of nfts for this contract has not been hit
797			if HighsnobietyNotInParis.maxSupply != nil{ 
798				assert(HighsnobietyNotInParis.totalSupply + 1 <= HighsnobietyNotInParis.maxSupply!, message: "Max supply for NFTs has been hit")
799			}
800			
801			// Get the publicMinter details so we can apply all the correct props to the NFT
802			//NOTE: Therefore relies on a pM having been created
803			let publicPath = PublicPath(identifier: publicMinterPathString) ?? panic("Failed to construct public path from path string: ".concat(publicMinterPathString))
804			let publicMinterCap = getAccount((self.owner!).address).capabilities.get<&HighsnobietyNotInParis.PublicMinter>(publicPath).borrow() ?? panic("Couldn't get publicMinter ref or pathString is wrong: ".concat(publicMinterPathString))
805			let publicMinterDetails = publicMinterCap.getPublicMinterDetails()
806			
807			//Confirm that minting is open on the publicMinter
808			let isOpen = publicMinterDetails["isOpen"] as! Bool?
809			assert(isOpen!, message: "Minting is not open!")
810			
811			//Check that the address has access via the access list. If isOpenAccess, then anyone can mint.
812			let isOpenAccess = publicMinterDetails["isOpenAccess"] as! Bool?
813			let accessListId = publicMinterDetails["accessListId"] as! UInt64?
814			if !isOpenAccess!{ 
815				assert(TheFabricantAccessList.checkAccessForAddress(accessListDetailsId: accessListId!, address: (receiver.owner!).address), message: "User address is not on the access list and so cannot mint.")
816			}
817			
818			// Create the NFT
819			let license = publicMinterDetails["license"] as! MetadataViews.License?
820			let nft <- create NFT(originalRecipient: (receiver.owner!).address, license: license)
821			let name = publicMinterDetails["name"] as! String?
822			let description = publicMinterDetails["description"] as! String?
823			let collection = publicMinterDetails["collection"] as! String?
824			let externalURL = publicMinterDetails["externalURL"] as! MetadataViews.ExternalURL?
825			let coCreatable = publicMinterDetails["coCreatable"] as! Bool?
826			let revealableTraits = publicMinterDetails["revealableTraits"] as!{ String: Bool}?
827			let royalties = publicMinterDetails["royalties"] as! MetadataViews.Royalties?
828			let royaltiesTFMarketplace = publicMinterDetails["royaltiesTFMarketplace"] as! TheFabricantMetadataViewsV2.Royalties?
829			
830			//Create the nftMetadata
831			HighsnobietyNotInParis.createNftMetadata(id: nft.id, nftUuid: nft.uuid, variantId: variantId, name: name!, description: description!, collection: collection!, characteristics:{} , license: nft.license, externalURL: externalURL!, coCreatable: coCreatable!, coCreator: (receiver.owner!).address, editionNumber: nft.editionNumber, maxEditionNumber: nft.maxEditionNumber, revealableTraits: revealableTraits!, royalties: royalties!, royaltiesTFMarketplace: royaltiesTFMarketplace!)
832			
833			//NOTE: Event is emitted here and not in nft init because
834			// data is split between RevealableMetadata and nft,
835			// so not all event data is accessible during nft init
836			emit ItemMintedAndTransferred(uuid: nft.uuid, id: nft.id, name: (HighsnobietyNotInParis.nftMetadata[nft.nftMetadataId]!).name, description: (HighsnobietyNotInParis.nftMetadata[nft.nftMetadataId]!).description, collection: (HighsnobietyNotInParis.nftMetadata[nft.nftMetadataId]!).collection, editionNumber: nft.editionNumber, originalRecipient: nft.originalRecipient, license: nft.license, nftMetadataId: nft.nftMetadataId)
837			receiver.deposit(token: <-nft)
838			
839			// Increment the number of mints that an address has
840			if HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] != nil{ 
841				HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] = HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address]! + 1
842			} else{ 
843				HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] = 1
844			}
845		}
846		
847		// NOTE: It is in the public minter that you would create the restrictions
848		// for minting. 
849		access(all)
850		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){ 
851			pre{ 
852				HighsnobietyNotInParis.paymentReceiverCap != nil:
853					"Please set the paymentReceiverCap before creating a minter"
854			}
855			let publicMinter: @HighsnobietyNotInParis.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)
856			
857			// Save path: name_collection_uuid
858			// Link the Public Minter to a Public Path of the admin account
859			let publicMinterStoragePath = StoragePath(identifier: publicMinter.path)
860			let publicMinterPublicPath = PublicPath(identifier: publicMinter.path)
861			HighsnobietyNotInParis.account.storage.save(<-publicMinter, to: publicMinterStoragePath!)
862		}
863		
864		access(all)
865		fun revealTraits(nftMetadataId: UInt64, traits: [{RevealableV2.RevealableTrait}]){ 
866			let nftMetadata = HighsnobietyNotInParis.nftMetadata[nftMetadataId]! as! HighsnobietyNotInParis.RevealableMetadata
867			nftMetadata.revealTraits(traits: traits)
868			HighsnobietyNotInParis.nftMetadata[nftMetadataId] = nftMetadata
869			
870			// Event should be emitted in resource, not struct
871			var i = 1
872			while i < traits.length{ 
873				let traitName = traits[i].name
874				let traitValue = traits[i].value
875				emit TraitRevealed(nftUuid: nftMetadata.nftUuid, id: nftMetadata.id, trait: traitName)
876				i = i + 1
877			}
878			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)
879		}
880		
881		init(adminAddress: Address){ 
882			emit AdminResourceCreated(uuid: self.uuid, adminAddress: adminAddress)
883		}
884	}
885	
886	// -----------------------------------------------------------------------
887	// PublicMinter Resource
888	// -----------------------------------------------------------------------
889	// NOTE: The public minter is exposed via a capability to allow the public
890	// to mint the NFT so long as they meet the criteria.
891	// It is in the public minter that the various mint functions would be exposed
892	// such as paid mint etc.
893	// Every contract has to manage its own minting via the PublicMinter.
894	//NOTE: Customise
895	// Update the mint functions
896	access(all)
897	resource interface Minter{ 
898		access(all)
899		fun mintUsingAccessList(receiver: &{NonFungibleToken.CollectionPublic}, payment: @{FungibleToken.Vault}, variantId: UInt64)
900		
901		access(all)
902		fun getPublicMinterDetails():{ String: AnyStruct}
903	}
904	
905	access(all)
906	resource PublicMinter: TheFabricantNFTStandardV2.TFNFTPublicMinter, Minter{ 
907		access(all)
908		var path: String
909		
910		access(all)
911		var isOpen: Bool
912		
913		access(all)
914		var isAccessListOnly: Bool
915		
916		access(all)
917		var isOpenAccess: Bool
918		
919		// NOTE: Remove these as required and update the NFT props and 
920		// resolveView to reflect this, so that views that this nft
921		// does not display are not provided
922		// Name of nft, not campaign. This will be combined with the edition number
923		access(all)
924		let name: String
925		
926		access(all)
927		let description: String
928		
929		access(all)
930		let collection: String
931		
932		access(all)
933		let license: MetadataViews.License?
934		
935		access(all)
936		let externalURL: MetadataViews.ExternalURL
937		
938		access(all)
939		let coCreatable: Bool
940		
941		access(all)
942		let revealableTraits:{ String: Bool}
943		
944		// 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).
945		access(all)
946		var minterMintLimit: UInt64?
947		
948		access(all)
949		var numberOfMints: UInt64
950		
951		access(all)
952		let royalties: MetadataViews.Royalties
953		
954		access(all)
955		let royaltiesTFMarketplace: TheFabricantMetadataViewsV2.Royalties
956		
957		access(all)
958		var paymentAmount: UFix64
959		
960		access(all)
961		let paymentType: Type
962		
963		// paymentSplit: How much each address gets paid on minting of NFT
964		access(all)
965		let paymentSplit: MetadataViews.Royalties?
966		
967		access(all)
968		var typeRestrictions: [Type]?
969		
970		access(all)
971		var accessListId: UInt64
972		
973		access(all)
974		fun changeIsOpenAccess(isOpenAccess: Bool){ 
975			self.isOpenAccess = isOpenAccess
976			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)
977		}
978		
979		access(all)
980		fun changeIsAccessListOnly(isAccessListOnly: Bool){ 
981			self.isAccessListOnly = isAccessListOnly
982			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)
983		}
984		
985		access(all)
986		fun changeMintingIsOpen(isOpen: Bool){ 
987			self.isOpen = isOpen
988			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)
989		}
990		
991		access(all)
992		fun setAccessListId(accessListId: UInt64){ 
993			self.accessListId = accessListId
994			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)
995		}
996		
997		access(all)
998		fun setPaymentAmount(amount: UFix64){ 
999			self.paymentAmount = amount
1000			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)
1001		}
1002		
1003		access(all)
1004		fun setMinterMintLimit(minterMintLimit: UInt64){ 
1005			self.minterMintLimit = minterMintLimit
1006			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)
1007		}
1008		
1009		// The owner of the pM can access this via borrow in tx.
1010		access(all)
1011		fun updateTypeRestrictions(types: [Type]){ 
1012			self.typeRestrictions = types
1013		}
1014		
1015		//NOTE: Customise
1016		// mint not:
1017		// maxMint for this address has been hit
1018		// maxSupply has been hit √
1019		// minting isn't open (!isOpen) √
1020		// payment is insufficient √
1021		// minterMintLimit is hit √
1022		// mint if:
1023		// openAccess √
1024		// OR address on access list √
1025		// Output:
1026		// NFT √
1027		// nftMetadata √
1028		// update mints per address √
1029		access(all)
1030		fun mintUsingAccessList(receiver: &{NonFungibleToken.CollectionPublic}, payment: @{FungibleToken.Vault}, variantId: UInt64){ 
1031			pre{ 
1032				self.isOpen:
1033					"Minting is not currently open!"
1034				payment.isInstance(self.paymentType):
1035					"payment vault is not requested fungible token"
1036				payment.balance == self.paymentAmount:
1037					"Incorrect payment amount provided for minting"
1038				HighsnobietyNotInParis.paymentReceiverCap != nil:
1039					"Payment Receiver Cap must be set for minting!"
1040			}
1041			post{ 
1042				receiver.getIDs().length == before(receiver.getIDs().length) + 1:
1043					"Minted NFT must be deposited into Collection"
1044			}
1045			
1046			// Total number of mints by this pM
1047			self.numberOfMints = self.numberOfMints + 1
1048			
1049			// Ensure that minterMintLimit for this pM has not been hit
1050			if self.minterMintLimit != nil{ 
1051				assert(self.numberOfMints <= self.minterMintLimit!, message: "Maximum number of mints for this public minter has been hit")
1052			}
1053			
1054			// Ensure that the maximum supply of nfts for this contract has not been hit
1055			if HighsnobietyNotInParis.maxSupply != nil{ 
1056				assert(HighsnobietyNotInParis.totalSupply + 1 <= HighsnobietyNotInParis.maxSupply!, message: "Max supply for NFTs has been hit")
1057			}
1058			
1059			// Ensure user hasn't minted more NFTs from this contract than allowed
1060			if HighsnobietyNotInParis.addressMintLimit != nil{ 
1061				if HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] != nil{ 
1062					assert(HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address]! < HighsnobietyNotInParis.addressMintLimit!, message: "User has already minted the maximum allowance per address!")
1063				}
1064			}
1065			
1066			// Check that the address has access via the access list. If isOpenAccess, then anyone can mint.
1067			if !self.isOpenAccess{ 
1068				assert(TheFabricantAccessList.checkAccessForAddress(accessListDetailsId: self.accessListId, address: (receiver.owner!).address), message: "User address is not on the access list and so cannot mint.")
1069			}
1070			
1071			// Settle Payment
1072			if let _paymentSplit = self.paymentSplit{ 
1073				var i = 0
1074				let splits = _paymentSplit.getRoyalties()
1075				while i < splits.length{ 
1076					let split = splits[i]
1077					let receiver = split.receiver
1078					let cut = split.cut
1079					let paymentAmount = self.paymentAmount * cut
1080					if let wallet = receiver.borrow(){ 
1081						let pay <- payment.withdraw(amount: paymentAmount)
1082						emit MintPaymentSplitDeposited(address: (wallet.owner!).address, price: self.paymentAmount, amount: pay.balance, nftUuid: self.uuid)
1083						wallet.deposit(from: <-pay)
1084					}
1085					i = i + 1
1086				}
1087			}
1088			if payment.balance != 0.0 || payment.balance == 0.0{ 
1089				// pay rest to TF
1090				emit MintPaymentSplitDeposited(address: (HighsnobietyNotInParis.paymentReceiverCap!).address, price: self.paymentAmount, amount: payment.balance, nftUuid: self.uuid)
1091			}
1092			((			  // Deposit has to occur outside of above if statement as resource must be moved or destroyed
1093			  HighsnobietyNotInParis.paymentReceiverCap!).borrow()!).deposit(from: <-payment)
1094			let nft <- create NFT(originalRecipient: (receiver.owner!).address, license: self.license)
1095			HighsnobietyNotInParis.createNftMetadata(id: nft.id, nftUuid: nft.uuid, variantId: variantId, name: self.name, description: self.description, collection: self.collection, characteristics:{} , license: nft.license, externalURL: self.externalURL, coCreatable: self.coCreatable, coCreator: (receiver.owner!).address, editionNumber: nft.editionNumber, maxEditionNumber: nft.maxEditionNumber, revealableTraits: self.revealableTraits, royalties: self.royalties, royaltiesTFMarketplace: self.royaltiesTFMarketplace)
1096			
1097			//NOTE: Event is emitted here and not in nft init because
1098			// data is split between RevealableMetadata and nft,
1099			// so not all event data is accessible during nft init
1100			emit ItemMintedAndTransferred(uuid: nft.uuid, id: nft.id, name: (HighsnobietyNotInParis.nftMetadata[nft.nftMetadataId]!).name, description: (HighsnobietyNotInParis.nftMetadata[nft.nftMetadataId]!).description, collection: (HighsnobietyNotInParis.nftMetadata[nft.nftMetadataId]!).collection, editionNumber: nft.editionNumber, originalRecipient: nft.originalRecipient, license: self.license, nftMetadataId: nft.nftMetadataId)
1101			receiver.deposit(token: <-nft)
1102			
1103			// Increment the number of mints that an address has
1104			if HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] != nil{ 
1105				HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] = HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address]! + 1
1106			} else{ 
1107				HighsnobietyNotInParis.addressMintCount[(receiver.owner!).address] = 1
1108			}
1109		}
1110		
1111		access(all)
1112		fun getPublicMinterDetails():{ String: AnyStruct}{ 
1113			let ret:{ String: AnyStruct} ={} 
1114			ret["name"] = self.name
1115			ret["uuid"] = self.uuid
1116			ret["path"] = self.path
1117			ret["isOpen"] = self.isOpen
1118			ret["isAccessListOnly"] = self.isAccessListOnly
1119			ret["isOpenAccess"] = self.isOpenAccess
1120			ret["description"] = self.description
1121			ret["collection"] = self.collection
1122			ret["collectionId"] = HighsnobietyNotInParis.collectionId
1123			ret["license"] = self.license
1124			ret["externalURL"] = self.externalURL
1125			ret["coCreatable"] = self.coCreatable
1126			ret["revealableTraits"] = self.revealableTraits
1127			ret["minterMintLimit"] = self.minterMintLimit
1128			ret["numberOfMints"] = self.numberOfMints
1129			ret["royalties"] = self.royalties
1130			ret["royaltiesTFMarketplace"] = self.royaltiesTFMarketplace
1131			ret["paymentAmount"] = self.paymentAmount
1132			ret["paymentType"] = self.paymentType
1133			ret["paymentSplit"] = self.paymentSplit
1134			ret["typeRestrictions"] = self.typeRestrictions
1135			ret["accessListId"] = self.accessListId
1136			return ret
1137		}
1138		
1139		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){ 
1140			
1141			// Create and save path: name_collection_uuid
1142			let pathString = "TheFabricantNFTPublicMinter_HighsnobietyNotInParis_".concat(self.uuid.toString())
1143			HighsnobietyNotInParis.publicMinterPaths[self.uuid] = pathString
1144			self.path = pathString
1145			self.isOpen = false
1146			self.isAccessListOnly = true
1147			self.isOpenAccess = false
1148			self.name = name
1149			self.description = description
1150			self.collection = collection
1151			self.license = license
1152			self.externalURL = externalURL
1153			self.coCreatable = coCreatable
1154			self.revealableTraits = revealableTraits
1155			self.minterMintLimit = minterMintLimit
1156			self.numberOfMints = 0
1157			self.royalties = royalties
1158			self.royaltiesTFMarketplace = royaltiesTFMarketplace
1159			self.paymentAmount = paymentAmount
1160			self.paymentType = paymentType
1161			self.paymentSplit = paymentSplit
1162			self.typeRestrictions = typeRestrictions
1163			self.accessListId = accessListId
1164			emit PublicMinterCreated(uuid: self.uuid, name: name, description: description, collection: collection, path: self.path)
1165		}
1166	}
1167	
1168	// -----------------------------------------------------------------------
1169	// Private Utility Functions
1170	// -----------------------------------------------------------------------
1171	//NOTE: Customise
1172	// This function generates the metadata for the minted nft.
1173	access(contract)
1174	fun createNftMetadata(id: UInt64, nftUuid: UInt64, variantId: 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){ 
1175		pre{ 
1176			HighsnobietyNotInParis.baseTokenURI != nil:
1177				"Base URI must be set to mint an NFT!"
1178			variantId == 1 || variantId == 2 || variantId == 3:
1179				"Variant ID must be either 1, 2 or 3"
1180		}
1181		var nftName: String = ""
1182		var nftDescription: String = ""
1183		var metadata:{ String: String} ={} 
1184		switch variantId{ 
1185			case 1:
1186				nftName = "Eiffel Tower Pursuit"
1187				nftDescription = "Introducing the Eiffel Tower Pursuit Varsity Jacket, a digital twin of the classic Varsity Jacket. Mini Eiffel Towers follow you as you wander the city, paying homage to the iconic landmark."
1188				metadata ={ "mainImage": (HighsnobietyNotInParis.baseTokenURI!).concat("/EiffelTowerPursuit.png"), "video": (HighsnobietyNotInParis.baseTokenURI!).concat("/EiffelTowerPursuit.mp4")}
1189			case 2:
1190				nftName = "Parisian Mirage"
1191				nftDescription = "Step into a world of illusions with the Parisian Mirage Varsity Jacket. This jacket creates a mesmerizing effect that seamlessly transforms from a realistic surface to a mirage-like reflection of the surrounding environment."
1192				metadata ={ "mainImage": (HighsnobietyNotInParis.baseTokenURI!).concat("/ParisianMirage.png"), "video": (HighsnobietyNotInParis.baseTokenURI!).concat("/ParisianMirage.mp4")}
1193			case 3:
1194				nftName = "Echoes of Paris"
1195				nftDescription = "Get ready to trip out with the Echoes of Paris Varsity Jacket. This jacket features patches with a slow-mo effect that creates an entrancing visual display as you move."
1196				metadata ={ "mainImage": (HighsnobietyNotInParis.baseTokenURI!).concat("/EchoesOfParis.png"), "video": (HighsnobietyNotInParis.baseTokenURI!).concat("/EchoesOfParis.mp4")}
1197		}
1198		let mD = RevealableMetadata(id: id, nftUuid: nftUuid, name: nftName, description: nftDescription, collection: collection, metadata: metadata, characteristics: characteristics, license: license, externalURL: externalURL, coCreatable: coCreatable, coCreator: coCreator, editionNumber: editionNumber, maxEditionNumber: maxEditionNumber, revealableTraits: revealableTraits, royalties: royalties, royaltiesTFMarketplace: royaltiesTFMarketplace)
1199		HighsnobietyNotInParis.nftMetadata[id] = mD
1200	}
1201	
1202	access(self)
1203	fun nftsCanBeUsedForMint(receiver: &{NonFungibleToken.CollectionPublic}, refs: [&{NonFungibleToken.NFT}], typeRestrictions: [Type]): Bool{ 
1204		assert(typeRestrictions.length != 0, message: "There are no type restrictions for this promotion")
1205		var i = 0
1206		while i < refs.length{ 
1207			if typeRestrictions.contains(refs[i].getType()) && (receiver.owner!).address == (refs[i].owner!).address{ 
1208				return true
1209			}
1210			i = i + 1
1211		}
1212		return false
1213	}
1214	
1215	// -----------------------------------------------------------------------
1216	// Public Utility Functions
1217	// -----------------------------------------------------------------------
1218	// createEmptyCollection creates an empty Collection
1219	// and returns it to the caller so that they can own NFTs
1220	access(all)
1221	fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
1222		return <-create Collection()
1223	}
1224	
1225	access(all)
1226	fun getPublicMinterPaths():{ UInt64: String}{ 
1227		return HighsnobietyNotInParis.publicMinterPaths
1228	}
1229	
1230	access(all)
1231	fun getNftIdsToOwner():{ UInt64: Address}{ 
1232		return HighsnobietyNotInParis.nftIdsToOwner
1233	}
1234	
1235	access(all)
1236	fun getMaxSupply(): UInt64?{ 
1237		return HighsnobietyNotInParis.maxSupply
1238	}
1239	
1240	access(all)
1241	fun getTotalSupply(): UInt64{ 
1242		return HighsnobietyNotInParis.totalSupply
1243	}
1244	
1245	access(all)
1246	fun getCollectionId(): String?{ 
1247		return HighsnobietyNotInParis.collectionId
1248	}
1249	
1250	access(all)
1251	fun getNftMetadatas():{ UInt64:{ RevealableV2.RevealableMetadata}}{ 
1252		return self.nftMetadata
1253	}
1254	
1255	access(all)
1256	fun getPaymentCap(): Address?{ 
1257		return HighsnobietyNotInParis.paymentReceiverCap?.address
1258	}
1259	
1260	access(all)
1261	fun getBaseUri(): String?{ 
1262		return HighsnobietyNotInParis.baseTokenURI
1263	}
1264
1265	access(all)
1266	view fun getContractViews(resourceType: Type?): [Type]{ 
1267		return []
1268	}
1269
1270	access(all)
1271	view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct?{ 
1272		return nil
1273	}
1274	
1275	// -----------------------------------------------------------------------
1276	// Contract Init
1277	// -----------------------------------------------------------------------
1278	init(){ 
1279		self.totalSupply = 0
1280		self.maxSupply = nil
1281		self.publicMinterPaths ={} 
1282		self.collectionId = nil
1283		self.nftIdsToOwner ={} 
1284		self.addressMintCount ={} 
1285		self.paymentReceiverCap = nil
1286		self.nftMetadata ={} 
1287		self.addressMintLimit = nil
1288		self.baseTokenURI = nil
1289		self.HighsnobietyNotInParisCollectionStoragePath = /storage/HighsnobietyNotInParisCollectionStoragePath
1290		self.HighsnobietyNotInParisCollectionPublicPath = /public/HighsnobietyNotInParisCollectionPublicPath
1291		self.HighsnobietyNotInParisProviderStoragePath = /private/HighsnobietyNotInParisProviderStoragePath
1292		self.HighsnobietyNotInParisAdminStoragePath = /storage/HighsnobietyNotInParisAdminStoragePath
1293		self.HighsnobietyNotInParisPublicMinterStoragePath = /storage/HighsnobietyNotInParisPublicMinterStoragePath
1294		self.HighsnobietyNotInParisPublicMinterPublicPath = /public/HighsnobietyNotInParisPublicMinterPublicPath
1295		self.account.storage.save(<-create Admin(adminAddress: self.account.address), to: self.HighsnobietyNotInParisAdminStoragePath)
1296		emit ContractInitialized()
1297	}
1298}
1299