Smart Contract
FlowversePass
A.9212a87501a8a6a2.FlowversePass
1/*
2 FlowversePass.cdc
3
4 Author: Brian Min brian@flowverse.co
5*/
6
7import NonFungibleToken from 0x1d7e57aa55817448
8import MetadataViews from 0x1d7e57aa55817448
9import FungibleToken from 0xf233dcee88fe0abe
10import ViewResolver from 0x1d7e57aa55817448
11
12access(all) contract FlowversePass: NonFungibleToken {
13 // Events
14 access(all) event EntityCreated(id: UInt64, metadata: {String:String})
15 access(all) event EntityUpdated(id: UInt64, metadata: {String:String})
16 access(all) event SetCreated(setID: UInt64, name: String, description: String, externalURL: String, isPrivate: Bool, imageIPFS: String)
17 access(all) event SetUpdated(setID: UInt64, description: String?, externalURL: String?, imageIPFS: String?)
18 access(all) event EntityAddedToSet(setID: UInt64, entityID: UInt64)
19 access(all) event EntityRetiredFromSet(setID: UInt64, entityID: UInt64, numNFTs: UInt64)
20 access(all) event SetLocked(setID: UInt64)
21 access(all) event NFTMinted(nftID: UInt64, nftUUID: UInt64, entityID: UInt64, setID: UInt64, mintNumber: UInt64, minterAddress: Address)
22
23 // Named Paths
24 access(all) let CollectionStoragePath: StoragePath
25 access(all) let CollectionPublicPath: PublicPath
26 access(all) let AdminStoragePath: StoragePath
27
28 access(self) var entityDatas: {UInt64: Entity}
29 access(self) var setDatas: {UInt64: SetData}
30 access(self) var sets: @{UInt64: Set}
31
32 // Total number of FlowversePass NFTs that have been minted
33 access(all) var totalSupply: UInt64
34
35 // Incremented ID used to create entities
36 access(all) var nextEntityID: UInt64
37
38 // Incremented ID used to create sets
39 access(all) var nextSetID: UInt64
40
41 // Entity is a Struct that holds metadata associated
42 // with an NFT entity
43 // NFTs will all reference a single entity as the owner of
44 // its metadata. The entities are publicly accessible, so anyone can
45 // read the metadata associated with a specific entity ID
46 access(all) struct Entity {
47 // Unique ID for the entity
48 access(all) let entityID: UInt64
49
50 // Stores all the metadata about the entity as a string mapping
51 access(all) var metadata: {String: String}
52
53 init(metadata: {String: String}) {
54 pre {
55 metadata.length != 0: "New Entity metadata cannot be empty"
56 }
57 self.entityID = FlowversePass.nextEntityID
58 self.metadata = metadata
59 }
60 }
61
62 // A Set is a group of Entities, like a group of collectibles.
63 // An Entity can exist in multiple different sets.
64 // SetData is a struct that is stored in a field of the contract.
65 access(all) struct SetData {
66
67 // Unique ID for the Set
68 access(all) let setID: UInt64
69
70 // Name of the Set
71 access(all) let name: String
72
73 // Description of the Set
74 access(all) let description: String
75
76 // externalURL of the Set
77 access(all) let externalURL: String
78
79 // Image of the Set
80 access(all) let imageIPFS: String
81
82 // Indicates if this Set is listed / available to public
83 // e.g. admin may create a private collection for air dropping nfts
84 access(all) var isPrivate: Bool
85
86 init(setID: UInt64, name: String, description: String, externalURL: String, imageIPFS: String, isPrivate: Bool) {
87 pre {
88 name.length > 0: "New set name cannot be empty"
89 description.length > 0: "New set description cannot be empty"
90 imageIPFS.length > 0: "New set imageIPFS cannot be empty"
91 }
92
93 self.setID = setID
94 self.name = name
95 self.description = description
96 self.externalURL = externalURL
97 self.imageIPFS = imageIPFS
98 self.isPrivate = isPrivate
99 }
100 }
101
102 // Set is a resource type that contains the functions to add and remove
103 // Entities from a set and mint NFTs.
104 //
105 // It is stored in a private field in the contract so that
106 // the admin resource can call its methods.
107 //
108 // The admin can add Entitys to a Set so that the set can mint NFTs
109 // that reference that entity data.
110 // The NFTs that are minted by a Set will be listed as belonging to
111 // the Set that minted it, as well as the Entity it references.
112 //
113 // Admin can also lock Entitys from the Set, meaning that the locked
114 // Entity can no longer have NFTs minted from it.
115 //
116 // If the admin locks the Set, no more Entitys can be added to it, but
117 // NFTs can still be minted.
118 //
119 // If lockAll() and lock() are called back-to-back,
120 // the Set is closed off forever and nothing more can be done with it.
121 access(all) resource Set {
122
123 // Unique ID for the set
124 access(all) let setID: UInt64
125
126 // Array of entities that are a part of this set.
127 // When a entity is added to the set, its ID gets appended here.
128 // The ID does not get removed from this array when a entities is locked.
129 access(self) var entities: [UInt64]
130
131 // Map of entity IDs that Indicates if a entity in this Set can be minted.
132 // When a entities is added to a Set, it is mapped to false (not locked).
133 // When a entity is retired, this is set to true and cannot be changed.
134 access(self) var retired: {UInt64: Bool}
135
136 // Indicates if the Set is currently locked.
137 // When a Set is created, it is unlocked
138 // and entities are allowed to be added to it.
139 // When a set is locked, entities cannot be added.
140 // A Set can never be changed from locked to unlocked,
141 // the decision to lock a Set it is final.
142 // If a Set is locked, entities cannot be added, but
143 // NFTs can still be minted from entities
144 // that exist in the Set.
145 access(all) var locked: Bool
146
147 // Mapping of Entity IDs that indicates the number of NFTs
148 // that have been minted for specific Entitys in this Set.
149 // When a NFT is minted, this value is stored in the NFT to
150 // show its place in the Set, eg. 13 of 60.
151 access(self) var numMintedPerEntity: {UInt64: UInt64}
152
153 access(all) var totalMinted: UInt64
154
155 init()
156 {
157 self.setID = FlowversePass.nextSetID
158 self.entities = []
159 self.retired = {}
160 self.locked = false
161 self.numMintedPerEntity = {}
162 self.totalMinted = 0
163 }
164
165 // addEntity adds an entity to the set
166 //
167 // Parameters: entityID: The ID of the entity that is being added
168 //
169 // Pre-Conditions:
170 // The entity needs to be an existing entity
171 // The Set needs to be not locked
172 // The entity can't have already been added to the Set
173 //
174 access(all) fun addEntity(entityID: UInt64) {
175 pre {
176 FlowversePass.entityDatas[entityID] != nil: "Cannot add the Entity to Set: Entity doesn't exist."
177 !self.locked: "Cannot add the entity to the Set after the set has been locked."
178 self.numMintedPerEntity[entityID] == nil: "The entity has already been added to the set."
179 }
180
181 // Add the Entity to the array of Plays
182 self.entities.append(entityID)
183
184 // Open the Entity up for minting
185 self.retired[entityID] = false
186
187 // Initialize the Entity count to zero
188 self.numMintedPerEntity[entityID] = 0
189
190 emit EntityAddedToSet(setID: self.setID, entityID: entityID)
191 }
192
193 // addEntities adds multiple entities to the Set
194 //
195 // Parameters: entityIDs: The IDs of the entities that are being added
196 //
197 access(all) fun addEntities(entityIDs: [UInt64]) {
198 for entity in entityIDs {
199 self.addEntity(entityID: entity)
200 }
201 }
202
203 // retireEntity retires a Entity from the Set so that it can't mint new NFTs
204 //
205 // Parameters: entityID: The ID of the Entity that is being retired
206 //
207 // Pre-Conditions:
208 // The Entity is part of the Set and not retired (available for minting).
209 //
210 access(all) fun retireEntity(entityID: UInt64) {
211 pre {
212 self.retired[entityID] != nil: "Cannot retire the entity: Entity doesn't exist in this set!"
213 }
214
215 if !self.retired[entityID]! {
216 self.retired[entityID] = true
217
218 emit EntityRetiredFromSet(setID: self.setID, entityID: entityID, numNFTs: self.numMintedPerEntity[entityID]!)
219 }
220 }
221
222 // retireAll retires all the entities in the Set
223 // Afterwards, none of the retired entities will be able to mint new instances
224 //
225 access(all) fun retireAll() {
226 for entity in self.entities {
227 self.retireEntity(entityID: entity)
228 }
229 }
230
231 // lock() locks the Set so that no more Entitys can be added to it
232 //
233 // Pre-Conditions:
234 // The Set should not be locked
235 access(all) fun lock() {
236 if !self.locked {
237 self.locked = true
238 emit SetLocked(setID: self.setID)
239 }
240 }
241
242 // mint mints a new entity instance and returns the newly minted instance of an entity
243 //
244 // Parameters:
245 // entityID: The ID of the Entity that the NFT references
246 // minterAddress: The address of the minter
247 //
248 // Pre-Conditions:
249 // The Entity must exist in the Set and be allowed to mint new NFTs
250 //
251 // Returns: The NFT that was minted
252 //
253 access(all) fun mint(entityID: UInt64, minterAddress: Address): @NFT {
254 pre {
255 self.retired[entityID] != nil: "Cannot mint: the entity doesn't exist."
256 !self.retired[entityID]!: "Cannot mint from this entity: the entity has been retired."
257 }
258
259 // Gets the number of NFTs that have been minted for this Entity
260 // to use as this NFT's serial number
261 let numInEntity = self.numMintedPerEntity[entityID]!
262
263 // Mint the new NFT
264 let newNFT: @NFT <- create NFT(mintNumber: numInEntity + UInt64(1),
265 entityID: entityID,
266 setID: self.setID,
267 minterAddress: minterAddress)
268
269 // Increment the number of copies minted for this NFT
270 self.numMintedPerEntity[entityID] = numInEntity + UInt64(1)
271
272 self.totalMinted = self.totalMinted + UInt64(1)
273
274 return <-newNFT
275 }
276
277 access(all) view fun getEntities(): [UInt64] {
278 return self.entities
279 }
280
281 access(all) view fun getRetired(): {UInt64: Bool} {
282 return self.retired
283 }
284
285 access(all) view fun getNumMintedPerEntity(): {UInt64: UInt64} {
286 return self.numMintedPerEntity
287 }
288
289 access(all) view fun getTotalMinted(): UInt64 {
290 return self.totalMinted
291 }
292 }
293
294 // Struct that contains all of the important data about a set
295 // Can be easily queried by instantiating the `QuerySetData` object
296 // with the desired set ID
297 // let setData = FlowversePass.QuerySetData(setID: 12)
298 //
299 access(all) struct QuerySetData {
300 access(all) let setID: UInt64
301 access(all) let name: String
302 access(all) let description: String
303 access(all) let externalURL: String
304 access(all) let imageIPFS: String
305 access(all) let isPrivate: Bool
306 access(self) var entities: [UInt64]
307 access(self) var retired: {UInt64: Bool}
308 access(all) var locked: Bool
309 access(self) var numMintedPerEntity: {UInt64: UInt64}
310 access(all) var totalMinted: UInt64
311
312 init(setID: UInt64) {
313 pre {
314 FlowversePass.sets[setID] != nil: "The set with the provided ID does not exist"
315 }
316
317 let set = (&FlowversePass.sets[setID] as &Set?)!
318 let setData = FlowversePass.setDatas[setID]!
319
320 self.setID = setID
321 self.name = setData.name
322 self.description = setData.description
323 self.externalURL = setData.externalURL
324 self.imageIPFS = setData.imageIPFS
325 self.entities = set.getEntities()
326 self.retired = set.getRetired()
327 self.locked = set.locked
328 self.numMintedPerEntity = set.getNumMintedPerEntity()
329 self.totalMinted = set.getTotalMinted()
330 self.isPrivate = setData.isPrivate
331 }
332
333 access(all) view fun getEntities(): [UInt64] {
334 return self.entities
335 }
336
337 access(all) view fun getRetired(): {UInt64: Bool} {
338 return self.retired
339 }
340
341 access(all) view fun getNumMintedPerEntity(): {UInt64: UInt64} {
342 return self.numMintedPerEntity
343 }
344 }
345
346 // NFT Resource that represents the Entity instances
347 access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
348 // Global unique NFT ID
349 access(all) let id: UInt64
350
351 // The ID of the Set that the NFT comes from
352 access(all) let setID: UInt64
353
354 // The ID of the Entity that the NFT references
355 access(all) let entityID: UInt64
356
357 // The minterAddress of the NFT
358 access(all) let minterAddress: Address
359
360 // The serial number of the NFT, number minted for this entity in the set
361 access(all) let mintNumber: UInt64
362
363 init(mintNumber: UInt64, entityID: UInt64, setID: UInt64, minterAddress: Address) {
364 // Increment the global NFT ID
365 FlowversePass.totalSupply = FlowversePass.totalSupply + UInt64(1)
366
367 self.id = FlowversePass.totalSupply
368
369 self.mintNumber = mintNumber
370 self.entityID = entityID
371 self.setID = setID
372 self.minterAddress = minterAddress
373
374 emit NFTMinted(nftID: self.id, nftUUID: self.uuid, entityID: entityID, setID: self.setID, mintNumber: self.mintNumber, minterAddress: self.minterAddress)
375 }
376
377 access(all) view fun name(): String {
378 let name: String = FlowversePass.getEntityMetaDataByField(entityID: self.entityID, field: "name") ?? ""
379 return name.concat(" #").concat(self.mintNumber.toString())
380 }
381
382 access(all) view fun getViews(): [Type] {
383 return [
384 Type<MetadataViews.Display>(),
385 Type<MetadataViews.Royalties>(),
386 Type<MetadataViews.Serial>(),
387 Type<MetadataViews.Edition>(),
388 Type<MetadataViews.ExternalURL>(),
389 Type<MetadataViews.Medias>()
390 ]
391 }
392
393 access(all) view fun resolveView(_ view: Type): AnyStruct? {
394 let fileExtension = FlowversePass.getEntityMetaDataByField(entityID: self.entityID, field: "fileExtension") ?? ""
395 switch view {
396 case Type<MetadataViews.Display>():
397 return MetadataViews.Display(
398 name: self.name(),
399 description: FlowversePass.getEntityMetaDataByField(entityID: self.entityID, field: "description") ?? "",
400 thumbnail: MetadataViews.HTTPFile(url: "https://flowverse-mystery-pass.s3.filebase.com/mainnet/nft_thumbnail.gif")
401 )
402 case Type<MetadataViews.Royalties>():
403 let feeReceiverAddress: Address = 0x604b63bcbef5974f
404 let feeCut: UFix64 = 0.05
405 let royalties : [MetadataViews.Royalty] = [
406 MetadataViews.Royalty(
407 receiver: getAccount(feeReceiverAddress).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!,
408 cut: feeCut,
409 description: "Creator Royalty Fee")
410 ]
411 return MetadataViews.Royalties(royalties)
412 case Type<MetadataViews.Serial>():
413 return MetadataViews.Serial(self.mintNumber)
414 case Type<MetadataViews.Edition>():
415 return MetadataViews.Edition(
416 name: FlowversePass.getEntityMetaDataByField(entityID: self.entityID, field: "name") ?? "",
417 number: self.mintNumber,
418 max: 1111
419 )
420 case Type<MetadataViews.ExternalURL>():
421 let baseURL = "https://nft.flowverse.co/collections/FlowversePass/"
422 return MetadataViews.ExternalURL(baseURL.concat(self.owner!.address.toString()).concat("/".concat(self.id.toString())))
423 case Type<MetadataViews.Medias>():
424 let video = MetadataViews.Media(
425 file: MetadataViews.HTTPFile(
426 url: "https://flowverse-mystery-pass.s3.filebase.com/mainnet/nft.".concat(fileExtension)
427 ),
428 mediaType: "video/".concat(fileExtension)
429 )
430 let medias: [MetadataViews.Media] = [video]
431 return MetadataViews.Medias(medias)
432 }
433
434 return nil
435 }
436
437 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
438 return <- FlowversePass.createEmptyCollection(nftType: Type<@FlowversePass.NFT>())
439 }
440 }
441
442 access(all) resource SetMinter {
443 access(all) let setID: UInt64
444
445 init(setID: UInt64) {
446 self.setID = setID
447 }
448
449 access(all) fun mint(entityID: UInt64, minterAddress: Address): @NFT {
450 let setRef = (&FlowversePass.sets[self.setID] as &Set?)!
451 return <- setRef.mint(entityID: entityID, minterAddress: minterAddress)
452 }
453 }
454
455 // Admin is a special authorization resource that
456 // allows the owner to perform important functions to modify the
457 // various aspects of the Entities, Sets, and NFTs
458 //
459 access(all) resource Admin {
460
461 // createEntity creates a new Entity struct
462 // and stores it in the Entities dictionary in the FlowversePass smart contract
463 access(all) fun createEntity(metadata: {String: String}): UInt64 {
464 // Create the new Entity
465 var newEntity = Entity(metadata: metadata)
466 let newID = newEntity.entityID
467
468 // Increment the ID so that it isn't used again
469 FlowversePass.nextEntityID = FlowversePass.nextEntityID + UInt64(1)
470
471 // Store it in the contract storage
472 FlowversePass.entityDatas[newID] = newEntity
473
474 emit EntityCreated(id: newID, metadata: metadata)
475
476 return newID
477 }
478
479 // createSet creates a new Set resource and stores it
480 // in the sets mapping in the contract
481 access(all) fun createSet(name: String, description: String, externalURL: String, imageIPFS: String, isPrivate: Bool): UInt64 {
482 // Create a new SetData for this Set
483 let setData = SetData(
484 setID: FlowversePass.nextSetID,
485 name: name,
486 description: description,
487 externalURL: externalURL,
488 imageIPFS: imageIPFS,
489 isPrivate: isPrivate
490 )
491
492 // Create the new Set
493 var newSet <- create Set()
494
495 // Increment the setID so that it isn't used again
496 FlowversePass.nextSetID = FlowversePass.nextSetID + UInt64(1)
497
498 let newID = newSet.setID
499
500 emit SetCreated(setID: newID, name: name, description: description, externalURL: externalURL, isPrivate: isPrivate, imageIPFS: imageIPFS)
501
502 // Store in contract storage
503 FlowversePass.setDatas[newID] = setData
504 FlowversePass.sets[newID] <-! newSet
505
506 return newID
507 }
508
509 // updateSetData updates set info including: description, externalURL, imageIPFS
510 access(all) fun updateSetData(setID: UInt64, description: String?, externalURL: String?, imageIPFS: String?) {
511 pre {
512 FlowversePass.setDatas.containsKey(setID): "Set data does not exist"
513 FlowversePass.sets.containsKey(setID): "Set data does not exist"
514 FlowversePass.sets[setID]?.locked == false: "Locked set data cannot be updated"
515 }
516 var setData = FlowversePass.setDatas[setID]!
517 let updatedSetData = SetData(
518 setID: setID,
519 name: setData.name,
520 description: description ?? setData.description,
521 externalURL: externalURL ?? setData.externalURL,
522 imageIPFS: imageIPFS ?? setData.imageIPFS,
523 isPrivate: setData.isPrivate
524 )
525 FlowversePass.setDatas[setID] = updatedSetData
526 emit SetUpdated(setID: setID, description: description, externalURL: externalURL, imageIPFS: imageIPFS)
527 }
528
529 // borrowSet returns a reference to a set in the contract
530 // so that the admin can call methods on it
531 access(all) view fun borrowSet(setID: UInt64): &Set {
532 pre {
533 FlowversePass.sets[setID] != nil: "Cannot borrow Set: The Set doesn't exist"
534 }
535
536 // Get a reference to the Set and return it
537 // use `&` to indicate the reference to the object and type
538 return (&FlowversePass.sets[setID] as &Set?)!
539 }
540
541 access(all) fun createSetMinter(setID: UInt64): @SetMinter {
542 return <- create SetMinter(setID: setID)
543 }
544
545 // createNewAdmin creates a new Admin resource
546 access(all) fun createNewAdmin(): @Admin {
547 return <-create Admin()
548 }
549 }
550
551 // Public interface for the FlowversePass Collection that allows users access to certain functionalities
552 access(all) resource interface CollectionPublic {
553 access(all) fun deposit(token: @{NonFungibleToken.NFT})
554 access(all) view fun getIDs(): [UInt64]
555 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
556 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?
557 }
558
559 // Collection
560 // A collection of FlowversePass NFTs owned by an account
561 access(all) resource Collection: CollectionPublic, NonFungibleToken.Collection {
562 // Dictionary of entity instances conforming tokens
563 // NFT is a resource type with a UInt64 ID field
564 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
565
566 init () {
567 self.ownedNFTs <- {}
568 }
569
570 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
571 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
572 let supportedTypes: {Type: Bool} = {}
573 supportedTypes[Type<@FlowversePass.NFT>()] = true
574 return supportedTypes
575 }
576
577 /// Returns whether or not the given type is accepted by the collection
578 /// A collection that can accept any type should just return true by default
579 access(all) view fun isSupportedNFTType(type: Type): Bool {
580 if type == Type<@FlowversePass.NFT>() {
581 return true
582 } else {
583 return false
584 }
585 }
586
587 /// Gets the amount of NFTs stored in the collection
588 access(all) view fun getLength(): Int {
589 return self.ownedNFTs.keys.length
590 }
591
592 /// Allows a given function to iterate through the list
593 /// of owned NFT IDs in a collection without first
594 /// having to load the entire list into memory
595 access(all) fun forEachID(_ f: fun (UInt64): Bool): Void {
596 self.ownedNFTs.forEachKey(f)
597 }
598
599 /// deposit takes a NFT and adds it to the collections dictionary
600 /// and adds the ID to the id array
601 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
602 let token <- token as! @FlowversePass.NFT
603 let id = token.id
604
605 // add the new token to the dictionary which removes the old one
606 let oldToken <- self.ownedNFTs[token.id] <- token
607
608 destroy oldToken
609 }
610
611 access(all) view fun getIDs(): [UInt64] {
612 return self.ownedNFTs.keys
613 }
614
615 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
616 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
617 }
618
619 /// Borrow the view resolver for the specified NFT ID
620 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
621 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
622 return nft as &{ViewResolver.Resolver}
623 }
624 return nil
625 }
626
627 /// createEmptyCollection creates an empty Collection of the same type
628 /// and returns it to the caller
629 /// @return A an empty collection of the same type
630 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
631 return <-FlowversePass.createEmptyCollection(nftType: Type<@FlowversePass.NFT>())
632 }
633
634 /// withdraw removes an NFT from the collection and moves it to the caller
635 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
636 let token <- self.ownedNFTs.remove(key: withdrawID)
637 ?? panic("Could not withdraw an NFT with the provided ID from the collection")
638
639 return <-token
640 }
641 }
642
643 // -----------------------------------------------------------------------
644 // FlowversePass contract-level function definitions
645 // -----------------------------------------------------------------------
646
647 /// createEmptyCollection creates an empty Collection for the specified NFT type
648 /// and returns it to the caller so that they can own NFTs
649 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
650 return <- create Collection()
651 }
652
653 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
654 ///
655 /// @return An array of Types defining the implemented views. This value will be used by
656 /// developers to know which parameter to pass to the resolveView() method.
657 ///
658 access(all) view fun getContractViews(resourceType: Type?): [Type] {
659 return [
660 Type<MetadataViews.NFTCollectionData>(),
661 Type<MetadataViews.NFTCollectionDisplay>()
662 ]
663 }
664
665 /// Function that resolves a metadata view for this contract.
666 ///
667 /// @param view: The Type of the desired view.
668 /// @return A structure representing the requested view.
669 ///
670 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
671 switch viewType {
672 case Type<MetadataViews.NFTCollectionData>():
673 return MetadataViews.NFTCollectionData(
674 storagePath: self.CollectionStoragePath,
675 publicPath: self.CollectionPublicPath,
676 publicCollection: Type<&FlowversePass.Collection>(),
677 publicLinkedType: Type<&FlowversePass.Collection>(),
678 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {return <- FlowversePass.createEmptyCollection(nftType: Type<@FlowversePass.NFT>())}),
679 )
680 case Type<MetadataViews.NFTCollectionDisplay>():
681 let squareImage = MetadataViews.Media(
682 file: MetadataViews.HTTPFile(
683 url: "https://flowverse-mystery-pass.s3.filebase.com/mainnet/squareImage.jpg"
684 ),
685 mediaType: "image/jpg"
686 )
687 let bannerImage = MetadataViews.Media(
688 file: MetadataViews.HTTPFile(
689 url: "https://flowverse-mystery-pass.s3.filebase.com/mainnet/bannerImage.jpg"
690 ),
691 mediaType: "image/jpg"
692 )
693 return MetadataViews.NFTCollectionDisplay(
694 name: "Flowverse Mystery Pass",
695 description: "The Flowverse Mystery Pass is a utility-focused membership pass that aims to level up Flowverse from a web2 information provider to a web3 brand",
696 externalURL: MetadataViews.ExternalURL("https://nft.flowverse.co/flowverse-mystery-pass"),
697 squareImage: squareImage,
698 bannerImage: bannerImage,
699 socials: {
700 "discord": MetadataViews.ExternalURL("https://discord.gg/flowverse"),
701 "twitter": MetadataViews.ExternalURL("https://twitter.com/flowverse_"),
702 "instagram": MetadataViews.ExternalURL("https://www.instagram.com/flowverseofficial")
703 }
704 )
705 }
706 return nil
707 }
708
709 // getAllEntities returns all the entities available
710 access(all) view fun getAllEntities(): [FlowversePass.Entity] {
711 return FlowversePass.entityDatas.values
712 }
713
714 // getEntity returns an entity by ID
715 access(all) view fun getEntity(entityID: UInt64): FlowversePass.Entity? {
716 return self.entityDatas[entityID]
717 }
718
719 // getEntityMetaData returns all the metadata associated with a specific Entity
720 access(all) view fun getEntityMetaData(entityID: UInt64): {String: String}? {
721 return self.entityDatas[entityID]?.metadata
722 }
723
724 access(all) view fun getEntityMetaDataByField(entityID: UInt64, field: String): String? {
725 if let entity = FlowversePass.entityDatas[entityID] {
726 return entity.metadata[field]
727 } else {
728 return nil
729 }
730 }
731
732
733 // getSetData returns the data that the specified Set
734 // is associated with.
735 //
736 // Parameters: setID: The id of the Set that is being searched
737 //
738 // Returns: The QuerySetData struct that has all the important information about the set
739 access(all) fun getSetData(setID: UInt64): QuerySetData? {
740 if FlowversePass.sets[setID] == nil {
741 return nil
742 } else {
743 return QuerySetData(setID: setID)
744 }
745 }
746
747 // getSetName returns the name that the specified Set
748 // is associated with.
749 //
750 // Parameters: setID: The id of the Set that is being searched
751 //
752 // Returns: The name of the Set
753 access(all) view fun getSetName(setID: UInt64): String? {
754 // Don't force a revert if the setID is invalid
755 return FlowversePass.setDatas[setID]?.name
756 }
757
758 // getSetIDsByName returns the IDs that the specified Set name
759 // is associated with.
760 access(all) fun getSetIDsByName(setName: String): [UInt64]? {
761 var setIDs: [UInt64] = []
762
763 for setData in FlowversePass.setDatas.values {
764 if setName == setData.name {
765 setIDs.append(setData.setID)
766 }
767 }
768
769 if setIDs.length == 0 {
770 return nil
771 } else {
772 return setIDs
773 }
774 }
775
776 // getAllSetDatas returns all the set datas available
777 access(all) view fun getAllSetDatas(): [SetData] {
778 return FlowversePass.setDatas.values
779 }
780
781 // getEntitiesInSet returns the list of Entity IDs that are in the Set
782 access(all) view fun getEntitiesInSet(setID: UInt64): [UInt64]? {
783 return FlowversePass.sets[setID]?.getEntities()
784 }
785
786 // isSetEntityRetired returns a boolean that indicates if a Set/Entity combination
787 // is retired.
788 // If an entity is retired, it still remains in the Set,
789 // but NFTs can no longer be minted from it.
790 access(all) fun isSetEntityRetired(setID: UInt64, entityID: UInt64): Bool? {
791 if let setdata = self.getSetData(setID: setID) {
792 // See if the Entity is retired from this Set
793 let retired = setdata.getRetired()[entityID]
794
795 // Return the retired status
796 return retired
797 } else {
798 // If the Set wasn't found, return nil
799 return nil
800 }
801 }
802
803 access(all) view fun isSetLocked(setID: UInt64): Bool? {
804 return FlowversePass.sets[setID]?.locked
805 }
806
807 // getNumInstancesOfEntity return the number of entity instances that have been
808 // minted in a set.
809 //
810 // Parameters: setID: The id of the Set that is being searched
811 // entityID: The id of the Entity that is being searched
812 //
813 // Returns: The total number of entity instances (NFTs)
814 // that have been minted in a set
815 access(all) fun getNumInstancesOfEntity(setID: UInt64, entityID: UInt64): UInt64? {
816 if let setdata = self.getSetData(setID: setID) {
817 // return numMintedPerEntity
818 return setdata.getNumMintedPerEntity()[entityID]
819 } else {
820 // If the set wasn't found return nil
821 return nil
822 }
823 }
824
825 // -----------------------------------------------------------------------
826 // FlowversePass initialization function
827 // -----------------------------------------------------------------------
828 //
829 init() {
830 self.AdminStoragePath = /storage/FlowversePassAdmin
831 self.CollectionStoragePath = /storage/FlowversePassCollection
832 self.CollectionPublicPath = /public/FlowversePassCollection
833
834 // Initialize contract fields
835 self.entityDatas = {}
836 self.setDatas = {}
837 self.sets <- {}
838 self.nextEntityID = 1
839 self.nextSetID = 1
840 self.totalSupply = 0
841
842 // Create and save a new Collection in storage
843 let collection <- create Collection()
844 self.account.storage.save(<-collection, to: self.CollectionStoragePath)
845
846 // Issue a public capability for the Collection
847 let collectionCap = self.account.capabilities.storage.issue<&FlowversePass.Collection>(self.CollectionStoragePath)
848 self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
849
850 // Create and save Admin resource in storage
851 self.account.storage.save<@Admin>(<- create Admin(), to: self.AdminStoragePath)
852 }
853}
854