Smart Contract

TheFabricantXXories

A.7752ea736384322f.TheFabricantXXories

Deployed

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

Dependents

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