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