@flowtyio/flow-contracts 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,740 @@
1
+ import "FungibleToken"
2
+ import "NonFungibleToken"
3
+
4
+ /// This contract implements the metadata standard proposed
5
+ /// in FLIP-0636.
6
+ ///
7
+ /// Ref: https://github.com/onflow/flips/blob/main/application/20210916-nft-metadata.md
8
+ ///
9
+ /// Structs and resources can implement one or more
10
+ /// metadata types, called views. Each view type represents
11
+ /// a different kind of metadata, such as a creator biography
12
+ /// or a JPEG image file.
13
+ ///
14
+ pub contract MetadataViews {
15
+
16
+ /// Provides access to a set of metadata views. A struct or
17
+ /// resource (e.g. an NFT) can implement this interface to provide access to
18
+ /// the views that it supports.
19
+ ///
20
+ pub resource interface Resolver {
21
+ pub fun getViews(): [Type]
22
+ pub fun resolveView(_ view: Type): AnyStruct?
23
+ }
24
+
25
+ /// A group of view resolvers indexed by ID.
26
+ ///
27
+ pub resource interface ResolverCollection {
28
+ pub fun borrowViewResolver(id: UInt64): &{Resolver}
29
+ pub fun getIDs(): [UInt64]
30
+ }
31
+
32
+ /// NFTView wraps all Core views along `id` and `uuid` fields, and is used
33
+ /// to give a complete picture of an NFT. Most NFTs should implement this
34
+ /// view.
35
+ ///
36
+ pub struct NFTView {
37
+ pub let id: UInt64
38
+ pub let uuid: UInt64
39
+ pub let display: Display?
40
+ pub let externalURL: ExternalURL?
41
+ pub let collectionData: NFTCollectionData?
42
+ pub let collectionDisplay: NFTCollectionDisplay?
43
+ pub let royalties: Royalties?
44
+ pub let traits: Traits?
45
+
46
+ init(
47
+ id : UInt64,
48
+ uuid : UInt64,
49
+ display : Display?,
50
+ externalURL : ExternalURL?,
51
+ collectionData : NFTCollectionData?,
52
+ collectionDisplay : NFTCollectionDisplay?,
53
+ royalties : Royalties?,
54
+ traits: Traits?
55
+ ) {
56
+ self.id = id
57
+ self.uuid = uuid
58
+ self.display = display
59
+ self.externalURL = externalURL
60
+ self.collectionData = collectionData
61
+ self.collectionDisplay = collectionDisplay
62
+ self.royalties = royalties
63
+ self.traits = traits
64
+ }
65
+ }
66
+
67
+ /// Helper to get an NFT view
68
+ ///
69
+ /// @param id: The NFT id
70
+ /// @param viewResolver: A reference to the resolver resource
71
+ /// @return A NFTView struct
72
+ ///
73
+ pub fun getNFTView(id: UInt64, viewResolver: &{Resolver}) : NFTView {
74
+ let nftView = viewResolver.resolveView(Type<NFTView>())
75
+ if nftView != nil {
76
+ return nftView! as! NFTView
77
+ }
78
+
79
+ return NFTView(
80
+ id : id,
81
+ uuid: viewResolver.uuid,
82
+ display: self.getDisplay(viewResolver),
83
+ externalURL : self.getExternalURL(viewResolver),
84
+ collectionData : self.getNFTCollectionData(viewResolver),
85
+ collectionDisplay : self.getNFTCollectionDisplay(viewResolver),
86
+ royalties : self.getRoyalties(viewResolver),
87
+ traits : self.getTraits(viewResolver)
88
+ )
89
+ }
90
+
91
+ /// Display is a basic view that includes the name, description and
92
+ /// thumbnail for an object. Most objects should implement this view.
93
+ ///
94
+ pub struct Display {
95
+
96
+ /// The name of the object.
97
+ ///
98
+ /// This field will be displayed in lists and therefore should
99
+ /// be short an concise.
100
+ ///
101
+ pub let name: String
102
+
103
+ /// A written description of the object.
104
+ ///
105
+ /// This field will be displayed in a detailed view of the object,
106
+ /// so can be more verbose (e.g. a paragraph instead of a single line).
107
+ ///
108
+ pub let description: String
109
+
110
+ /// A small thumbnail representation of the object.
111
+ ///
112
+ /// This field should be a web-friendly file (i.e JPEG, PNG)
113
+ /// that can be displayed in lists, link previews, etc.
114
+ ///
115
+ pub let thumbnail: AnyStruct{File}
116
+
117
+ init(
118
+ name: String,
119
+ description: String,
120
+ thumbnail: AnyStruct{File}
121
+ ) {
122
+ self.name = name
123
+ self.description = description
124
+ self.thumbnail = thumbnail
125
+ }
126
+ }
127
+
128
+ /// Helper to get Display in a typesafe way
129
+ ///
130
+ /// @param viewResolver: A reference to the resolver resource
131
+ /// @return An optional Display struct
132
+ ///
133
+ pub fun getDisplay(_ viewResolver: &{Resolver}) : Display? {
134
+ if let view = viewResolver.resolveView(Type<Display>()) {
135
+ if let v = view as? Display {
136
+ return v
137
+ }
138
+ }
139
+ return nil
140
+ }
141
+
142
+ /// Generic interface that represents a file stored on or off chain. Files
143
+ /// can be used to references images, videos and other media.
144
+ ///
145
+ pub struct interface File {
146
+ pub fun uri(): String
147
+ }
148
+
149
+ /// View to expose a file that is accessible at an HTTP (or HTTPS) URL.
150
+ ///
151
+ pub struct HTTPFile: File {
152
+ pub let url: String
153
+
154
+ init(url: String) {
155
+ self.url = url
156
+ }
157
+
158
+ pub fun uri(): String {
159
+ return self.url
160
+ }
161
+ }
162
+
163
+ /// View to expose a file stored on IPFS.
164
+ /// IPFS images are referenced by their content identifier (CID)
165
+ /// rather than a direct URI. A client application can use this CID
166
+ /// to find and load the image via an IPFS gateway.
167
+ ///
168
+ pub struct IPFSFile: File {
169
+
170
+ /// CID is the content identifier for this IPFS file.
171
+ ///
172
+ /// Ref: https://docs.ipfs.io/concepts/content-addressing/
173
+ ///
174
+ pub let cid: String
175
+
176
+ /// Path is an optional path to the file resource in an IPFS directory.
177
+ ///
178
+ /// This field is only needed if the file is inside a directory.
179
+ ///
180
+ /// Ref: https://docs.ipfs.io/concepts/file-systems/
181
+ ///
182
+ pub let path: String?
183
+
184
+ init(cid: String, path: String?) {
185
+ self.cid = cid
186
+ self.path = path
187
+ }
188
+
189
+ /// This function returns the IPFS native URL for this file.
190
+ /// Ref: https://docs.ipfs.io/how-to/address-ipfs-on-web/#native-urls
191
+ ///
192
+ /// @return The string containing the file uri
193
+ ///
194
+ pub fun uri(): String {
195
+ if let path = self.path {
196
+ return "ipfs://".concat(self.cid).concat("/").concat(path)
197
+ }
198
+
199
+ return "ipfs://".concat(self.cid)
200
+ }
201
+ }
202
+
203
+ /// Optional view for collections that issue multiple objects
204
+ /// with the same or similar metadata, for example an X of 100 set. This
205
+ /// information is useful for wallets and marketplaces.
206
+ /// An NFT might be part of multiple editions, which is why the edition
207
+ /// information is returned as an arbitrary sized array
208
+ ///
209
+ pub struct Edition {
210
+
211
+ /// The name of the edition
212
+ /// For example, this could be Set, Play, Series,
213
+ /// or any other way a project could classify its editions
214
+ pub let name: String?
215
+
216
+ /// The edition number of the object.
217
+ /// For an "24 of 100 (#24/100)" item, the number is 24.
218
+ pub let number: UInt64
219
+
220
+ /// The max edition number of this type of objects.
221
+ /// This field should only be provided for limited-editioned objects.
222
+ /// For an "24 of 100 (#24/100)" item, max is 100.
223
+ /// For an item with unlimited edition, max should be set to nil.
224
+ ///
225
+ pub let max: UInt64?
226
+
227
+ init(name: String?, number: UInt64, max: UInt64?) {
228
+ if max != nil {
229
+ assert(number <= max!, message: "The number cannot be greater than the max number!")
230
+ }
231
+ self.name = name
232
+ self.number = number
233
+ self.max = max
234
+ }
235
+ }
236
+
237
+ /// Wrapper view for multiple Edition views
238
+ ///
239
+ pub struct Editions {
240
+
241
+ /// An arbitrary-sized list for any number of editions
242
+ /// that the NFT might be a part of
243
+ pub let infoList: [Edition]
244
+
245
+ init(_ infoList: [Edition]) {
246
+ self.infoList = infoList
247
+ }
248
+ }
249
+
250
+ /// Helper to get Editions in a typesafe way
251
+ ///
252
+ /// @param viewResolver: A reference to the resolver resource
253
+ /// @return An optional Editions struct
254
+ ///
255
+ pub fun getEditions(_ viewResolver: &{Resolver}) : Editions? {
256
+ if let view = viewResolver.resolveView(Type<Editions>()) {
257
+ if let v = view as? Editions {
258
+ return v
259
+ }
260
+ }
261
+ return nil
262
+ }
263
+
264
+ /// View representing a project-defined serial number for a specific NFT
265
+ /// Projects have different definitions for what a serial number should be
266
+ /// Some may use the NFTs regular ID and some may use a different
267
+ /// classification system. The serial number is expected to be unique among
268
+ /// other NFTs within that project
269
+ ///
270
+ pub struct Serial {
271
+ pub let number: UInt64
272
+
273
+ init(_ number: UInt64) {
274
+ self.number = number
275
+ }
276
+ }
277
+
278
+ /// Helper to get Serial in a typesafe way
279
+ ///
280
+ /// @param viewResolver: A reference to the resolver resource
281
+ /// @return An optional Serial struct
282
+ ///
283
+ pub fun getSerial(_ viewResolver: &{Resolver}) : Serial? {
284
+ if let view = viewResolver.resolveView(Type<Serial>()) {
285
+ if let v = view as? Serial {
286
+ return v
287
+ }
288
+ }
289
+ return nil
290
+ }
291
+
292
+ /// View that defines the composable royalty standard that gives marketplaces a
293
+ /// unified interface to support NFT royalties.
294
+ ///
295
+ pub struct Royalty {
296
+
297
+ /// Generic FungibleToken Receiver for the beneficiary of the royalty
298
+ /// Can get the concrete type of the receiver with receiver.getType()
299
+ /// Recommendation - Users should create a new link for a FlowToken
300
+ /// receiver for this using `getRoyaltyReceiverPublicPath()`, and not
301
+ /// use the default FlowToken receiver. This will allow users to update
302
+ /// the capability in the future to use a more generic capability
303
+ pub let receiver: Capability<&AnyResource{FungibleToken.Receiver}>
304
+
305
+ /// Multiplier used to calculate the amount of sale value transferred to
306
+ /// royalty receiver. Note - It should be between 0.0 and 1.0
307
+ /// Ex - If the sale value is x and multiplier is 0.56 then the royalty
308
+ /// value would be 0.56 * x.
309
+ /// Generally percentage get represented in terms of basis points
310
+ /// in solidity based smart contracts while cadence offers `UFix64`
311
+ /// that already supports the basis points use case because its
312
+ /// operations are entirely deterministic integer operations and support
313
+ /// up to 8 points of precision.
314
+ pub let cut: UFix64
315
+
316
+ /// Optional description: This can be the cause of paying the royalty,
317
+ /// the relationship between the `wallet` and the NFT, or anything else
318
+ /// that the owner might want to specify.
319
+ pub let description: String
320
+
321
+ init(receiver: Capability<&AnyResource{FungibleToken.Receiver}>, cut: UFix64, description: String) {
322
+ pre {
323
+ cut >= 0.0 && cut <= 1.0 : "Cut value should be in valid range i.e [0,1]"
324
+ }
325
+ self.receiver = receiver
326
+ self.cut = cut
327
+ self.description = description
328
+ }
329
+ }
330
+
331
+ /// Wrapper view for multiple Royalty views.
332
+ /// Marketplaces can query this `Royalties` struct from NFTs
333
+ /// and are expected to pay royalties based on these specifications.
334
+ ///
335
+ pub struct Royalties {
336
+
337
+ /// Array that tracks the individual royalties
338
+ access(self) let cutInfos: [Royalty]
339
+
340
+ pub init(_ cutInfos: [Royalty]) {
341
+ // Validate that sum of all cut multipliers should not be greater than 1.0
342
+ var totalCut = 0.0
343
+ for royalty in cutInfos {
344
+ totalCut = totalCut + royalty.cut
345
+ }
346
+ assert(totalCut <= 1.0, message: "Sum of cutInfos multipliers should not be greater than 1.0")
347
+ // Assign the cutInfos
348
+ self.cutInfos = cutInfos
349
+ }
350
+
351
+ /// Return the cutInfos list
352
+ ///
353
+ /// @return An array containing all the royalties structs
354
+ ///
355
+ pub fun getRoyalties(): [Royalty] {
356
+ return self.cutInfos
357
+ }
358
+ }
359
+
360
+ /// Helper to get Royalties in a typesafe way
361
+ ///
362
+ /// @param viewResolver: A reference to the resolver resource
363
+ /// @return A optional Royalties struct
364
+ ///
365
+ pub fun getRoyalties(_ viewResolver: &{Resolver}) : Royalties? {
366
+ if let view = viewResolver.resolveView(Type<Royalties>()) {
367
+ if let v = view as? Royalties {
368
+ return v
369
+ }
370
+ }
371
+ return nil
372
+ }
373
+
374
+ /// Get the path that should be used for receiving royalties
375
+ /// This is a path that will eventually be used for a generic switchboard receiver,
376
+ /// hence the name but will only be used for royalties for now.
377
+ ///
378
+ /// @return The PublicPath for the generic FT receiver
379
+ ///
380
+ pub fun getRoyaltyReceiverPublicPath(): PublicPath {
381
+ return /public/GenericFTReceiver
382
+ }
383
+
384
+ /// View to represent, a file with an correspoiding mediaType.
385
+ ///
386
+ pub struct Media {
387
+
388
+ /// File for the media
389
+ ///
390
+ pub let file: AnyStruct{File}
391
+
392
+ /// media-type comes on the form of type/subtype as described here
393
+ /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
394
+ ///
395
+ pub let mediaType: String
396
+
397
+ init(file: AnyStruct{File}, mediaType: String) {
398
+ self.file=file
399
+ self.mediaType=mediaType
400
+ }
401
+ }
402
+
403
+ /// Wrapper view for multiple media views
404
+ ///
405
+ pub struct Medias {
406
+
407
+ /// An arbitrary-sized list for any number of Media items
408
+ pub let items: [Media]
409
+
410
+ init(_ items: [Media]) {
411
+ self.items = items
412
+ }
413
+ }
414
+
415
+ /// Helper to get Medias in a typesafe way
416
+ ///
417
+ /// @param viewResolver: A reference to the resolver resource
418
+ /// @return A optional Medias struct
419
+ ///
420
+ pub fun getMedias(_ viewResolver: &{Resolver}) : Medias? {
421
+ if let view = viewResolver.resolveView(Type<Medias>()) {
422
+ if let v = view as? Medias {
423
+ return v
424
+ }
425
+ }
426
+ return nil
427
+ }
428
+
429
+ /// View to represent a license according to https://spdx.org/licenses/
430
+ /// This view can be used if the content of an NFT is licensed.
431
+ ///
432
+ pub struct License {
433
+ pub let spdxIdentifier: String
434
+
435
+ init(_ identifier: String) {
436
+ self.spdxIdentifier = identifier
437
+ }
438
+ }
439
+
440
+ /// Helper to get License in a typesafe way
441
+ ///
442
+ /// @param viewResolver: A reference to the resolver resource
443
+ /// @return A optional License struct
444
+ ///
445
+ pub fun getLicense(_ viewResolver: &{Resolver}) : License? {
446
+ if let view = viewResolver.resolveView(Type<License>()) {
447
+ if let v = view as? License {
448
+ return v
449
+ }
450
+ }
451
+ return nil
452
+ }
453
+
454
+ /// View to expose a URL to this item on an external site.
455
+ /// This can be used by applications like .find and Blocto to direct users
456
+ /// to the original link for an NFT or a project page that describes the NFT collection.
457
+ /// eg https://www.my-nft-project.com/overview-of-nft-collection
458
+ ///
459
+ pub struct ExternalURL {
460
+ pub let url: String
461
+
462
+ init(_ url: String) {
463
+ self.url=url
464
+ }
465
+ }
466
+
467
+ /// Helper to get ExternalURL in a typesafe way
468
+ ///
469
+ /// @param viewResolver: A reference to the resolver resource
470
+ /// @return A optional ExternalURL struct
471
+ ///
472
+ pub fun getExternalURL(_ viewResolver: &{Resolver}) : ExternalURL? {
473
+ if let view = viewResolver.resolveView(Type<ExternalURL>()) {
474
+ if let v = view as? ExternalURL {
475
+ return v
476
+ }
477
+ }
478
+ return nil
479
+ }
480
+
481
+ /// View to expose the information needed store and retrieve an NFT.
482
+ /// This can be used by applications to setup a NFT collection with proper
483
+ /// storage and public capabilities.
484
+ ///
485
+ pub struct NFTCollectionData {
486
+ /// Path in storage where this NFT is recommended to be stored.
487
+ pub let storagePath: StoragePath
488
+
489
+ /// Public path which must be linked to expose public capabilities of this NFT
490
+ /// including standard NFT interfaces and metadataviews interfaces
491
+ pub let publicPath: PublicPath
492
+
493
+ /// Private path which should be linked to expose the provider
494
+ /// capability to withdraw NFTs from the collection holding NFTs
495
+ pub let providerPath: PrivatePath
496
+
497
+ /// Public collection type that is expected to provide sufficient read-only access to standard
498
+ /// functions (deposit + getIDs + borrowNFT)
499
+ /// This field is for backwards compatibility with collections that have not used the standard
500
+ /// NonFungibleToken.CollectionPublic interface when setting up collections. For new
501
+ /// collections, this may be set to be equal to the type specified in `publicLinkedType`.
502
+ pub let publicCollection: Type
503
+
504
+ /// Type that should be linked at the aforementioned public path. This is normally a
505
+ /// restricted type with many interfaces. Notably the `NFT.CollectionPublic`,
506
+ /// `NFT.Receiver`, and `MetadataViews.ResolverCollection` interfaces are required.
507
+ pub let publicLinkedType: Type
508
+
509
+ /// Type that should be linked at the aforementioned private path. This is normally
510
+ /// a restricted type with at a minimum the `NFT.Provider` interface
511
+ pub let providerLinkedType: Type
512
+
513
+ /// Function that allows creation of an empty NFT collection that is intended to store
514
+ /// this NFT.
515
+ pub let createEmptyCollection: ((): @NonFungibleToken.Collection)
516
+
517
+ init(
518
+ storagePath: StoragePath,
519
+ publicPath: PublicPath,
520
+ providerPath: PrivatePath,
521
+ publicCollection: Type,
522
+ publicLinkedType: Type,
523
+ providerLinkedType: Type,
524
+ createEmptyCollectionFunction: ((): @NonFungibleToken.Collection)
525
+ ) {
526
+ pre {
527
+ publicLinkedType.isSubtype(of: Type<&{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, MetadataViews.ResolverCollection}>()): "Public type must include NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, and MetadataViews.ResolverCollection interfaces."
528
+ providerLinkedType.isSubtype(of: Type<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>()): "Provider type must include NonFungibleToken.Provider, NonFungibleToken.CollectionPublic, and MetadataViews.ResolverCollection interface."
529
+ }
530
+ self.storagePath=storagePath
531
+ self.publicPath=publicPath
532
+ self.providerPath = providerPath
533
+ self.publicCollection=publicCollection
534
+ self.publicLinkedType=publicLinkedType
535
+ self.providerLinkedType = providerLinkedType
536
+ self.createEmptyCollection=createEmptyCollectionFunction
537
+ }
538
+ }
539
+
540
+ /// Helper to get NFTCollectionData in a way that will return an typed Optional
541
+ ///
542
+ /// @param viewResolver: A reference to the resolver resource
543
+ /// @return A optional NFTCollectionData struct
544
+ ///
545
+ pub fun getNFTCollectionData(_ viewResolver: &{Resolver}) : NFTCollectionData? {
546
+ if let view = viewResolver.resolveView(Type<NFTCollectionData>()) {
547
+ if let v = view as? NFTCollectionData {
548
+ return v
549
+ }
550
+ }
551
+ return nil
552
+ }
553
+
554
+ /// View to expose the information needed to showcase this NFT's
555
+ /// collection. This can be used by applications to give an overview and
556
+ /// graphics of the NFT collection this NFT belongs to.
557
+ ///
558
+ pub struct NFTCollectionDisplay {
559
+ // Name that should be used when displaying this NFT collection.
560
+ pub let name: String
561
+
562
+ // Description that should be used to give an overview of this collection.
563
+ pub let description: String
564
+
565
+ // External link to a URL to view more information about this collection.
566
+ pub let externalURL: ExternalURL
567
+
568
+ // Square-sized image to represent this collection.
569
+ pub let squareImage: Media
570
+
571
+ // Banner-sized image for this collection, recommended to have a size near 1200x630.
572
+ pub let bannerImage: Media
573
+
574
+ // Social links to reach this collection's social homepages.
575
+ // Possible keys may be "instagram", "twitter", "discord", etc.
576
+ pub let socials: {String: ExternalURL}
577
+
578
+ init(
579
+ name: String,
580
+ description: String,
581
+ externalURL: ExternalURL,
582
+ squareImage: Media,
583
+ bannerImage: Media,
584
+ socials: {String: ExternalURL}
585
+ ) {
586
+ self.name = name
587
+ self.description = description
588
+ self.externalURL = externalURL
589
+ self.squareImage = squareImage
590
+ self.bannerImage = bannerImage
591
+ self.socials = socials
592
+ }
593
+ }
594
+
595
+ /// Helper to get NFTCollectionDisplay in a way that will return a typed
596
+ /// Optional
597
+ ///
598
+ /// @param viewResolver: A reference to the resolver resource
599
+ /// @return A optional NFTCollection struct
600
+ ///
601
+ pub fun getNFTCollectionDisplay(_ viewResolver: &{Resolver}) : NFTCollectionDisplay? {
602
+ if let view = viewResolver.resolveView(Type<NFTCollectionDisplay>()) {
603
+ if let v = view as? NFTCollectionDisplay {
604
+ return v
605
+ }
606
+ }
607
+ return nil
608
+ }
609
+
610
+ /// View to expose rarity information for a single rarity
611
+ /// Note that a rarity needs to have either score or description but it can
612
+ /// have both
613
+ ///
614
+ pub struct Rarity {
615
+ /// The score of the rarity as a number
616
+ pub let score: UFix64?
617
+
618
+ /// The maximum value of score
619
+ pub let max: UFix64?
620
+
621
+ /// The description of the rarity as a string.
622
+ ///
623
+ /// This could be Legendary, Epic, Rare, Uncommon, Common or any other string value
624
+ pub let description: String?
625
+
626
+ init(score: UFix64?, max: UFix64?, description: String?) {
627
+ if score == nil && description == nil {
628
+ panic("A Rarity needs to set score, description or both")
629
+ }
630
+
631
+ self.score = score
632
+ self.max = max
633
+ self.description = description
634
+ }
635
+ }
636
+
637
+ /// Helper to get Rarity view in a typesafe way
638
+ ///
639
+ /// @param viewResolver: A reference to the resolver resource
640
+ /// @return A optional Rarity struct
641
+ ///
642
+ pub fun getRarity(_ viewResolver: &{Resolver}) : Rarity? {
643
+ if let view = viewResolver.resolveView(Type<Rarity>()) {
644
+ if let v = view as? Rarity {
645
+ return v
646
+ }
647
+ }
648
+ return nil
649
+ }
650
+
651
+ /// View to represent a single field of metadata on an NFT.
652
+ /// This is used to get traits of individual key/value pairs along with some
653
+ /// contextualized data about the trait
654
+ ///
655
+ pub struct Trait {
656
+ // The name of the trait. Like Background, Eyes, Hair, etc.
657
+ pub let name: String
658
+
659
+ // The underlying value of the trait, the rest of the fields of a trait provide context to the value.
660
+ pub let value: AnyStruct
661
+
662
+ // displayType is used to show some context about what this name and value represent
663
+ // for instance, you could set value to a unix timestamp, and specify displayType as "Date" to tell
664
+ // platforms to consume this trait as a date and not a number
665
+ pub let displayType: String?
666
+
667
+ // Rarity can also be used directly on an attribute.
668
+ //
669
+ // This is optional because not all attributes need to contribute to the NFT's rarity.
670
+ pub let rarity: Rarity?
671
+
672
+ init(name: String, value: AnyStruct, displayType: String?, rarity: Rarity?) {
673
+ self.name = name
674
+ self.value = value
675
+ self.displayType = displayType
676
+ self.rarity = rarity
677
+ }
678
+ }
679
+
680
+ /// Wrapper view to return all the traits on an NFT.
681
+ /// This is used to return traits as individual key/value pairs along with
682
+ /// some contextualized data about each trait.
683
+ pub struct Traits {
684
+ pub let traits: [Trait]
685
+
686
+ init(_ traits: [Trait]) {
687
+ self.traits = traits
688
+ }
689
+
690
+ /// Adds a single Trait to the Traits view
691
+ ///
692
+ /// @param Trait: The trait struct to be added
693
+ ///
694
+ pub fun addTrait(_ t: Trait) {
695
+ self.traits.append(t)
696
+ }
697
+ }
698
+
699
+ /// Helper to get Traits view in a typesafe way
700
+ ///
701
+ /// @param viewResolver: A reference to the resolver resource
702
+ /// @return A optional Traits struct
703
+ ///
704
+ pub fun getTraits(_ viewResolver: &{Resolver}) : Traits? {
705
+ if let view = viewResolver.resolveView(Type<Traits>()) {
706
+ if let v = view as? Traits {
707
+ return v
708
+ }
709
+ }
710
+ return nil
711
+ }
712
+
713
+ /// Helper function to easily convert a dictionary to traits. For NFT
714
+ /// collections that do not need either of the optional values of a Trait,
715
+ /// this method should suffice to give them an array of valid traits.
716
+ ///
717
+ /// @param dict: The dictionary to be converted to Traits
718
+ /// @param excludedNames: An optional String array specifying the `dict`
719
+ /// keys that are not wanted to become `Traits`
720
+ /// @return The generated Traits view
721
+ ///
722
+ pub fun dictToTraits(dict: {String: AnyStruct}, excludedNames: [String]?): Traits {
723
+ // Collection owners might not want all the fields in their metadata included.
724
+ // They might want to handle some specially, or they might just not want them included at all.
725
+ if excludedNames != nil {
726
+ for k in excludedNames! {
727
+ dict.remove(key: k)
728
+ }
729
+ }
730
+
731
+ let traits: [Trait] = []
732
+ for k in dict.keys {
733
+ let trait = Trait(name: k, value: dict[k]!, displayType: nil, rarity: nil)
734
+ traits.append(trait)
735
+ }
736
+
737
+ return Traits(traits)
738
+ }
739
+
740
+ }