Smart Contract

AeraNFT

A.30cf5dcf6ea8d379.AeraNFT

Deployed

1w ago
Feb 15, 2026, 08:33:04 PM UTC

Dependents

3468 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import ViewResolver from 0x1d7e57aa55817448 
3import MetadataViews from 0x1d7e57aa55817448
4import FungibleToken from 0xf233dcee88fe0abe 
5import FindViews from 0x097bafa4e0b48eef
6
7
8access(all)
9contract AeraNFT: NonFungibleToken{ 
10
11    access(all) entitlement Owner
12
13    access(all)
14    var totalSupply: UInt64
15
16    access(all)
17    event ContractInitialized()
18
19    access(all)
20    event Withdraw(id: UInt64, from: Address?)
21
22    access(all)
23    event Deposit(id: UInt64, to: Address?)
24
25    access(all)
26    event Minted(id: UInt64, address: Address)
27
28    access(all)
29    event Burned(id: UInt64, from: Address?, playId: UInt64, edition: UInt64)
30
31    access(all)
32    let CollectionStoragePath: StoragePath
33
34    access(all)
35    let CollectionPublicPath: PublicPath
36
37    access(all)
38    let CollectionPrivatePath: PrivatePath
39
40    access(all)
41    resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver{ 
42        access(all)
43        let id: UInt64
44
45        access(all)
46        var nounce: UInt64
47
48        access(all)
49        let play: Play
50
51        access(all)
52        let edition: UInt64
53
54        access(all)
55        let badges:{ UInt64: Badge}
56
57        init(play: Play, edition: UInt64){ 
58            self.nounce = 0
59            self.id = self.uuid
60            self.play = play
61            self.edition = edition
62            self.badges ={} 
63        }
64
65        access(account)
66        fun addBadge(_ badge: Badge){ 
67            self.badges[badge.id] = badge
68        }
69
70        access(all)
71        view fun getViews(): [Type]{ 
72            let views = [Type<MetadataViews.Display>(), Type<License>(), Type<MetadataViews.Editions>(), Type<MetadataViews.Medias>(), Type<MetadataViews.Rarity>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Traits>(),  Type<MetadataViews.ExternalURL>(), Type<MetadataViews.Royalties>(), Type<Play>()]
73
74            return views
75        }
76
77        access(all)
78        view fun isSoulBound(): Bool{ 
79            return self.badges[1] != nil && (self.badges[1]!).name == "soulbound"
80        }
81
82        access(all)
83        fun resolveView(_ view: Type): AnyStruct?{ 
84            let play = self.play
85            switch view { 
86            case Type<FindViews.SoulBound>():
87                if self.isSoulBound(){ 
88                    return FindViews.SoulBound("This NFT cannot be traded, it is soulbound")
89                }
90                return nil
91            case Type<Play>():
92                return play
93            case Type<License>():
94                if let licence = play.getLicenseID(){ 
95                    return AeraNFT.getLicense(licence)!
96                } else{ 
97                    return nil
98                }
99            case Type<MetadataViews.NFTCollectionData>():
100                return MetadataViews.NFTCollectionData(storagePath: AeraNFT.CollectionStoragePath, publicPath: AeraNFT.CollectionPublicPath, publicCollection: Type<&AeraNFT.Collection>(), publicLinkedType: Type<&AeraNFT.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
101                    return <-AeraNFT.createEmptyCollection(nftType: Type<@AeraNFT.Collection>())
102                })
103            case Type<MetadataViews.NFTCollectionDisplay>():
104                let externalURL = MetadataViews.ExternalURL("http://aera.onefootbal.com")
105                let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://miro.medium.com/fit/c/176/176/1*hzfP51MATg3et5vghYy1uQ.png"), mediaType: "image/png")
106                let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://bafybeiaaf7bfbppep5ahb2m75xlmxyj76hxo3oh5dcl4prxeskruwpe6c4.ipfs.nftstorage.link/"), mediaType: "image/png")
107                let socialMap:{ String: MetadataViews.ExternalURL} ={ "twitter": MetadataViews.ExternalURL("https://twitter.com/aera_football"), "discord": MetadataViews.ExternalURL("https://discord.gg/aera"), "instagram": MetadataViews.ExternalURL("https://www.instagram.com/aera_football/")}
108                return MetadataViews.NFTCollectionDisplay(name: "Aera", description: "Aera by OneFootball", externalURL: externalURL, squareImage: squareImage, bannerImage: bannerImage, socials: socialMap)
109            case Type<MetadataViews.Display>():
110                return play.asDisplay()
111            case Type<MetadataViews.Medias>():
112                return play.getMedias()
113            case Type<MetadataViews.Editions>():
114                return MetadataViews.Editions([MetadataViews.Edition(name: "play", number: self.edition, max: play.maxSerial)])
115            case Type<MetadataViews.Rarity>():
116                var rarity: UFix64 = 0.0
117                switch play.rarity{ 
118                case "Common":
119                    rarity = 1.0
120                case "Rare":
121                    rarity = 2.0
122                case "Epic":
123                    rarity = 3.0
124                case "Legendary":
125                    rarity = 4.0
126                }
127                return MetadataViews.Rarity(score: rarity, max: nil, description: play.rarity)
128            case Type<MetadataViews.Traits>():
129                let traits = play.getTraits()
130                for badgeID in self.badges.keys{ 
131                    let badge = self.badges[badgeID]!
132                    let value = badge.name!
133                    traits.append(MetadataViews.Trait(name: "badge_".concat(badge.name!), value: value, displayType: "String", rarity: badge.rarity))
134                }
135                return MetadataViews.Traits(traits)
136            case Type<MetadataViews.Royalties>():
137                var address = AeraNFT.account.address
138                if address == 0x46625f59708ec2f8{ 
139                    //testnet merchant address
140                    address = 0x4ff956c78244911b
141                } else if address == 0x30cf5dcf6ea8d379{ 
142                    //mainnet merchant address
143                    address = 0xa9277dcbec7769df
144                }
145                let ducReceiver = getAccount(address).capabilities.get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
146                return MetadataViews.Royalties([MetadataViews.Royalty(receiver: ducReceiver!, cut: 0.06, description: "onefootball largest of 6% or 0.65")])
147
148            }
149            return nil
150        }
151
152        access(contract)
153        fun increaseNounce(){ 
154            self.nounce = self.nounce + 1
155        }
156
157        access(all)
158        fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
159            return <-create Collection()
160        }
161    }
162
163    access(all)
164    struct Badge{ 
165        access(all)
166        let id: UInt64
167
168        access(all)
169        let name: String?
170
171        access(all)
172        let description: String?
173
174        access(all)
175        let rarity: MetadataViews.Rarity?
176
177        init(id: UInt64, name: String?, description: String?, rarity: MetadataViews.Rarity?){ 
178            self.id = id
179            self.name = name
180            self.description = description
181            self.rarity = rarity
182        }
183    }
184
185    access(all)
186    resource interface CollectionPublic{ 
187        access(account)
188        fun addBadge(id: UInt64, badge: Badge)
189
190        access(all)
191        fun hasNFT(_ id: UInt64): Bool
192    }
193
194    access(all)
195    resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection, CollectionPublic{ 
196        // dictionary of NFT conforming tokens
197        // NFT is a resource type with an `UInt64` ID field
198        access(all)
199        var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
200
201        init(){ 
202            self.ownedNFTs <-{} 
203        }
204
205        access(all)
206        fun hasNFT(_ id: UInt64): Bool{ 
207            return self.ownedNFTs[id] != nil
208        }
209
210        // withdraw removes an NFT from the collection and moves it to the caller
211        access(NonFungibleToken.Withdraw)
212        fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
213            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
214            emit Withdraw(id: token.id, from: self.owner?.address)
215            return <-token
216        }
217
218        // withdraw removes an NFT from the collection and moves it to the caller
219        access(Owner)
220        fun burn(_ id: UInt64){ 
221            let token <- self.withdraw(withdrawID: id) as! @NFT
222            emit Burned(id: token.id, from: self.owner?.address, playId: token.play.id, edition: token.edition)
223            destroy <-token
224        }
225
226        // deposit takes a NFT and adds it to the collections dictionary
227        // and adds the ID to the id array
228        access(all)
229        fun deposit(token: @{NonFungibleToken.NFT}): Void{ 
230            let token <- token as! @NFT
231            let id: UInt64 = token.id
232            token.increaseNounce()
233            // add the new token to the dictionary which removes the old one
234            let oldToken <- self.ownedNFTs[id] <- token
235            emit Deposit(id: id, to: self.owner?.address)
236            destroy oldToken
237        }
238
239        // getIDs returns an array of the IDs that are in the collection
240        access(all)
241        view fun getIDs(): [UInt64]{ 
242            return self.ownedNFTs.keys
243        }
244
245        // borrowNFT gets a reference to an NFT in the collection
246        // so that the caller can read its metadata and call its methods
247        access(all)
248        view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
249            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
250        }
251
252        access(all)
253        view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
254            let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
255            let aera = nft as! &NFT
256            return aera as &{ViewResolver.Resolver}
257        }
258
259        access(account)
260        fun addBadge(id: UInt64, badge: Badge){ 
261            let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
262            let aera = nft as! &NFT
263            aera.addBadge(badge)
264        }
265
266        access(all)
267        view fun getSupportedNFTTypes():{ Type: Bool}{ 
268            panic("implement me")
269        }
270
271        access(all)
272        view fun isSupportedNFTType(type: Type): Bool{ 
273            panic("implement me")
274        }
275
276        access(all)
277        fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
278            return <-create Collection()
279        }
280    }
281
282    // public function that anyone can call to create a new empty collection
283    access(all)
284    fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
285        return <-create Collection()
286    }
287
288    // mintNFT mints a new NFT with a new ID
289    // and deposit it in the recipients collection using their collection reference
290    //The distinction between sending in a reference and sending in a capability is that when you send in a reference it cannot be stored. So it can only be used in this method
291    //while a capability can be stored and used later. So in this case using a reference is the right choice, but it needs to be owned so that you can have a good event
292    access(account)
293    fun mintNFT(recipient: &{NonFungibleToken.Receiver}, edition: UInt64, play: Play, badges: [Badge]){ 
294        pre{ 
295            recipient.owner != nil:
296            "Recipients NFT collection is not owned"
297        }
298        AeraNFT.totalSupply = AeraNFT.totalSupply + 1
299        // create a new NFT
300        var newNFT <- create NFT(play: play, edition: edition)
301        for badge in badges{ 
302            newNFT.addBadge(badge)
303        }
304        recipient.deposit(token: <-newNFT)
305    }
306
307    access(all)
308    struct Game{ 
309        access(all)
310        let id: UInt64
311
312        access(all)
313        let homeTeamName: String
314
315        access(all)
316        let awayTeamName: String
317
318        access(all)
319        let derbyID: String
320
321        access(all)
322        let competition: String
323
324        access(all)
325        let season: String
326
327        access(all)
328        let date: UFix64
329
330        access(all)
331        let matchday: UInt64
332
333        access(all)
334        let highlightedTeam: String
335
336        access(all)
337        let homeScore: UInt64
338
339        access(all)
340        let awayScore: UInt64
341
342        access(all)
343        let metadata:{ String: String}
344
345        init(id: UInt64, homeTeamName: String, awayTeamName: String, derbyID: String, competition: String, season: String, date: UFix64, matchday: UInt64, highlightedTeam: String, homeScore: UInt64, awayScore: UInt64, metadata:{ String: String}){ 
346            self.id = id
347            self.homeTeamName = homeTeamName
348            self.awayTeamName = awayTeamName
349            self.derbyID = derbyID
350            self.competition = competition
351            self.season = season
352            self.date = date
353            self.matchday = matchday
354            self.highlightedTeam = highlightedTeam
355            self.homeScore = homeScore
356            self.awayScore = awayScore
357            self.metadata = metadata
358        }
359
360        access(all)
361        fun getTraits(): [MetadataViews.Trait]{ 
362            var winner = "tie"
363            if self.homeScore > self.awayScore{ 
364                winner = "home"
365            } else if self.homeScore < self.awayScore{ 
366                winner = "away"
367            }
368            let views = [MetadataViews.Trait(name: "home_team", value: self.homeTeamName, displayType: "String", rarity: nil), MetadataViews.Trait(name: "away_team", value: self.awayTeamName, displayType: "String", rarity: nil), MetadataViews.Trait(name: "competition", value: self.competition, displayType: "String", rarity: nil), MetadataViews.Trait(name: "season", value: self.season, displayType: "String", rarity: nil), MetadataViews.Trait(name: "match_date", value: self.date, displayType: "Date", rarity: nil), MetadataViews.Trait(name: "match_day", value: self.matchday, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "home_score", value: self.homeScore, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "away_score", value: self.awayScore, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "score", value: self.homeScore.toString().concat("-").concat(self.awayScore.toString()), displayType: "String", rarity: nil), MetadataViews.Trait(name: "winner", value: winner, displayType: "String", rarity: nil)]
369            if self.derbyID != "-" && self.derbyID != ""{ 
370                views.append(MetadataViews.Trait(name: "derby_id", value: self.derbyID, displayType: "String", rarity: nil))
371            }
372            return views
373        }
374    }
375
376    access(self)
377    let games:{ UInt64: Game}
378
379    access(account)
380    fun addGame(_ game: Game){ 
381        self.games[game.id] = game
382    }
383
384    access(all)
385    fun getGames():{ UInt64: Game}{ 
386        return self.games
387    }
388
389    access(all)
390    fun getGame(_ id: UInt64): Game?{ 
391        return self.games[id]
392    }
393
394    access(all)
395    struct License{ 
396        access(all)
397        let id: UInt64
398
399        access(all)
400        let note: String
401
402        access(all)
403        let copyright: String
404
405        init(id: UInt64, note: String, copyright: String){ 
406            self.id = id
407            self.note = note
408            self.copyright = copyright
409        }
410    }
411
412    access(self)
413    let licenses:{ UInt64: License}
414
415    access(account)
416    fun addLicense(_ license: License){ 
417        self.licenses[license.id] = license
418    }
419
420    access(all)
421    fun getLicenses():{ UInt64: License}{ 
422        return self.licenses
423    }
424
425    access(all)
426    fun getLicense(_ id: UInt64): License?{ 
427        return self.licenses[id]
428    }
429
430    /// A struct to hold information about a play
431    access(all)
432    struct Play{ 
433        access(all)
434        let id: UInt64
435
436        access(all)
437        let gameID: UInt64
438
439        access(all)
440        let playersInvolved:{ String: UInt64}
441
442        access(all)
443        let title: String
444
445        access(all)
446        let description: String
447
448        //this is the thumbnail
449        access(all)
450        let imageIpfsHash: String
451
452        access(all)
453        let videoIpfsHash: String
454
455        //these are images
456        access(all)
457        let thumbnailsIpfsHash: [String]
458
459        access(all)
460        let type: String
461
462        access(all)
463        let rarity: String
464
465        access(all)
466        let maxSerial: UInt64
467
468        access(all)
469        let period: String
470
471        access(all)
472        let date: UFix64
473
474        access(all)
475        let time: UInt64
476
477        access(all)
478        let homeScore: UInt64
479
480        access(all)
481        let awayScore: UInt64
482
483        init(id: UInt64, gameID: UInt64, playersInvolved:{ String: UInt64}, title: String, description: String, imageIpfsHash: String, videoIpfsHash: String, thumbnailsIpfsHash: [String], type: String, rarity: String, maxSerial: UInt64, period: String, date: UFix64, time: UInt64, homeScore: UInt64, awayScore: UInt64){ 
484            self.id = id
485            self.gameID = gameID
486            self.playersInvolved = playersInvolved
487            self.title = title
488            self.description = description
489            self.imageIpfsHash = imageIpfsHash
490            self.videoIpfsHash = videoIpfsHash
491            self.thumbnailsIpfsHash = thumbnailsIpfsHash
492            self.type = type
493            self.rarity = rarity
494            self.maxSerial = maxSerial
495            self.period = period
496            self.date = date
497            self.time = time
498            self.homeScore = homeScore
499            self.awayScore = awayScore
500        }
501
502        access(all)
503        fun getLicenseID(): UInt64?{ 
504            let playId = self.id
505            if let licence = self.playersInvolved["license"]{ 
506                //There was a typo in mint so we fix it here
507                if playId == 338{ 
508                    return 2
509                } else if playId == 342{ 
510                    return 1
511                } else{ 
512                    return licence
513                }
514            }
515            return nil
516        }
517
518        access(all)
519        fun getTraits(): [MetadataViews.Trait]{ 
520            let traits = [MetadataViews.Trait(name: "play_id", value: self.id, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "play_period", value: self.period, displayType: "String", rarity: nil), MetadataViews.Trait(name: "play_time", value: self.time, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "play_home_score", value: self.homeScore, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "play_away_score", value: self.awayScore, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "play_score", value: self.homeScore.toString().concat("-").concat(self.awayScore.toString()), displayType: "String", rarity: nil), MetadataViews.Trait(name: "play_type", value: self.type, displayType: "String", rarity: nil)]
521            let game = AeraNFT.getGame(self.gameID)!
522            if let ht = self.playersInvolved["highlightedTeam"]{ 
523                var highlightedTeam = game.homeTeamName
524                if ht == 2{ 
525                    highlightedTeam = game.awayTeamName
526                }
527                traits.append(MetadataViews.Trait(name: "highlighted_team", value: highlightedTeam, displayType: "String", rarity: nil))
528            }
529            if let pt = self.playersInvolved["play_type"]{ 
530                var playType = "player"
531                if pt == 2{ 
532                    playType = "team"
533                } else if pt == 3{ 
534                    playType = "reward"
535                }
536                traits.append(MetadataViews.Trait(name: "play_entity", value: playType, displayType: "String", rarity: nil))
537            }
538            traits.appendAll(game.getTraits())
539            for role in self.playersInvolved.keys{ 
540                if role == "highlightedTeam" || role == "license" || role == "play_type"{ 
541                    continue
542                }
543                let player = AeraNFT.getPlayer(self.playersInvolved[role]!)!
544                var prefix = role
545                traits.appendAll(player.getTraits(prefix))
546            }
547            if let licence = self.getLicenseID(){ 
548                let l = AeraNFT.getLicense(licence)!
549                traits.append(MetadataViews.Trait(name: "copyright", value: l.copyright, displayType: "String", rarity: nil))
550            }
551            return traits
552        }
553
554        access(all)
555        fun getMetadata(_ field: String): AnyStruct?{ 
556            if let metadata = AeraNFT.getPlayMetadata(self.id){ 
557                return metadata.metadata[field]
558            }
559            return nil
560        }
561
562        access(all)
563        fun getMedias(): MetadataViews.Medias{ 
564            let imageFile = MetadataViews.IPFSFile(cid: self.imageIpfsHash, path: nil)
565            let medias = [self.getVideo(), MetadataViews.Media(file: imageFile, mediaType: "image/png")]
566            for hash in self.thumbnailsIpfsHash{ 
567                let file = MetadataViews.IPFSFile(cid: hash, path: nil)
568                medias.append(MetadataViews.Media(file: file, mediaType: "image/png"))
569            }
570            return MetadataViews.Medias(medias)
571        }
572
573        access(all)
574        fun asDisplay(): MetadataViews.Display{ 
575            let imageFile = MetadataViews.IPFSFile(cid: self.imageIpfsHash, path: nil)
576            return MetadataViews.Display(name: self.title, description: self.description, thumbnail: imageFile)
577        }
578
579        access(all)
580        fun getVideo(): MetadataViews.Media{ 
581            let file = MetadataViews.IPFSFile(cid: self.videoIpfsHash, path: nil)
582            return MetadataViews.Media(file: file, mediaType: self.getVideoMediaType())
583        }
584
585        access(all)
586        fun getVideoMediaType(): String{ 
587            if let videoMediaType = self.getMetadata("videoMediaType"){ 
588                return videoMediaType as! String
589            }
590            return "video/mp4"
591        }
592    }
593
594    access(all)
595    struct PlayMetadata{ 
596        access(all)
597        let id: UInt64
598
599        access(all)
600        let metadata:{ String: String}
601
602        init(id: UInt64, metadata:{ String: String}){ 
603            self.id = id
604            self.metadata = metadata
605        }
606    }
607
608    access(self)
609    let playMetadata:{ UInt64: PlayMetadata}
610
611    access(account)
612    fun addPlayMetadata(_ play: PlayMetadata){ 
613        self.playMetadata[play.id] = play
614    }
615
616    access(all)
617    fun getAllPlayMetadata():{ UInt64: PlayMetadata}{ 
618        return self.playMetadata
619    }
620
621    access(all)
622    fun getPlayMetadata(_ id: UInt64): PlayMetadata?{ 
623        return self.playMetadata[id]
624    }
625
626    /// Players store information about a player that can change and is therefore not stored in the NFT itself
627    /// can be mutated from Admin
628    access(all)
629    struct Player{ 
630        access(all)
631        let id: UInt64
632
633        access(all)
634        let jerseyname: String
635
636        access(all)
637        let position: String
638
639        access(all)
640        let number: UInt64
641
642        access(all)
643        let nationality: String
644
645        access(all)
646        let birthday: String
647
648        access(all)
649        let metadata:{ String: String}
650
651        init(id: UInt64, jerseyname: String, position: String, number: UInt64, nationality: String, birthday: String, metadata:{ String: String}){ 
652            self.id = id
653            self.jerseyname = jerseyname
654            self.position = position
655            self.number = number
656            self.nationality = nationality
657            self.birthday = birthday
658            self.metadata = metadata
659        }
660
661        access(all)
662        fun getTraits(_ role: String): [MetadataViews.Trait]{ 
663            return [MetadataViews.Trait(name: "player_".concat(role).concat("_jersey"), value: self.jerseyname, displayType: "String", rarity: nil), MetadataViews.Trait(name: "player_".concat(role).concat("_position"), value: self.position, displayType: "String", rarity: nil), MetadataViews.Trait(name: "player_".concat(role).concat("_number"), value: self.number, displayType: "Number", rarity: nil), MetadataViews.Trait(name: "player_".concat(role).concat("_nationality"), value: self.nationality, displayType: "String", rarity: nil), MetadataViews.Trait(name: "player_".concat(role).concat("_birthday"), value: self.birthday, displayType: "String", rarity: nil)]
664        }
665    }
666
667    access(self)
668    let players:{ UInt64: Player}
669
670    access(account)
671    fun addPlayer(_ player: Player){ 
672        self.players[player.id] = player
673    }
674
675    access(all)
676    fun getPlayers():{ UInt64: Player}{ 
677        return self.players
678    }
679
680    access(all)
681    fun getPlayer(_ id: UInt64): Player?{ 
682        return self.players[id]
683    }
684
685    access(all)
686    fun resolveLicence(_ viewResolver: &{ViewResolver.Resolver}): License?{ 
687        if let view = viewResolver.resolveView(Type<License>()){ 
688            if let v = view as? License{ 
689                return v
690            }
691        }
692        return nil
693    }
694
695    access(all)
696    struct Challenge{ 
697        access(all)
698        var completed: Bool
699
700        access(all)
701        let playIds: [UInt64]
702
703        access(all)
704        let numberOfPlaysToComplete: Int
705
706        access(all)
707        let qualification:{ UInt64: UInt64}
708
709        init(playIds: [UInt64], numberOfPlaysToComplete: Int){ 
710            self.playIds = playIds
711            self.numberOfPlaysToComplete = numberOfPlaysToComplete
712            self.qualification ={} 
713            self.completed = false
714        }
715
716        access(all)
717        fun checkPlay(playId: UInt64, id: UInt64){ 
718            if self.playIds.contains(playId) && self.qualification[playId] == nil{ 
719                self.qualification[playId] = id
720            }
721            self.completed = self.qualification.length >= self.numberOfPlaysToComplete
722        }
723    }
724
725    access(all) view fun getContractViews(resourceType: Type?): [Type] {
726        return [
727        Type<MetadataViews.NFTCollectionData>(),
728        Type<MetadataViews.NFTCollectionDisplay>()
729        ]
730    }
731
732    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
733        switch viewType {
734        case Type<MetadataViews.NFTCollectionData>():
735            return MetadataViews.NFTCollectionData(storagePath: AeraNFT.CollectionStoragePath, publicPath: AeraNFT.CollectionPublicPath, publicCollection: Type<&AeraNFT.Collection>(), publicLinkedType: Type<&AeraNFT.Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
736                return <-AeraNFT.createEmptyCollection(nftType: Type<@AeraNFT.Collection>())
737            })
738        case Type<MetadataViews.NFTCollectionDisplay>():
739            let externalURL = MetadataViews.ExternalURL("http://aera.onefootbal.com")
740            let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://miro.medium.com/fit/c/176/176/1*hzfP51MATg3et5vghYy1uQ.png"), mediaType: "image/png")
741            let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://bafybeiaaf7bfbppep5ahb2m75xlmxyj76hxo3oh5dcl4prxeskruwpe6c4.ipfs.nftstorage.link/"), mediaType: "image/png")
742            let socialMap:{ String: MetadataViews.ExternalURL} ={ "twitter": MetadataViews.ExternalURL("https://twitter.com/aera_football"), "discord": MetadataViews.ExternalURL("https://discord.gg/aera"), "instagram": MetadataViews.ExternalURL("https://www.instagram.com/aera_football/")}
743            return MetadataViews.NFTCollectionDisplay(name: "Aera", description: "Aera by OneFootball", externalURL: externalURL, squareImage: squareImage, bannerImage: bannerImage, socials: socialMap)
744
745        }
746        return nil
747    }
748
749
750    init(){ 
751        self.players ={} 
752        self.games ={} 
753        self.playMetadata ={} 
754        self.licenses ={} 
755        // Initialize the total supply
756        self.totalSupply = 0
757
758        // Set the named paths
759        self.CollectionStoragePath = /storage/aeraNFTs
760        self.CollectionPublicPath = /public/aeraNFTs
761        self.CollectionPrivatePath = /private/aeraNFTs
762        emit ContractInitialized()
763    }
764}
765
766