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