Smart Contract
MFLClub
A.8ebcbfd516b1da27.MFLClub
1import NonFungibleToken from 0x1d7e57aa55817448
2import ViewResolver from 0x1d7e57aa55817448
3import FungibleToken from 0xf233dcee88fe0abe
4import MetadataViews from 0x1d7e57aa55817448
5import MFLAdmin from 0x8ebcbfd516b1da27
6
7/**
8 This contract is based on the NonFungibleToken standard on Flow.
9 It allows an admin to mint clubs (NFTs) and squads. Clubs and squads have metadata that can be updated by an admin.
10**/
11
12access(all)
13contract MFLClub: NonFungibleToken {
14 // Entitlements
15 access(all)
16 entitlement ClubAdminAction
17
18 access(all)
19 entitlement SquadAdminAction
20
21 access(all)
22 entitlement ClubAction
23
24 // Global Events
25 access(all)
26 event ContractInitialized()
27
28 access(all)
29 event Withdraw(id: UInt64, from: Address?)
30
31 access(all)
32 event Deposit(id: UInt64, to: Address?)
33
34 access(all)
35 event ClubMinted(id: UInt64)
36
37 access(all)
38 event ClubStatusUpdated(id: UInt64, status: UInt8)
39
40 access(all)
41 event ClubMetadataUpdated(id: UInt64)
42
43 access(all)
44 event ClubSquadsIDsUpdated(id: UInt64, squadsIDs: [UInt64])
45
46 access(all)
47 event ClubInfoUpdateRequested(id: UInt64, info: {String: String})
48
49 access(all)
50 event ClubFounded(
51 id: UInt64,
52 from: Address?,
53 name: String,
54 description: String,
55 foundationDate: UFix64,
56 foundationLicenseSerialNumber: UInt64?,
57 foundationLicenseCity: String?,
58 foundationLicenseCountry: String?,
59 foundationLicenseSeason: UInt32?
60 )
61
62 // Squads Events
63 access(all)
64 event SquadMinted(id: UInt64)
65
66 access(all)
67 event SquadMetadataUpdated(id: UInt64)
68
69 access(all)
70 event SquadCompetitionMembershipAdded(id: UInt64, competitionID: UInt64)
71
72 access(all)
73 event SquadCompetitionMembershipUpdated(id: UInt64, competitionID: UInt64)
74
75 access(all)
76 event SquadCompetitionMembershipRemoved(id: UInt64, competitionID: UInt64)
77
78 // Named Paths
79 access(all)
80 let CollectionStoragePath: StoragePath
81
82 access(all)
83 let CollectionPrivatePath: PrivatePath
84
85 access(all)
86 let CollectionPublicPath: PublicPath
87
88 access(all)
89 let ClubAdminStoragePath: StoragePath
90
91 access(all)
92 let SquadAdminStoragePath: StoragePath
93
94 // The total number of clubs that have been minted
95 access(all)
96 var totalSupply: UInt64
97
98 // All clubs datas are stored in this dictionary
99 access(self)
100 let clubsDatas: {UInt64: ClubData}
101
102 // The total number of squads that have been minted
103 access(all)
104 var squadsTotalSupply: UInt64
105
106 // All squads data are stored in this dictionary
107 access(self)
108 let squadsDatas: {UInt64: SquadData}
109
110 access(all)
111 enum SquadStatus: UInt8 {
112 access(all)
113 case ACTIVE
114 }
115
116 access(all)
117 struct SquadData {
118 access(all)
119 let id: UInt64
120
121 access(all)
122 let clubID: UInt64
123
124 access(all)
125 let type: String
126
127 access(self)
128 var status: SquadStatus
129
130 access(self)
131 var metadata: {String: AnyStruct}
132
133 access(self)
134 var competitionsMemberships: {UInt64: AnyStruct} // {competitionID: AnyStruct}
135
136
137 init(id: UInt64, clubID: UInt64, type: String, metadata: {String: AnyStruct}, competitionsMemberships: {UInt64: AnyStruct}) {
138 self.id = id
139 self.clubID = clubID
140 self.type = type
141 self.status = SquadStatus.ACTIVE
142 self.metadata = metadata
143 self.competitionsMemberships = {}
144 for competitionID in competitionsMemberships.keys {
145 self.addCompetitionMembership(competitionID: competitionID, competitionMembershipData: competitionsMemberships[competitionID])
146 }
147 }
148
149 // Getter for metadata
150 access(all)
151 view fun getMetadata(): {String: AnyStruct} {
152 return self.metadata
153 }
154
155 // Setter for metadata
156 access(contract)
157 fun setMetadata(metadata: {String: AnyStruct}) {
158 self.metadata = metadata
159 emit SquadMetadataUpdated(id: self.id)
160 }
161
162 // Getter for competitionsMemberships
163 access(all)
164 view fun getCompetitionsMemberships(): {UInt64: AnyStruct} {
165 return self.competitionsMemberships
166 }
167
168 // Add competitionMembership
169 access(contract)
170 fun addCompetitionMembership(competitionID: UInt64, competitionMembershipData: AnyStruct) {
171 self.competitionsMemberships.insert(key: competitionID, competitionMembershipData)
172 emit SquadCompetitionMembershipAdded(id: self.id, competitionID: competitionID)
173 }
174
175 // Update competitionMembership
176 access(contract)
177 fun updateCompetitionMembership(competitionID: UInt64, competitionMembershipData: AnyStruct) {
178 pre {
179 self.competitionsMemberships[competitionID] != nil:
180 "Competition membership not found"
181 }
182 self.competitionsMemberships[competitionID] = competitionMembershipData
183 emit SquadCompetitionMembershipUpdated(id: self.id, competitionID: competitionID)
184 }
185
186 // Remove competitionMembership
187 access(contract)
188 fun removeCompetitionMembership(competitionID: UInt64) {
189 self.competitionsMemberships.remove(key: competitionID)
190 emit SquadCompetitionMembershipRemoved(id: self.id, competitionID: competitionID)
191 }
192
193 // Getter for status
194 access(all)
195 view fun getStatus(): SquadStatus {
196 return self.status
197 }
198 }
199
200 access(all)
201 resource Squad {
202 access(all)
203 let id: UInt64
204
205 access(all)
206 let clubID: UInt64
207
208 access(all)
209 let type: String
210
211 access(self)
212 var metadata: {String: AnyStruct}
213
214 init(
215 id: UInt64,
216 clubID: UInt64,
217 type: String,
218 nftMetadata: {String: AnyStruct},
219 metadata: {String: AnyStruct},
220 competitionsMemberships: {UInt64: AnyStruct}
221 ) {
222 pre {
223 MFLClub.getSquadData(id: id) == nil:
224 "Squad already exists"
225 }
226 self.id = id
227 self.clubID = clubID
228 self.type = type
229 self.metadata = nftMetadata
230 MFLClub.squadsTotalSupply = MFLClub.squadsTotalSupply + 1 as UInt64
231
232 // Set squad data
233 MFLClub.squadsDatas[id] = MFLClub.SquadData(id: id, clubID: clubID, type: type, metadata: metadata, competitionsMemberships: competitionsMemberships)
234 emit SquadMinted(id: self.id)
235 }
236 }
237
238 access(all)
239 enum ClubStatus: UInt8 {
240 access(all)
241 case NOT_FOUNDED
242
243 access(all)
244 case PENDING_VALIDATION
245
246 access(all)
247 case FOUNDED
248 }
249
250 // Data stored in clubsDatas. Updatable by an admin
251 access(all)
252 struct ClubData {
253 access(all)
254 let id: UInt64
255
256 access(self)
257 var status: ClubStatus
258
259 access(self)
260 var squadsIDs: [UInt64]
261
262 access(self)
263 var metadata: {String: AnyStruct}
264
265 init(id: UInt64, status: ClubStatus, squadsIDs: [UInt64], metadata: {String: AnyStruct}) {
266 self.id = id
267 self.status = status
268 self.squadsIDs = squadsIDs
269 self.metadata = metadata
270 }
271
272 // Getter for status
273 access(all)
274 view fun getStatus(): ClubStatus {
275 return self.status
276 }
277
278 // Setter for status
279 access(contract)
280 fun setStatus(status: ClubStatus) {
281 self.status = status
282 emit ClubStatusUpdated(id: self.id, status: self.status.rawValue)
283 }
284
285 // Getter for squadsIDs
286 access(all)
287 view fun getSquadIDs(): [UInt64] {
288 return self.squadsIDs
289 }
290
291 // Setter for squadsIDs
292 access(contract)
293 fun setSquadsIDs(squadsIDs: [UInt64]) {
294 self.squadsIDs = squadsIDs
295 emit ClubSquadsIDsUpdated(id: self.id, squadsIDs: self.squadsIDs)
296 }
297
298 // Getter for metadata
299 access(all)
300 view fun getMetadata(): {String: AnyStruct} {
301 return self.metadata
302 }
303
304 // Setter for metadata
305 access(contract)
306 fun setMetadata(metadata: {String: AnyStruct}) {
307 self.metadata = metadata
308 emit ClubMetadataUpdated(id: self.id)
309 }
310 }
311
312 // The resource that represents the Club NFT
313 access(all)
314 resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
315 access(all)
316 let id: UInt64
317
318 access(self)
319 let squads: @{UInt64: Squad}
320
321 access(self)
322 let metadata: {String: AnyStruct}
323
324 init(id: UInt64, squads: @[Squad], nftMetadata: {String: AnyStruct}, metadata: {String: AnyStruct}) {
325 pre {
326 MFLClub.getClubData(id: id) == nil:
327 "Club already exists"
328 }
329 self.id = id
330 self.squads <-{}
331 self.metadata = nftMetadata
332 let squadsIDs: [UInt64] = []
333 while squads.length > 0 {
334 squadsIDs.append(squads[0].id)
335 let oldSquad <- self.squads[squads[0].id] <- squads.remove(at: 0)
336 destroy oldSquad
337 }
338 destroy squads
339 MFLClub.totalSupply = MFLClub.totalSupply + 1 as UInt64
340
341 // Set club data
342 MFLClub.clubsDatas[id] = ClubData(id: self.id, status: ClubStatus.NOT_FOUNDED, squadsIDs: squadsIDs, metadata: metadata)
343 emit ClubMinted(id: self.id)
344 }
345
346 // Get all supported views for this NFT
347 access(all)
348 view fun getViews(): [Type] {
349 return [
350 Type<MetadataViews.Display>(),
351 Type<MetadataViews.Royalties>(),
352 Type<MetadataViews.NFTCollectionDisplay>(),
353 Type<MetadataViews.NFTCollectionData>(),
354 Type<MetadataViews.ExternalURL>(),
355 Type<MetadataViews.Traits>(),
356 Type<MetadataViews.Serial>()
357 ]
358 }
359
360 // Resolve a specific view
361 access(all)
362 fun resolveView(_ view: Type): AnyStruct? {
363 let clubData = MFLClub.getClubData(id: self.id)!
364 return MFLClub.resolveViewFromData(view, clubData: clubData)
365
366 }
367
368 // Getter for metadata
369 access(contract)
370 view fun getMetadata(): {String: AnyStruct} {
371 return self.metadata
372 }
373
374 access(all)
375 fun createEmptyCollection(): @{NonFungibleToken.Collection} {
376 return <-create Collection()
377 }
378 }
379
380 access(all)
381 fun resolveViewFromData(_ view: Type, clubData: ClubData): AnyStruct? {
382 switch view {
383 case Type<MetadataViews.Display>():
384 if clubData.getStatus() == ClubStatus.NOT_FOUNDED {
385 return MetadataViews.Display(
386 name: "Club License #".concat(clubData.id.toString()),
387 description: "MFL Club License #".concat(clubData.id.toString()),
388 thumbnail: MetadataViews.HTTPFile(
389 url: MFLAdmin.imageHostUrl().concat("/clubs/").concat(clubData.id.toString()).concat("/licenses/foundation.png")
390 )
391 )
392 } else {
393 let clubMetadata = clubData.getMetadata()
394 let division: UInt32? = clubMetadata["division"] as! UInt32?
395 let clubDescription = clubMetadata["description"] as! String? ?? ""
396 return MetadataViews.Display(
397 name: clubMetadata["name"] as! String? ?? "",
398 description: "Before purchasing this MFL Club, make sure to check the club's in-game profile for the latest information: https://app.playmfl.com/clubs/"
399 .concat(clubData.id.toString())
400 .concat(clubDescription != "" ? "\n\n---\n\n".concat(clubDescription) : ""),
401 thumbnail: MetadataViews.HTTPFile(
402 url: MFLAdmin.imageHostUrl()
403 .concat("/u/clubs/")
404 .concat(clubData.id.toString())
405 .concat("/logo.webp")
406 .concat(division != nil ? "?v=".concat(division!.toString()) : "")
407 )
408 )
409 }
410 case Type<MetadataViews.Royalties>():
411 let royalties: [MetadataViews.Royalty] = []
412 let royaltyReceiverCap = getAccount(MFLAdmin.royaltyAddress()).capabilities.get<&{FungibleToken.Receiver}>(/public/GenericFTReceiver)
413 royalties.append(MetadataViews.Royalty(receiver: royaltyReceiverCap!, cut: 0.05, description: "Creator Royalty"))
414 return MetadataViews.Royalties(royalties)
415 case Type<MetadataViews.NFTCollectionDisplay>():
416 return MFLClub.resolveContractView(resourceType: Type<@MFLClub.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
417 case Type<MetadataViews.NFTCollectionData>():
418 return MFLClub.resolveContractView(resourceType: Type<@MFLClub.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
419 case Type<MetadataViews.ExternalURL>():
420 return MetadataViews.ExternalURL("https://playmfl.com")
421 case Type<MetadataViews.Traits>():
422 let traits: [MetadataViews.Trait] = []
423
424 // TODO must be fixed correctly in the data rather than here.
425 // foundationLicenseCity and foundationLicenseCountry should always be of type String? in the metadata
426 let clubMetadata = clubData.getMetadata()
427 var city: String? = nil
428 var country: String? = nil
429 if clubData.getStatus() == ClubStatus.NOT_FOUNDED {
430 city = clubMetadata["foundationLicenseCity"] as! String?
431 country = clubMetadata["foundationLicenseCountry"] as! String?
432 } else {
433 city = clubMetadata["foundationLicenseCity"] as! String?? ?? nil
434 country = clubMetadata["foundationLicenseCountry"] as! String?? ?? nil
435 }
436 let division: UInt32? = clubMetadata["division"] as! UInt32?
437
438 traits.append(MetadataViews.Trait(name: "city", value: city, displayType: "String", rarity: nil))
439 traits.append(MetadataViews.Trait(name: "country", value: country, displayType: "String", rarity: nil))
440
441 if division != nil {
442 traits.append(MetadataViews.Trait(name: "division", value: division, displayType: "Number", rarity: nil))
443 } else {
444 let squadsIDs = clubData.getSquadIDs()
445 if squadsIDs.length > 0 {
446 let firstSquadID = squadsIDs[0]
447 if let squadData = MFLClub.getSquadData(id: firstSquadID) {
448 if let globalLeagueMembership = squadData.getCompetitionsMemberships()[1] {
449 if let globalLeagueMembershipDataOptional = globalLeagueMembership as? {String: AnyStruct}? {
450 if let globalLeagueMembershipData = globalLeagueMembershipDataOptional {
451 traits.append(MetadataViews.Trait(
452 name: "division",
453 value: globalLeagueMembershipData["division"] as! UInt32?,
454 displayType: "Number",
455 rarity: nil
456 ))
457 }
458 }
459 }
460 }
461 }
462 }
463
464 return MetadataViews.Traits(traits)
465 case Type<MetadataViews.Serial>():
466 return MetadataViews.Serial(clubData.id)
467 }
468 return nil
469 }
470
471 // A collection of Club NFTs owned by an account
472 access(all)
473 resource Collection: NonFungibleToken.Collection, ViewResolver.ResolverCollection {
474
475 // Dictionary of NFT conforming tokens
476 access(all)
477 var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
478
479 // Removes an NFT from the collection and moves it to the caller
480 access(NonFungibleToken.Withdraw)
481 fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
482 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
483
484 emit Withdraw(id: token.id, from: self.owner?.address)
485
486 return <-token
487 }
488
489 // Withdraws multiple Clubs and returns them as a Collection
490 access(NonFungibleToken.Withdraw)
491 fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
492 var batchCollection <- create Collection()
493
494 // Iterate through the ids and withdraw them from the Collection
495 for id in ids {
496 batchCollection.deposit(token: <-self.withdraw(withdrawID: id))
497 }
498 return <-batchCollection
499 }
500
501 // Takes a NFT and adds it to the collections dictionary and adds the ID to the id array
502 access(all)
503 fun deposit(token: @{NonFungibleToken.NFT}) {
504 let token <- token as! @MFLClub.NFT
505 let id: UInt64 = token.id
506
507 // Add the new token to the dictionary which removes the old one
508 let oldToken <- self.ownedNFTs[id] <- token
509
510 emit Deposit(id: id, to: self.owner?.address)
511
512 destroy oldToken
513 }
514
515 // Returns an array of the IDs that are in the collection
516 access(all)
517 view fun getIDs(): [UInt64] {
518 return self.ownedNFTs.keys
519 }
520
521 access(all)
522 view fun getLength(): Int {
523 return self.ownedNFTs.length
524 }
525
526 // Gets a reference to an NFT in the collection so that the caller can read its metadata and call its methods
527 access(all)
528 view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
529 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
530 }
531
532 access(all)
533 view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
534 if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
535 return nft as &{ViewResolver.Resolver}
536 }
537 return nil
538 }
539
540 access(self)
541 view fun borrowClubRef(id: UInt64): &MFLClub.NFT? {
542 let ref = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}?
543 return ref as! &MFLClub.NFT?
544 }
545
546 access(ClubAction)
547 fun foundClub(id: UInt64, name: String, description: String) {
548 let clubRef = self.borrowClubRef(id: id) ?? panic("Club not found")
549 let clubData = MFLClub.getClubData(id: id) ?? panic("Club data not found")
550 assert(clubData.getStatus() == ClubStatus.NOT_FOUNDED, message: "Club already founded")
551 let updatedMetadata = clubData.getMetadata()
552 let foundationDate = getCurrentBlock().timestamp
553 let foundationLicenseSerialNumber = clubRef.getMetadata()["foundationLicenseSerialNumber"] as! UInt64?
554 let foundationLicenseCity = clubRef.getMetadata()["foundationLicenseCity"] as! String?
555 let foundationLicenseCountry = clubRef.getMetadata()["foundationLicenseCountry"] as! String?
556 let foundationLicenseSeason = clubRef.getMetadata()["foundationLicenseSeason"] as! UInt32?
557 let foundationLicenseImage = clubRef.getMetadata()["foundationLicenseImage"] as! MetadataViews.IPFSFile?
558 updatedMetadata.insert(key: "name", name)
559 updatedMetadata.insert(key: "description", description)
560 updatedMetadata.insert(key: "foundationDate", foundationDate)
561 updatedMetadata.insert(key: "foundationLicenseSerialNumber", foundationLicenseSerialNumber)
562 updatedMetadata.insert(key: "foundationLicenseCity", foundationLicenseCity)
563 updatedMetadata.insert(key: "foundationLicenseCountry", foundationLicenseCountry)
564 updatedMetadata.insert(key: "foundationLicenseSeason", foundationLicenseSeason)
565 updatedMetadata.insert(key: "foundationLicenseImage", foundationLicenseImage)
566 (MFLClub.clubsDatas[id]!).setMetadata(metadata: updatedMetadata)
567 (MFLClub.clubsDatas[id]!).setStatus(status: ClubStatus.PENDING_VALIDATION)
568 emit ClubFounded(
569 id: id,
570 from: self.owner?.address,
571 name: name,
572 description: description,
573 foundationDate: foundationDate,
574 foundationLicenseSerialNumber: foundationLicenseSerialNumber,
575 foundationLicenseCity: foundationLicenseCity,
576 foundationLicenseCountry: foundationLicenseCountry,
577 foundationLicenseSeason: foundationLicenseSeason
578 )
579 }
580
581 access(ClubAction)
582 fun requestClubInfoUpdate(id: UInt64, info: {String: String}) {
583 pre {
584 self.getIDs().contains(id) == true:
585 "Club not found"
586 }
587 let clubData = MFLClub.getClubData(id: id) ?? panic("Club data not found")
588 assert(clubData.getStatus() == ClubStatus.FOUNDED, message: "Club not founded")
589 emit ClubInfoUpdateRequested(id: id, info: info)
590 }
591
592 access(all)
593 view fun getSupportedNFTTypes(): {Type: Bool} {
594 let supportedTypes: {Type: Bool} = {}
595 supportedTypes[Type<@MFLClub.NFT>()] = true
596 return supportedTypes
597 }
598
599 access(all)
600 view fun isSupportedNFTType(type: Type): Bool {
601 return type == Type<@MFLClub.NFT>()
602 }
603
604 access(all)
605 fun createEmptyCollection(): @{NonFungibleToken.Collection} {
606 return <-create Collection()
607 }
608
609 access(contract)
610 view fun emitNFTUpdated(_ id: UInt64) {
611 MFLClub.emitNFTUpdated((&self.ownedNFTs[id] as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT}?)!)
612 }
613
614 init() {
615 self.ownedNFTs <-{}
616 }
617 }
618
619 // Public function that anyone can call to create a new empty collection
620 access(all)
621 fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
622 return <-create Collection()
623 }
624
625 // Get data for a specific club ID
626 access(all)
627 view fun getClubData(id: UInt64): ClubData? {
628 return self.clubsDatas[id]
629 }
630
631 // Get data for a specific squad ID
632 access(all)
633 view fun getSquadData(id: UInt64): SquadData? {
634 return self.squadsDatas[id]
635 }
636
637 access(all)
638 view fun getContractViews(resourceType: Type?): [Type] {
639 return [
640 Type<MetadataViews.NFTCollectionData>(),
641 Type<MetadataViews.NFTCollectionDisplay>()
642 ]
643 }
644
645 access(all)
646 fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
647 switch viewType {
648 case Type<MetadataViews.NFTCollectionData>():
649 let collectionData = MetadataViews.NFTCollectionData(
650 storagePath: self.CollectionStoragePath,
651 publicPath: self.CollectionPublicPath,
652 publicCollection: Type<&MFLClub.Collection>(),
653 publicLinkedType: Type<&MFLClub.Collection>(),
654 createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
655 return <-MFLClub.createEmptyCollection(nftType: Type<@MFLClub.NFT>())
656 })
657 )
658 return collectionData
659 case Type<MetadataViews.NFTCollectionDisplay>():
660 return MetadataViews.NFTCollectionDisplay(
661 name: "MFL Club Collection",
662 description: "Build your own football club, make strategic decisions, and live the thrill of real competition. Join a universe where the stakes–and your rivals–are real.",
663 externalURL: MetadataViews.ExternalURL("https://playmfl.com"),
664 squareImage: MetadataViews.Media(
665 file: MetadataViews.HTTPFile(url: "https://app.playmfl.com/img/collections/clubs/thumbnail.png"),
666 mediaType: "image/png"
667 ),
668 bannerImage: MetadataViews.Media(
669 file: MetadataViews.HTTPFile(url: "https://app.playmfl.com/img/collections/clubs/banner.png"),
670 mediaType: "image/png"
671 ),
672 socials: {
673 "twitter": MetadataViews.ExternalURL("https://twitter.com/playMFL"),
674 "discord": MetadataViews.ExternalURL("https://discord.gg/pEDTR4wSPr"),
675 "linkedin": MetadataViews.ExternalURL("https://www.linkedin.com/company/playmfl"),
676 "medium": MetadataViews.ExternalURL("https://medium.com/playmfl")
677 }
678 )
679 }
680 return nil
681 }
682
683 // Deprecated: Only here for backward compatibility.
684 access(all)
685 resource interface ClubAdminClaim {}
686
687 access(all)
688 resource ClubAdmin: ClubAdminClaim {
689 access(all)
690 let name: String
691
692 init() {
693 self.name = "ClubAdminClaim"
694 }
695
696 access(ClubAdminAction)
697 fun mintClub(id: UInt64, squads: @[Squad], nftMetadata: {String: AnyStruct}, metadata: {String: AnyStruct}): @MFLClub.NFT {
698 let club <- create MFLClub.NFT(id: id, squads: <-squads, nftMetadata: nftMetadata, metadata: metadata)
699 return <-club
700 }
701
702 access(ClubAdminAction)
703 fun updateClubStatus(id: UInt64, status: ClubStatus) {
704 pre {
705 MFLClub.getClubData(id: id) != nil:
706 "Club data not found"
707 }
708 (MFLClub.clubsDatas[id]!).setStatus(status: status)
709 }
710
711 access(ClubAdminAction)
712 fun updateClubMetadata(id: UInt64, metadata: {String: AnyStruct}, collectionRefOptional: &MFLClub.Collection?) {
713 pre {
714 MFLClub.getClubData(id: id) != nil:
715 "Club data not found"
716 }
717 (MFLClub.clubsDatas[id]!).setMetadata(metadata: metadata)
718 if let collectionRef = collectionRefOptional {
719 collectionRef.emitNFTUpdated(id)
720 }
721 }
722
723 access(ClubAdminAction)
724 fun updateClubSquadsIDs(id: UInt64, squadsIDs: [UInt64]) {
725 pre {
726 MFLClub.getClubData(id: id) != nil:
727 "Club data not found"
728 }
729 (MFLClub.clubsDatas[id]!).setSquadsIDs(squadsIDs: squadsIDs)
730 }
731 }
732
733 // Deprecated: Only here for backward compatibility.
734 access(all)
735 resource interface SquadAdminClaim {}
736
737 access(all)
738 resource SquadAdmin: SquadAdminClaim {
739 access(all)
740 let name: String
741
742 init() {
743 self.name = "SquadAdminClaim"
744 }
745
746 access(SquadAdminAction)
747 fun mintSquad(
748 id: UInt64,
749 clubID: UInt64,
750 type: String,
751 nftMetadata: {String: AnyStruct},
752 metadata: {String: AnyStruct},
753 competitionsMemberships: {UInt64: AnyStruct}
754 ): @Squad {
755 let squad <- create Squad(
756 id: id,
757 clubID: clubID,
758 type: type,
759 nftMetadata: nftMetadata,
760 metadata: metadata,
761 competitionsMemberships: competitionsMemberships
762 )
763 return <-squad
764 }
765
766 access(SquadAdminAction)
767 fun updateSquadMetadata(id: UInt64, metadata: {String: AnyStruct}) {
768 pre {
769 MFLClub.getSquadData(id: id) != nil:
770 "Squad data not found"
771 }
772 (MFLClub.squadsDatas[id]!).setMetadata(metadata: metadata)
773 }
774
775 access(SquadAdminAction)
776 fun addSquadCompetitionMembership(id: UInt64, competitionID: UInt64, competitionMembershipData: AnyStruct) {
777 pre {
778 MFLClub.getSquadData(id: id) != nil:
779 "Squad data not found"
780 }
781 (MFLClub.squadsDatas[id]!).addCompetitionMembership(competitionID: competitionID, competitionMembershipData: competitionMembershipData)
782 }
783
784 access(SquadAdminAction)
785 fun updateSquadCompetitionMembership(id: UInt64, competitionID: UInt64, competitionMembershipData: AnyStruct) {
786 pre {
787 MFLClub.getSquadData(id: id) != nil:
788 "Squad data not found"
789 }
790 (MFLClub.squadsDatas[id]!).updateCompetitionMembership(competitionID: competitionID, competitionMembershipData: competitionMembershipData)
791 }
792
793 access(SquadAdminAction)
794 fun removeSquadCompetitionMembership(id: UInt64, competitionID: UInt64) {
795 pre {
796 MFLClub.getSquadData(id: id) != nil:
797 "Squad data not found"
798 }
799 (MFLClub.squadsDatas[id]!).removeCompetitionMembership(competitionID: competitionID)
800 }
801 }
802
803 init() {
804 // Set our named paths
805 self.CollectionStoragePath = /storage/MFLClubCollection
806 self.CollectionPrivatePath = /private/MFLClubCollection
807 self.CollectionPublicPath = /public/MFLClubCollection
808 self.ClubAdminStoragePath = /storage/MFLClubAdmin
809 self.SquadAdminStoragePath = /storage/MFLSquadAdmin
810
811 // Put a new Collection in storage
812 self.account.storage.save<@Collection>(<-create Collection(), to: self.CollectionStoragePath)
813 // Create a public capability for the Collection
814 var capability_1 = self.account.capabilities.storage.issue<&MFLClub.Collection>(self.CollectionStoragePath)
815 self.account.capabilities.publish(capability_1, at: self.CollectionPublicPath)
816
817 // Create a ClubAdmin resource and save it to storage
818 self.account.storage.save(<-create ClubAdmin(), to: self.ClubAdminStoragePath)
819 // Create SquadAdmin resource and save it to storage
820 self.account.storage.save(<-create SquadAdmin(), to: self.SquadAdminStoragePath)
821
822 // Initialize contract fields
823 self.totalSupply = 0
824 self.squadsTotalSupply = 0
825 self.clubsDatas = {}
826 self.squadsDatas = {}
827 emit ContractInitialized()
828 }
829}
830