Smart Contract

AeraRewards

A.30cf5dcf6ea8d379.AeraRewards

Deployed

1d ago
Feb 26, 2026, 09:43:51 PM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4import ViewResolver from 0x1d7e57aa55817448
5import AeraNFT from 0x30cf5dcf6ea8d379
6
7
8/*
9Aera Reward is the NFT for fulfilling all the required panels for a chapter
10consists of thumbnail and a DVM
11*/
12
13access(all)
14contract AeraRewards: NonFungibleToken{ 
15    access(all)
16    var totalSupply: UInt64
17
18    access(all)
19    event ContractInitialized()
20
21    access(all)
22    event Withdraw(id: UInt64, from: Address?)
23
24    access(all)
25    event Deposit(id: UInt64, to: Address?)
26
27    access(all)
28    event Minted(id: UInt64, address: Address, reward_template_id: UInt64, edition: UInt64)
29
30    // we cannot have address here as it will always be nil
31    access(all)
32    event Burned(id: UInt64, reward_template_id: UInt64, edition: UInt64)
33
34    access(all)
35    event RewardMetadataRegistered(reward_template_id: UInt64, reward_name: String, thumbnail: String, video: String, max_quantity: UInt64?)
36
37    access(all)
38    event RewardClaimed(id: UInt64, address: Address, rewardTemplateId: UInt64, rewardName: String, description: String, thumbnailHash: String, edition: UInt64, type: String, soulBounded: Bool, rarity: String, rewardFields:{ UInt64:{ String: String}})
39
40    access(all)
41    let CollectionStoragePath: StoragePath
42
43    access(all)
44    let CollectionPublicPath: PublicPath
45
46    access(account)
47    let royalties: [MetadataViews.Royalty]
48
49    access(all)
50    let rewardTemplates:{ UInt64: MintDetail}
51
52    access(all)
53    var currentSerial: UInt64
54
55    access(all)
56    struct RewardClaimedData{ 
57        access(all)
58        let data:{ String: String}
59
60        init(_ data:{ String: String}){ 
61            self.data = data
62        }
63    }
64
65    access(all)
66    struct MintDetail{ 
67        access(all)
68        let reward_template: RewardTemplate
69
70        access(all)
71        var current_edition: UInt64
72
73        access(all)
74        var total_supply: UInt64
75
76        access(all)
77        let max_quantity: UInt64?
78
79        init(reward_template: RewardTemplate, current_edition: UInt64, total_supply: UInt64, max_quantity: UInt64?){ 
80            self.reward_template = reward_template
81            self.current_edition = current_edition
82            self.total_supply = total_supply
83            self.max_quantity = max_quantity
84        }
85
86        access(account)
87        fun mint(): MintDetail{ 
88            pre{ 
89                self.max_quantity == nil || self.max_quantity! >= self.current_edition + UInt64(1):
90                "Cannot mint reward with edition greater than max quantity"
91            }
92            self.current_edition = self.current_edition + 1
93            self.total_supply = self.total_supply + 1
94            return self
95        }
96
97        access(account)
98        fun burn(){ 
99            self.total_supply = self.total_supply - 1
100        }
101    }
102
103    access(all)
104    struct Media{ 
105        access(all)
106        let name: String
107
108        access(all)
109        let media_type: String
110
111        access(all)
112        let ipfs_hash: String
113
114        access(all)
115        let extra:{ String: AnyStruct}
116
117        init(name: String, media_type: String, ipfs_hash: String){ 
118            self.name = name
119            self.media_type = media_type
120            self.ipfs_hash = ipfs_hash
121            self.extra ={} 
122        }
123    }
124
125    access(all)
126    struct RewardTemplate{ 
127        access(all)
128        let reward_template_id: UInt64
129
130        access(all)
131        let reward_name: String
132
133        access(all)
134        let description: String
135
136        access(all)
137        let thumbnail_hash: String
138
139        access(all)
140        let video_hash: String
141
142        access(all)
143        let video_file: String
144
145        access(all)
146        let video_type: String
147
148        // for struct's use only
149        access(all)
150        var edition: UInt64
151
152        access(all)
153        let files: [Media]
154
155        access(all)
156        let type: String
157
158        access(all)
159        let detail_id:{ String: UInt64}
160
161        access(all)
162        let traits: [MetadataViews.Trait]
163
164        access(all)
165        let soulBounded: Bool
166
167        access(all)
168        let rarity: String
169
170        access(all)
171        let extra:{ String: AnyStruct}
172
173        init(reward_template_id: UInt64, reward_name: String, description: String, thumbnail_hash: String, video_hash: String, video_file: String, video_type: String, files: [Media], type: String, detail_id:{ String: UInt64}, traits: [MetadataViews.Trait], soulBounded: Bool, rarity: String){ 
174            pre{ 
175                video_hash == "" && video_file != "" || video_file == "" && video_hash != "":
176                "Requires either video_file or video_hash to be empty string"
177            }
178            self.reward_template_id = reward_template_id
179            self.reward_name = reward_name
180            self.description = description
181            self.thumbnail_hash = thumbnail_hash
182            self.video_hash = video_hash
183            self.video_file = video_file
184            self.video_type = video_type
185            self.extra ={} 
186            self.edition = 0
187            self.type = type
188            self.detail_id = detail_id
189            self.traits = traits
190            self.files = files
191            self.soulBounded = soulBounded
192            self.rarity = rarity
193        }
194
195        access(all)
196        fun getPlayer(): AeraNFT.Player?{ 
197            if let p = self.detail_id["player_id"]{ 
198                return AeraNFT.getPlayer(p)
199            }
200            return nil
201        }
202
203        access(all)
204        fun getLicense(): AeraNFT.License?{ 
205            if let id = self.detail_id["license_id"]{ 
206                return AeraNFT.getLicense(id)
207            }
208            return nil
209        }
210
211        access(all)
212        fun getFiles(): [MetadataViews.Media]{ 
213            var m: [MetadataViews.Media] = []
214            for f in self.files{ 
215                m.append(MetadataViews.Media(file: MetadataViews.IPFSFile(cid: f.ipfs_hash, path: nil), mediaType: f.media_type))
216            }
217            return m
218        }
219
220        access(all)
221        fun getFileAsTraits(): [MetadataViews.Trait]{ 
222            var t: [MetadataViews.Trait] = []
223            for f in self.files{ 
224                t.append(MetadataViews.Trait(name: f.name, value: "ipfs://".concat(f.ipfs_hash), displayType: "string", rarity: nil))
225            }
226            return t
227        }
228
229        access(all)
230        fun getVideoAsFile():{ MetadataViews.File}{ 
231            if self.video_hash != ""{ 
232                return MetadataViews.IPFSFile(cid: self.video_hash, path: nil)
233            }
234            return MetadataViews.HTTPFile(url: self.video_file)
235        }
236    }
237
238    access(account)
239    fun addRewardTemplate(reward: RewardTemplate, maxQuantity: UInt64?){ 
240        let mintDetail = MintDetail(reward_template: reward, current_edition: 0, total_supply: 0, max_quantity: maxQuantity)
241        self.rewardTemplates[reward.reward_template_id] = mintDetail
242        emit RewardMetadataRegistered(reward_template_id: reward.reward_template_id, reward_name: reward.reward_name, thumbnail: "ipfs://".concat(reward.thumbnail_hash), video: reward.getVideoAsFile().uri(), max_quantity: maxQuantity)
243    }
244
245    access(all)
246    fun getReward(_ id: UInt64): RewardTemplate{ 
247        return (AeraRewards.rewardTemplates[id]!).reward_template
248    }
249
250    access(all)
251    resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver{ 
252        access(all)
253        let id: UInt64
254
255        access(all)
256        let reward_template_id: UInt64
257
258        access(all)
259        let serial: UInt64
260
261        access(all)
262        let edition: UInt64
263
264        access(all)
265        let max_edition: UInt64?
266
267        access(all)
268        var nounce: UInt64
269
270        access(all)
271        let extra:{ String: AnyStruct}
272
273        init(reward_template_id: UInt64, serial: UInt64, edition: UInt64, max_edition: UInt64?){ 
274            self.reward_template_id = reward_template_id
275            self.nounce = 0
276            self.id = self.uuid
277            self.serial = serial
278            self.edition = edition
279            self.max_edition = max_edition
280            self.extra ={ }
281        }
282
283        access(all)
284        fun getReward(): AeraRewards.RewardTemplate{ 
285            return (AeraRewards.rewardTemplates[self.reward_template_id]!).reward_template
286        }
287
288        access(all)
289        fun getMintedAt(): UFix64?{ 
290            if let minted = self.extra["mintedAt"]{ 
291                let res = minted as! UFix64
292                return res
293            }
294            return nil
295        }
296
297        access(all)
298        view fun getViews(): [Type]{ 
299            let views: [Type] = [Type<MetadataViews.Display>(), Type<MetadataViews.Medias>(), Type<MetadataViews.Royalties>(), Type<MetadataViews.ExternalURL>(), Type<MetadataViews.NFTCollectionData>(), Type<MetadataViews.NFTCollectionDisplay>(), Type<MetadataViews.Serial>(), Type<MetadataViews.Editions>(), Type<MetadataViews.Traits>(), Type<MetadataViews.Rarity>(), Type<AeraNFT.License>(), Type<RewardTemplate>()]
300            let reward = (AeraRewards.rewardTemplates[self.reward_template_id]!).reward_template
301            return views
302        }
303
304        access(all)
305        fun resolveView(_ view: Type): AnyStruct?{ 
306            let reward = self.getReward()
307            let thumbnail = MetadataViews.IPFSFile(cid: reward.thumbnail_hash, path: nil)
308            let ipfsVideo = reward.getVideoAsFile()
309
310            // mind the mediaType here, to be confirmed
311            let thumbnailMedia = MetadataViews.Media(file: thumbnail, mediaType: "image/png")
312            let media = MetadataViews.Media(file: ipfsVideo, mediaType: reward.video_type)
313            switch view{ 
314            case Type<MetadataViews.Display>():
315                let name = reward.reward_name
316                // Question : Any preferred description on rewrad NFT?
317                let description = reward.description
318                let thumbnail = thumbnail
319                return MetadataViews.Display(name: name, description: description, thumbnail: thumbnail)
320            case Type<MetadataViews.ExternalURL>():
321                if let addr = self.owner?.address{ 
322                    return MetadataViews.ExternalURL("https://aera.onefootball.com/collectibles/".concat(addr.toString()).concat("/rewards/").concat(self.id.toString()))
323                }
324                return MetadataViews.ExternalURL("https://aera.onefootball.com/collectibles/")
325            case Type<MetadataViews.Royalties>():
326                // return MetadataViews.Royalties(AeraRewards.royalties)
327                var address = AeraRewards.account.address
328                if address == 0x46625f59708ec2f8{ 
329                    //testnet merchant address
330                    address = 0x4ff956c78244911b
331                } else if address == 0x30cf5dcf6ea8d379{ 
332                    //mainnet merchant address
333                    address = 0xa9277dcbec7769df
334                }
335                let ducReceiver = getAccount(address).capabilities.get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)
336                return MetadataViews.Royalties([MetadataViews.Royalty(receiver: ducReceiver!, cut: 0.06, description: "onefootball largest of 6% or 0.65")])
337            case Type<MetadataViews.Medias>():
338                let m = [thumbnailMedia, media]
339                let r = self.getReward()
340                m.appendAll(r.getFiles())
341                return MetadataViews.Medias(m)
342            case Type<MetadataViews.NFTCollectionDisplay>():
343                let externalURL = MetadataViews.ExternalURL("http://aera.onefootbal.com")
344                let squareImage = MetadataViews.Media(file: MetadataViews.IPFSFile(cid: "bafkreiameqwyluog75u7zg3dmf56b5mbed7cdgv6uslkph6nvmdf2aipmy", path: nil), mediaType: "image/jpg")
345                let bannerImage = MetadataViews.Media(file: MetadataViews.IPFSFile(cid: "bafybeiayhvr2sm4lco3tbsa74blynlnzhhzrjouteyqaq43giuyiln4xb4", path: nil), mediaType: "image/png")
346                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/")}
347                return MetadataViews.NFTCollectionDisplay(name: "Aera Rewards", description: "Aera by OneFootball", externalURL: externalURL, squareImage: squareImage, bannerImage: bannerImage, socials: socialMap)
348            case Type<MetadataViews.NFTCollectionData>():
349                return MetadataViews.NFTCollectionData(storagePath: AeraRewards.CollectionStoragePath, publicPath: AeraRewards.CollectionPublicPath, publicCollection: Type<&Collection>(), publicLinkedType: Type<&Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
350                    return <-AeraRewards.createEmptyCollection(nftType: Type<@AeraRewards.Collection>())
351                })
352            case Type<MetadataViews.Serial>():
353                return MetadataViews.Serial(self.serial)
354            case Type<MetadataViews.Editions>():
355                return MetadataViews.Editions([MetadataViews.Edition(name: "set", number: self.edition, max: self.max_edition)])
356            case Type<MetadataViews.Traits>():
357                let ts: [MetadataViews.Trait] = [MetadataViews.Trait(name: "reward_template_id", value: self.reward_template_id, displayType: "number", rarity: nil)]
358                for detail in reward.detail_id.keys{ 
359                    ts.append(MetadataViews.Trait(name: detail, value: reward.detail_id[detail], displayType: "number", rarity: nil))
360                }
361                if let l = reward.getLicense(){ 
362                    ts.append(MetadataViews.Trait(name: "copyright", value: l.copyright, displayType: "string", rarity: nil))
363                }
364                if let player = reward.getPlayer(){ 
365                    ts.appendAll([MetadataViews.Trait(name: "player_jersey_name", value: player.jerseyname, displayType: "string", rarity: nil), MetadataViews.Trait(name: "player_position", value: player.position, displayType: "string", rarity: nil), MetadataViews.Trait(name: "player_number", value: player.number, displayType: "number", rarity: nil), MetadataViews.Trait(name: "player_nationality", value: player.nationality, displayType: "string", rarity: nil), MetadataViews.Trait(name: "player_birthday", value: player.birthday, displayType: "date", rarity: nil)])
366                }
367                if let m = self.getMintedAt(){ 
368                    ts.append(MetadataViews.Trait(name: "minted_at", value: m, displayType: "date", rarity: nil))
369                }
370                let r = self.getReward()
371                ts.appendAll(r.getFileAsTraits())
372                ts.appendAll(r.traits)
373                return MetadataViews.Traits(ts)
374            case Type<AeraNFT.License>():
375                if let license = reward.getLicense(){ 
376                    return license
377                }
378                return nil
379            case Type<AeraRewards.RewardTemplate>():
380                return (AeraRewards.rewardTemplates[self.reward_template_id]!).reward_template
381            case Type<MetadataViews.Rarity>():
382                let reward = (AeraRewards.rewardTemplates[self.reward_template_id]!).reward_template
383                return MetadataViews.Rarity(score: nil, max: nil, description: reward.rarity)
384            }
385            return nil
386        }
387
388        access(account)
389        fun increaseNounce(){ 
390            self.nounce = self.nounce + 1
391        }
392
393        access(all)
394        fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
395            return <-create Collection()
396        }
397    }
398
399    access(all)
400    resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection{ 
401        // dictionary of NFT conforming tokens
402        // NFT is a resource type with an `UInt64` ID field
403        access(all)
404        var ownedNFTs: @{UInt64:{ NonFungibleToken.NFT}}
405
406        init(){ 
407            self.ownedNFTs <-{} 
408        }
409
410        access(all)
411        fun hasNFT(_ id: UInt64): Bool{ 
412            return self.ownedNFTs.containsKey(id)
413        }
414
415        // withdraw removes an NFT from the collection and moves it to the caller
416        access(NonFungibleToken.Withdraw)
417        fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}{ 
418            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
419            emit Withdraw(id: token.id, from: self.owner?.address)
420            return <-token
421        }
422
423        // deposit takes a NFT and adds it to the collections dictionary
424        // and adds the ID to the id array
425        access(all)
426        fun deposit(token: @{NonFungibleToken.NFT}): Void{ 
427            let token <- token as! @NFT
428            let id: UInt64 = token.id
429            token.increaseNounce()
430            // add the new token to the dictionary which removes the old one
431            let oldToken <- self.ownedNFTs[id] <- token
432            emit Deposit(id: id, to: self.owner?.address)
433            destroy oldToken
434        }
435
436        // getIDs returns an array of the IDs that are in the collection
437        access(all)
438        view fun getIDs(): [UInt64]{ 
439            return self.ownedNFTs.keys
440        }
441
442        // borrowNFT gets a reference to an NFT in the collection
443        // so that the caller can read its metadata and call its methods
444        access(all)
445        view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?{ 
446            pre{ 
447                self.ownedNFTs.containsKey(id):
448                "Cannot borrow reference to Reward NFT ID : ".concat(id.toString())
449            }
450            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
451        }
452
453        access(all)
454        fun borrowRewardNFT(id: UInt64): &AeraRewards.NFT{ 
455            pre{ 
456                self.ownedNFTs.containsKey(id):
457                "Cannot borrow reference to Reward NFT ID : ".concat(id.toString())
458            }
459            let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
460            return nft as! &AeraRewards.NFT
461        }
462
463        access(all)
464        view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}?{ 
465            pre{ 
466                self.ownedNFTs.containsKey(id):
467                "Cannot borrow reference to Reward NFT ID : ".concat(id.toString())
468            }
469            let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
470            let aerarewardNFT = nft as! &NFT
471            return aerarewardNFT as &{ViewResolver.Resolver}
472        }
473
474        access(all)
475        view fun getSupportedNFTTypes():{ Type: Bool}{ 
476            panic("implement me")
477        }
478
479        access(all)
480        view fun isSupportedNFTType(type: Type): Bool{ 
481            panic("implement me")
482        }
483
484        access(all)
485        fun createEmptyCollection(): @{NonFungibleToken.Collection}{ 
486            return <-create Collection()
487        }
488    }
489
490    // public function that anyone can call to create a new empty collection
491    access(all)
492    fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection}{ 
493        return <-create Collection()
494    }
495
496    // mintNFT mints a new NFT with a new ID
497    // and deposit it in the recipients collection using their collection reference
498    //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
499    //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
500    access(account)
501    fun mintNFT(recipient: &{NonFungibleToken.Receiver}, rewardTemplateId: UInt64, rewardFields:{ UInt64:{ String: String}}): UInt64{ 
502        pre{ 
503            recipient.owner != nil:
504            "Recipients NFT collection is not owned"
505            self.rewardTemplates.containsKey(rewardTemplateId):
506            "Reward template does not exist. ID : ".concat(rewardTemplateId.toString())
507        }
508        AeraRewards.totalSupply = AeraRewards.totalSupply + 1
509        AeraRewards.currentSerial = AeraRewards.currentSerial + 1
510        let rewardMintDetail = (self.rewardTemplates[rewardTemplateId]!).mint()
511        // create a new NFT
512        var newNFT <- create NFT(reward_template_id: rewardTemplateId, serial: AeraRewards.currentSerial, edition: rewardMintDetail.current_edition, max_edition: rewardMintDetail.max_quantity)
513        let t = rewardMintDetail.reward_template
514        //Always emit events on state changes! always contain human readable and machine readable information
515        emit Minted(id: newNFT.id, address: (recipient.owner!).address, reward_template_id: rewardTemplateId, edition: rewardMintDetail.current_edition)
516        emit RewardClaimed(id: newNFT.id, address: (recipient.owner!).address, rewardTemplateId: t.reward_template_id, rewardName: t.reward_name, description: t.description, thumbnailHash: t.thumbnail_hash, edition: rewardMintDetail.current_edition, type: t.type, soulBounded: t.soulBounded, rarity: t.rarity, rewardFields: rewardFields)
517
518        // deposit it in the recipient's account using their reference
519        let id = newNFT.id
520        recipient.deposit(token: <-newNFT)
521        return id
522    }
523
524    access(account)
525    fun addRoyaltycut(_ cutInfo: MetadataViews.Royalty){ 
526        var cutInfos = self.royalties
527        cutInfos.append(cutInfo)
528        // for validation only
529        let royalties = MetadataViews.Royalties(cutInfos)
530        self.royalties.append(cutInfo)
531    }
532
533    access(all) view fun getContractViews(resourceType: Type?): [Type] {
534        return [
535        Type<MetadataViews.NFTCollectionData>(),
536        Type<MetadataViews.NFTCollectionDisplay>()
537        ]
538    }
539
540    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
541        switch viewType {
542
543        case Type<MetadataViews.NFTCollectionDisplay>():
544            let externalURL = MetadataViews.ExternalURL("http://aera.onefootbal.com")
545            let squareImage = MetadataViews.Media(file: MetadataViews.IPFSFile(cid: "bafkreiameqwyluog75u7zg3dmf56b5mbed7cdgv6uslkph6nvmdf2aipmy", path: nil), mediaType: "image/jpg")
546            let bannerImage = MetadataViews.Media(file: MetadataViews.IPFSFile(cid: "bafybeiayhvr2sm4lco3tbsa74blynlnzhhzrjouteyqaq43giuyiln4xb4", path: nil), mediaType: "image/png")
547            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/")}
548            return MetadataViews.NFTCollectionDisplay(name: "Aera Rewards", description: "Aera by OneFootball", externalURL: externalURL, squareImage: squareImage, bannerImage: bannerImage, socials: socialMap)
549        case Type<MetadataViews.NFTCollectionData>():
550            return MetadataViews.NFTCollectionData(storagePath: AeraRewards.CollectionStoragePath, publicPath: AeraRewards.CollectionPublicPath, publicCollection: Type<&Collection>(), publicLinkedType: Type<&Collection>(), createEmptyCollectionFunction: fun (): @{NonFungibleToken.Collection}{ 
551                return <-AeraRewards.createEmptyCollection(nftType: Type<@AeraRewards.Collection>())
552            })
553
554        }
555        return nil
556    }
557
558
559
560    init(){ 
561        // Initialize the total supply
562        self.totalSupply = 0
563        self.currentSerial = 0
564        self.rewardTemplates ={} 
565
566        // Set Royalty cuts in a transaction
567        self.royalties = []
568
569        // Set the named paths
570        self.CollectionStoragePath = /storage/aeraRewardsNFT
571        self.CollectionPublicPath = /public/aeraRewardsNFT
572        self.account.storage.save<@{NonFungibleToken.Collection}>(<-AeraRewards.createEmptyCollection(nftType: Type<@AeraRewards.Collection>()), to: AeraRewards.CollectionStoragePath)
573        var capability_1 = self.account.capabilities.storage.issue<&AeraRewards.Collection>(AeraRewards.CollectionStoragePath)
574        self.account.capabilities.publish(capability_1, at: AeraRewards.CollectionPublicPath)
575        var capability_2 = self.account.capabilities.storage.issue<&AeraRewards.Collection>(AeraRewards.CollectionStoragePath)
576        emit ContractInitialized()
577    }
578}
579
580