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