Smart Contract

MFLClub

A.8ebcbfd516b1da27.MFLClub

Valid From

135,750,666

Deployed

2w ago
Feb 11, 2026, 05:35:56 PM UTC

Dependents

3444 imports
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