Smart Contract
HighsnobietyNotInParis
A.7752ea736384322f.HighsnobietyNotInParis
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