Smart Contract

AFLNFT

A.8f9231920da9af6d.AFLNFT

Valid From

87,632,934

Deployed

2w ago
Feb 11, 2026, 06:36:06 PM UTC

Dependents

206 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import ViewResolver from 0x1d7e57aa55817448
5import Burner from 0xf233dcee88fe0abe
6import AFLBadges from 0x8f9231920da9af6d
7import AFLMetadataHelper from 0x8f9231920da9af6d
8import AFLBurnRegistry from 0x8f9231920da9af6d
9
10access(all) contract AFLNFT: NonFungibleToken {
11    // Events
12    access(all) event ContractInitialized()
13    access(all) event Withdraw(id: UInt64, from: Address?)
14    access(all) event Deposit(id: UInt64, to: Address?)
15    access(all) event NFTDestroyed(id: UInt64)
16    access(all) event NFTMinted(nftId: UInt64, templateId: UInt64, mintNumber: UInt64)
17    access(all) event TemplateCreated(templateId: UInt64, maxSupply: UInt64)
18    access(all) event NFTBurnt(nftId: UInt64, templateId: UInt64, mintNumber: UInt64)
19
20    // Paths
21    access(all) let CollectionStoragePath: StoragePath
22    access(all) let CollectionPublicPath: PublicPath
23    
24    // Latest template-id
25    access(all) var lastIssuedTemplateId: UInt64
26
27    // Total supply of all NFTs that are minted using this contract
28    access(all) var totalSupply: UInt64
29
30    // A dictionary that stores all Templates against it's template-id.
31    access(account) var allTemplates: {UInt64: Template}
32
33    // A dictionary that stores all NFTs against it's nft-id.
34    access(self) var allNFTs: {UInt64: NFTData}
35
36    // A structure that contain all the data and methods related to Template
37    access(all) struct Template {
38        access(all) let templateId: UInt64
39        access(all) var maxSupply: UInt64
40        access(all) var issuedSupply: UInt64
41        access(contract) var immutableData: {String: AnyStruct}
42
43        init(maxSupply: UInt64, immutableData: {String: AnyStruct}) {
44            pre {
45                maxSupply > 0 : "MaxSupply must be greater than zero"
46                immutableData != nil: "ImmutableData must not be nil"
47                immutableData.length != 0: "New template data cannot be empty"
48            }
49            
50            self.templateId = AFLNFT.lastIssuedTemplateId
51            self.maxSupply = maxSupply
52            self.immutableData = immutableData
53            self.issuedSupply = 0
54        }
55        access(all) view fun getImmutableData(): {String:AnyStruct} {
56            return self.immutableData
57        }
58
59        access(account) fun updateImmutableData(_ data: {String: AnyStruct}) {
60            for key in data.keys {
61                self.immutableData[key] = data[key]!
62            }
63        }
64
65        access(account) fun updateMaxSupply(_ maxSupply: UInt64) {
66            self.maxSupply = maxSupply
67        }
68
69        // a method to increment issued supply for template
70        access(account) fun incrementIssuedSupply(): UInt64 {
71            pre {
72                self.issuedSupply < self.maxSupply: "Template reached max supply"
73            }   
74            self.issuedSupply = self.issuedSupply + 1
75            return self.issuedSupply
76        }
77
78        access(account) fun decrementIssuedSupply(): UInt64 {
79            self.issuedSupply = self.issuedSupply - 1
80            return self.issuedSupply
81        }
82
83        access(account) fun addBadges() {
84            self.immutableData["badges"] = AFLBadges.getBadgesForTemplate(id: self.templateId)
85        }
86
87        access(account) fun addMetadata() {
88            for key in AFLMetadataHelper.getMetadataForTemplate(id: self.templateId).keys {
89                self.immutableData[key] = AFLMetadataHelper.getMetadataForTemplate(id: self.templateId)[key]!
90            }
91        }
92        access(account) fun updateSupplyInformation() {
93            self.immutableData["issuedSupply"] = self.issuedSupply
94            self.immutableData["circulatingSupply"] = self.issuedSupply - AFLBurnRegistry.getBurnDetails(templateId: self.templateId)
95            self.immutableData["burntSupply"] = AFLBurnRegistry.getBurnDetails(templateId: self.templateId)
96        }
97    }
98    // A structure that link template and mint-no of NFT
99    access(all) struct NFTData {
100        access(all) let templateId: UInt64
101        access(all) let mintNumber: UInt64
102
103        init(templateId: UInt64, mintNumber: UInt64) {
104            self.templateId = templateId
105            self.mintNumber = mintNumber
106        }
107    }
108    // The resource that represents the AFLNFT NFTs
109    // 
110    access(all) resource NFT: NonFungibleToken.NFT, Burner.Burnable {
111        access(all) let id: UInt64
112
113        init(templateId: UInt64, mintNumber: UInt64) {
114            AFLNFT.totalSupply = AFLNFT.totalSupply + 1
115
116            self.id = AFLNFT.totalSupply
117            AFLNFT.allNFTs[self.id] = NFTData(templateId: templateId, mintNumber: mintNumber)
118
119            emit NFTMinted(nftId: self.id, templateId: templateId, mintNumber: mintNumber)
120        }
121
122
123        access (contract) fun burnCallback() {
124            let nftData: &AFLNFT.NFTData = &AFLNFT.allNFTs[self.id]!
125            let templateId: UInt64 = nftData.templateId
126            let mintNumber: UInt64 = nftData.mintNumber
127            let templateRef: &AFLNFT.Template = &AFLNFT.allTemplates[templateId]!
128            // templateRef.decrementIssuedSupply()
129            // AFLNFT.totalSupply = AFLNFT.totalSupply - 1
130            // AFLNFT.allNFTs[self.id] = nil
131            emit NFTDestroyed(id: self.id)
132            emit NFTBurnt(nftId: self.id, templateId: templateId, mintNumber: mintNumber)
133            AFLBurnRegistry.burn(templateId: templateId)
134        }
135
136        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
137            return <- AFLNFT.createEmptyCollection(nftType: Type<@AFLNFT.NFT>())
138        }
139
140        access(all) view fun getViews(): [Type] {
141            return [
142                Type<MetadataViews.Display>(),
143                Type<MetadataViews.Royalties>(),
144                Type<MetadataViews.Editions>(),
145                Type<MetadataViews.ExternalURL>(),
146                Type<MetadataViews.NFTCollectionData>(),
147                Type<MetadataViews.NFTCollectionDisplay>(),
148                Type<MetadataViews.Serial>(),
149                Type<MetadataViews.Traits>()
150                // Type<MetadataViews.EVMBridgedMetadata>()
151            ]
152        }
153
154         access(all) fun resolveView(_ view: Type): AnyStruct? {
155            return nil
156         }      
157    }
158
159    access(all)resource interface AFLNFTCollectionPublic {
160        access(all) fun deposit(token: @{NonFungibleToken.NFT})
161        access(all) view fun getIDs(): [UInt64]
162        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}?
163        access(all) view fun borrowAFLNFT(id: UInt64): &AFLNFT.NFT? {
164            // If the result isn't nil, the id of the returned reference
165            // should be the same as the argument to the function
166            post {
167                (result == nil) || (result?.id == id):
168                    "Cannot borrow AFLNFT reference: The ID of the returned reference is incorrect"
169            }
170        }
171    }
172
173    // Collection is a resource that every user who owns NFTs 
174    // will store in their account to manage their NFTS
175    //
176    access(all) resource Collection: AFLNFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.Collection, NonFungibleToken.CollectionPublic, ViewResolver.ResolverCollection {
177    // AFLNFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic {
178        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
179
180        init() {
181            self.ownedNFTs <- {}
182        }
183
184        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
185        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
186            let supportedTypes: {Type: Bool} = {}
187            supportedTypes[Type<@AFLNFT.NFT>()] = true
188            return supportedTypes
189        }
190
191        /// Returns whether or not the given type is accepted by the collection
192        /// A collection that can accept any type should just return true by default
193        access(all) view fun isSupportedNFTType(type: Type): Bool {
194            return type == Type<@AFLNFT.NFT>()
195        }
196
197        access(all) fun forEachID(_ f: fun (UInt64): Bool): Void {}
198
199        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
200            assert(withdrawID != 87707, message: "Transfer of this NFT is not allowed!") // 87707 is the id of 2021 AFL Genesis Ball
201            let data = AFLNFT.getNFTData(nftId: withdrawID)
202            // assert(data.templateId != 0, message: "Transfer of this NFT is not allowed!") // yet to implement template transfer guard
203            let token: @{NonFungibleToken.NFT}  <- self.ownedNFTs.remove(key: withdrawID) 
204                ?? panic("Cannot withdraw: moment does not exist in the collection")
205
206            emit Withdraw(id: token.id, from: self.owner?.address)
207            return <-token
208        }
209
210        access(all) view fun getIDs(): [UInt64] {
211            return self.ownedNFTs.keys
212        }
213
214        access(all) view fun getLength(): Int {
215            return self.ownedNFTs.length
216        }
217
218        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
219            let token: @AFLNFT.NFT <- token as! @AFLNFT.NFT
220            let id: UInt64 = token.id
221            let oldToken: @{NonFungibleToken.NFT}? <- self.ownedNFTs[id] <- token
222            if self.owner?.address != nil {
223                emit Deposit(id: id, to: self.owner?.address)
224            }
225            Burner.burn(<-oldToken)
226        }
227
228        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
229            return (&self.ownedNFTs[id])
230        }
231        
232        access(all) view fun borrowAFLNFT(id: UInt64): &AFLNFT.NFT? {
233            if self.ownedNFTs[id] != nil {
234                let ref: &{NonFungibleToken.NFT} = (&self.ownedNFTs[id])!
235                return ref as! &AFLNFT.NFT?
236            }
237            else {
238                return nil
239            }
240        }
241
242        /// createEmptyCollection creates an empty Collection of the same type
243        /// and returns it to the caller
244        /// @return A an empty collection of the same type
245        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
246            return <- AFLNFT.createEmptyCollection(nftType: Type<@AFLNFT.NFT>())
247        }
248        
249    }
250
251    // account accessible methods called through AFLAdmin proxy contract/resource
252
253    access(account) fun createTemplate(maxSupply: UInt64, immutableData: {String: AnyStruct}): UInt64 {
254        let newTemplate: AFLNFT.Template = Template(maxSupply: maxSupply, immutableData: immutableData)
255        AFLNFT.allTemplates[AFLNFT.lastIssuedTemplateId] = newTemplate
256        emit TemplateCreated(templateId: AFLNFT.lastIssuedTemplateId, maxSupply: maxSupply)
257        AFLNFT.lastIssuedTemplateId = AFLNFT.lastIssuedTemplateId + 1
258        return AFLNFT.lastIssuedTemplateId - 1
259    }
260
261    access(account) fun mintNFT(templateInfo: {String: UInt64}, account: Address) {
262        pre {
263            account != nil: "invalid receipt Address"
264            AFLNFT.allTemplates[templateInfo["id"]!] != nil: "Template Id must be valid"
265        }
266        let receiptAccount: &Account = getAccount(account)
267
268
269        let recipientCollection: &{AFLNFT.AFLNFTCollectionPublic} = receiptAccount
270            .capabilities.get<&{AFLNFT.AFLNFTCollectionPublic}>(AFLNFT.CollectionPublicPath)
271            .borrow()
272            ?? panic("Could not get receiver reference to the NFT Collection")
273
274
275        let mintNumberFromSupply = AFLNFT.allTemplates[templateInfo["id"]!]!.incrementIssuedSupply()
276        let mintNumber = templateInfo["serial"] ?? mintNumberFromSupply
277        var newNFT: @NFT <- create NFT(templateId: templateInfo["id"]!, mintNumber: mintNumber)
278        recipientCollection.deposit(token: <-newNFT)
279    }
280
281    access(account) fun mintAndReturnNFT(templateInfo: {String: UInt64}): @{NonFungibleToken.NFT} {
282        pre {
283            AFLNFT.allTemplates[templateInfo["id"]!] != nil: "Template Id must be valid"
284        }
285        let mintNumberFromSupply: UInt64 = AFLNFT.allTemplates[templateInfo["id"]!]!.incrementIssuedSupply()
286        let mintNumber: UInt64 = templateInfo["serial"] ?? mintNumberFromSupply
287        var newNFT: @NFT <- create NFT(templateId: templateInfo["id"]!, mintNumber: mintNumber)
288        return <-newNFT
289    }
290
291    // public functions
292
293    // method to create empty Collection
294    /// @{NonFungibleToken.Collection}
295    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
296        return <- create Collection()
297    }
298
299    access(all) view fun getContractViews(resourceType: Type?): [Type] {
300        return [
301            Type<MetadataViews.NFTCollectionData>()
302        ]
303    }
304
305    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
306        switch viewType {
307
308         case Type<MetadataViews.NFTCollectionData>():
309                let collectionData: MetadataViews.NFTCollectionData = MetadataViews.NFTCollectionData(
310                    storagePath: self.CollectionStoragePath,
311                    publicPath: self.CollectionPublicPath,
312                    publicCollection: Type<&AFLNFT.Collection>(),
313                    publicLinkedType: Type<&AFLNFT.Collection>(),
314                    createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
315                        return <-AFLNFT.createEmptyCollection(nftType: Type<@AFLNFT.NFT>())
316                    })
317                )
318                return collectionData
319                
320            case Type<MetadataViews.NFTCollectionDisplay>():
321                let media: MetadataViews.Media = MetadataViews.Media(
322                    file: MetadataViews.HTTPFile(
323                        url: "https://www.aflmint.com.au/assets/logo/Mint-logo.png"
324                    ),
325                    mediaType: "image/png"
326                )
327                return MetadataViews.NFTCollectionDisplay(
328                    name: "The AFL Mint Collection",
329                    description: "The official AFL NFT collection on Flow",
330                    externalURL: MetadataViews.ExternalURL("https://www.aflmint.com.au/"),
331                    squareImage: media,
332                    bannerImage: media,
333                    socials: {
334                        "twitter": MetadataViews.ExternalURL("https://twitter.com/AFLMint"),
335                        "discord": MetadataViews.ExternalURL("https://discord.com/invite/aflmintofficial"),
336                        "instagram": MetadataViews.ExternalURL("https://www.instagram.com/aflmint/"),
337                        "youtube": MetadataViews.ExternalURL("https://www.youtube.com/channel/UCT1E0Rmm6_DY99dRTFI1hyQ"),
338                        "facebook": MetadataViews.ExternalURL("https://facebook.com/aflmint")
339                    }
340                )
341        }
342        return nil
343    }
344
345    
346    // access(all) fun
347    
348    //method to get all templates
349    access(all) fun getAllTemplates(): {UInt64: Template} { 
350        return AFLNFT.allTemplates
351    }
352
353    access(all) fun borrowTemplate(id: UInt64): &Template {
354        return &AFLNFT.allTemplates[id]!
355    }
356
357    //method to get the latest template id
358    access(all) fun getLatestTemplateId() : UInt64 {
359        return AFLNFT.lastIssuedTemplateId - 1
360    }
361
362    //method to get template by id
363    access(all) fun getTemplateById(templateId: UInt64): Template {
364        pre {
365            AFLNFT.allTemplates[templateId] != nil: "Template id does not exist"
366        }
367        let template = AFLNFT.allTemplates[templateId]! 
368        template.addBadges()
369        template.addMetadata()
370        template.updateSupplyInformation()
371        return template
372    } 
373
374    //method to get nft-data by id
375    access(all) fun getNFTData(nftId: UInt64): NFTData {
376        pre {
377            AFLNFT.allNFTs[nftId] != nil:"nft id does not exist"
378        }
379        return AFLNFT.allNFTs[nftId]!
380    }
381    
382    init() {
383        self.lastIssuedTemplateId = 1
384        self.totalSupply = 0
385        self.allTemplates = {}
386        self.allNFTs = {}
387        self.CollectionStoragePath = /storage/AFLNFTCollection
388        self.CollectionPublicPath = /public/AFLNFTCollection
389    }
390}
391