Smart Contract

aiSportsMinter

A.abe5a2bf47ce5bf3.aiSportsMinter

Deployed

2d ago
Feb 25, 2026, 02:55:55 AM UTC

Dependents

3456 imports
1//this address was 0xf8d6e0586b0a20c7 on localhost
2//testnet: 0x631e88ae7f1d7c20
3//mainnet: 0x1d7e57aa55817448
4
5//imports for emulator.
6import NonFungibleToken from 0x1d7e57aa55817448;
7import MetadataViews from 0x1d7e57aa55817448;
8import ViewResolver from 0x1d7e57aa55817448;
9
10//import NonFungibleToken from 0x631e88ae7f1d7c20
11//import MetadataViews from 0x631e88ae7f1d7c20
12//import ViewResolver from 0x631e88ae7f1d7c20
13
14access(all) contract aiSportsMinter: NonFungibleToken {
15  // Total supply of aiSportsMinters in existence
16  access(all) var totalSupply: UInt64
17  // total burned moments - not currently used, keeping for cadence upgrade
18  access(all) var totalBurned: UInt64
19
20  /// Standard Paths
21  access(all) let CollectionStoragePath: StoragePath
22  access(all) let CollectionPublicPath: PublicPath
23
24  /// Path where the minter should be stored
25  /// The standard paths for the collection are stored in the collection resource type
26  access(all) let MinterStoragePath: StoragePath
27
28  /// We choose the name NFT here, but this type can have any name now
29  /// because the interface does not require it to have a specific name any more
30  access(all) resource NFT: NonFungibleToken.NFT {
31    
32    // The unique ID that each NFT has
33    access(all) let id: UInt64
34
35    //Metadata fields
36    access(all) let name: String
37    access(all) let description: String
38    access(all) let thumbnail: String
39    access(all) let edition: String //this could be moved to Metadata
40    access(all) var items: @{UInt64: AnyResource}
41    /// For the Royalties metadata view
42    access(self) let royalties: [MetadataViews.Royalty]    
43    /// Generic dictionary of traits the NFT has
44    access(self) let metadata: {String: AnyStruct}
45
46    init(
47      id: UInt64,
48      name: String,
49      description: String,
50      edition: String,
51      thumbnail: String,
52      royalties: [MetadataViews.Royalty],
53      metadata: {String: AnyStruct},
54    ) {
55      self.id = id
56      self.name = name
57      self.description = description
58      self.edition = edition
59      self.thumbnail = thumbnail
60      self.items <- {}
61      self.royalties = royalties
62      self.metadata = metadata
63    }
64
65    /// createEmptyCollection creates an empty Collection
66    /// and returns it to the caller so that they can own NFTs
67    /// @{NonFungibleToken.Collection}
68    access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
69        return <-aiSportsMinter.createEmptyCollection(nftType: Type<@aiSportsMinter.NFT>())
70    }
71
72    /// Function that returns all the Metadata Views implemented by a Non Fungible Token
73    ///
74    /// @return An array of Types defining the implemented views. This value will be used by
75    /// developers to know which parameter to pass to the resolveView() method.
76    access(all) view fun getViews(): [Type] {
77        return [
78            Type<MetadataViews.Display>(),
79            Type<MetadataViews.Royalties>(),
80            Type<MetadataViews.Editions>(),
81            Type<MetadataViews.ExternalURL>(),
82            Type<MetadataViews.NFTCollectionData>(),
83            Type<MetadataViews.NFTCollectionDisplay>(),
84            Type<MetadataViews.Serial>(),
85            Type<MetadataViews.Traits>()
86        ]
87    }
88
89    /// Function that resolves a metadata view for this token.
90    ///
91    /// @param view: The Type of the desired view.
92    /// @return A structure representing the requested view.
93    ///
94    access(all) fun resolveView(_ view: Type): AnyStruct? {
95        switch view {
96            case Type<MetadataViews.Display>():
97                return MetadataViews.Display(
98                    name: self.name,
99                    description: self.description,
100                    thumbnail: MetadataViews.HTTPFile(
101                        url: self.thumbnail
102                    )
103                )
104            case Type<MetadataViews.Editions>():
105                // There is no max number of NFTs that can be minted from this contract
106                // so the max edition field value is set to nil
107                let editionInfo = MetadataViews.Edition(name: self.edition, number: self.id, max: nil)
108                let editionList: [MetadataViews.Edition] = [editionInfo]
109                return MetadataViews.Editions(
110                    editionList
111                )
112            case Type<MetadataViews.Serial>():
113                return MetadataViews.Serial(
114                    self.id
115                )
116            case Type<MetadataViews.Royalties>():
117                return MetadataViews.Royalties(
118                    self.royalties
119                )
120            case Type<MetadataViews.ExternalURL>():
121                return MetadataViews.ExternalURL("https://www.aisportspro.com/")
122            case Type<MetadataViews.NFTCollectionData>():
123                return aiSportsMinter.resolveContractView(resourceType: Type<@aiSportsMinter.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
124            case Type<MetadataViews.NFTCollectionDisplay>():
125                return aiSportsMinter.resolveContractView(resourceType: Type<@aiSportsMinter.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
126            case Type<MetadataViews.Traits>():
127                // exclude mintedTime and foo to show other uses of Traits
128                let excludedTraits = ["mintedTime"]
129                let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits)
130
131                // mintedTime is a unix timestamp, we should mark it with a displayType so platforms know how to show it.
132                let mintedTimeTrait = MetadataViews.Trait(name: "mintedTime", value: self.metadata["mintedTime"]!, displayType: "Date", rarity: nil)
133                traitsView.addTrait(mintedTimeTrait)
134
135                // foo is a trait with its own rarity
136                /* 
137                let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common")
138                let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity)
139                traitsView.addTrait(fooTrait) */
140                
141                return traitsView
142        }
143        return nil
144    }
145
146    access(contract) fun updateStatus(status: String) { //This is a custom function to allow for upgrading
147      self.metadata["status"] = status
148    }
149
150  }
151
152  /*
153  pub resource interface aiSportsMinterCollectionPublic {
154    pub fun deposit(token: @NonFungibleToken.NFT)
155    pub fun getIDs(): [UInt64]
156    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
157    pub fun borrowAiSportsMinter(id: UInt64): &aiSportsMinter.NFT? {
158      post {
159          (result == nil) || (result?.id == id):
160              "Cannot borrow aiSportsMinter reference: the ID of the returned reference is incorrect"
161      }
162    }
163  } */
164
165      // Deprecated: Only here for backward compatibility.
166    access(all) resource interface aiSportsMinterCollectionPublic {}
167
168    access(all) resource Collection: NonFungibleToken.Collection, aiSportsMinterCollectionPublic {
169    
170    // dictionary of NFT conforming tokens
171
172    /// NFT is a resource type with an `UInt64` ID field
173    access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
174
175    init () {
176      self.ownedNFTs <- {}
177    }
178
179    /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
180    access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
181        let supportedTypes: {Type: Bool} = {}
182        supportedTypes[Type<@aiSportsMinter.NFT>()] = true
183        return supportedTypes
184    }
185
186    /// Returns whether or not the given type is accepted by the collection
187    /// A collection that can accept any type should just return true by default
188    access(all) view fun isSupportedNFTType(type: Type): Bool {
189        return type == Type<@aiSportsMinter.NFT>()
190    }
191
192    /// withdraw removes an NFT from the collection and moves it to the caller
193    access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {        
194        let token <- self.ownedNFTs.remove(key: withdrawID)
195            ?? panic("Could not withdraw an NFT with the provided ID from the collection")
196
197        return <-token
198    }
199
200    /// deposit takes a NFT and adds it to the collections dictionary
201    /// and adds the ID to the id array
202    access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
203        let token <- token as! @aiSportsMinter.NFT
204         let id = token.id
205        // add the new token to the dictionary which removes the old one
206        let oldToken <- self.ownedNFTs[token.id] <- token
207
208        destroy oldToken
209    }
210
211    /// getIDs returns an array of the IDs that are in the collection
212    access(all) view fun getIDs(): [UInt64] {
213        return self.ownedNFTs.keys
214    }
215
216    /// Gets the amount of NFTs stored in the collection
217    access(all) view fun getLength(): Int {
218        return self.ownedNFTs.length
219    }
220
221    access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
222        return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
223    }
224
225    /// Borrow the view resolver for the specified NFT ID
226    access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
227        if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}?  {
228            return nft as &{ViewResolver.Resolver}
229        }
230        return nil
231    }
232
233    /// createEmptyCollection creates an empty Collection of the same type
234    /// and returns it to the caller
235    /// @return A an empty collection of the same type
236    access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
237        return <-aiSportsMinter.createEmptyCollection(nftType: Type<@aiSportsMinter.NFT>())
238    }
239
240  }
241
242  /// createEmptyCollection creates an empty Collection for the specified NFT type
243  /// and returns it to the caller so that they can own NFTs
244  access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {    
245      return <- create Collection()
246  }
247
248  access(all) resource Admin {
249    access(all) fun updateStatus(userNft: &aiSportsMinter.NFT, status: String) {
250      //let value = ref as! &aiSportsMinter.NFT
251      userNft.updateStatus(status: status)
252    }
253  }
254
255  /// Function that returns all the Metadata Views implemented by a Non Fungible Token
256  ///
257  /// @return An array of Types defining the implemented views. This value will be used by
258  ///         developers to know which parameter to pass to the resolveView() method.
259  ///
260  access(all) view fun getContractViews(resourceType: Type?): [Type] {
261      return [
262          Type<MetadataViews.NFTCollectionData>(),
263          Type<MetadataViews.NFTCollectionDisplay>()
264      ]
265  }
266
267  /// Function that resolves a metadata view for this contract.
268  ///
269  /// @param view: The Type of the desired view.
270  /// @return A structure representing the requested view.
271  ///
272  access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
273      switch viewType {
274          case Type<MetadataViews.NFTCollectionData>():
275              let collectionData = MetadataViews.NFTCollectionData(
276                  storagePath: self.CollectionStoragePath,
277                  publicPath: self.CollectionPublicPath,
278                  publicCollection: Type<&aiSportsMinter.Collection>(),
279                  publicLinkedType: Type<&aiSportsMinter.Collection>(),
280                  createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
281                      return <-aiSportsMinter.createEmptyCollection(nftType: Type<@aiSportsMinter.NFT>())
282                  })
283              )
284              return collectionData
285          case Type<MetadataViews.NFTCollectionDisplay>():
286            let media = MetadataViews.Media(
287                file: MetadataViews.HTTPFile(
288                    url: "https://firebasestorage.googleapis.com/v0/b/fantasyball-6e433.appspot.com/o/upload_image.png?alt=media&token=947bf82d-b697-4cb2-b58f-17b237705ae5"
289                ),
290                mediaType: "image/png"
291            )
292            return MetadataViews.NFTCollectionDisplay(
293                name: "The aiSports Collection",
294                description: "This collection is the home of the official aSports' NFTs.",
295                externalURL: MetadataViews.ExternalURL("https://www.aisportspro.com/"),
296                squareImage: media,
297                bannerImage: media,
298                socials: {
299                    "twitter": MetadataViews.ExternalURL("https://twitter.com/aisportspro")
300                }
301            ) 
302        }
303      return nil
304  }
305
306  /// Resource that an admin or something similar would own to be
307  /// able to mint new NFTs
308  ///
309  access(all) resource NFTMinter {
310
311    /// Mints a new NFT with a new ID and deposit it in the
312    /// recipients collection using their collection reference
313    ///
314    /// @param recipient: The reciever where the new NFT will be deposited
315    /// @param name: The name for the NFT metadata
316    /// @param description: The description for the NFT metadata
317    /// @param thumbnail: The thumbnail for the NFT metadata
318    /// @param royalties: An array of Royalty structs, see MetadataViews docs 
319    ///   
320
321    access(all) fun mintNFT(
322      
323      recipient: &{NonFungibleToken.Receiver},
324      name: String,
325      description: String,
326      edition: String,
327      thumbnail: String,
328      royalties: [MetadataViews.Royalty],
329      status: String,
330      player: String,
331      landscape: String,
332      scene: String,
333      style: String,
334      medium: String      
335    ): @aiSportsMinter.NFT {
336
337      let metadata: {String: AnyStruct} = {}
338      let currentBlock = getCurrentBlock()
339      metadata["mintedBlock"] = currentBlock.height
340      metadata["mintedTime"] = currentBlock.timestamp
341      metadata["minter"] = recipient.owner!.address
342
343      metadata["Status"] = status
344      metadata["Player"] = player
345      metadata["Landscape"] = landscape
346      metadata["Scene"] = scene
347      metadata["Style"] = style
348      metadata["Medium"] = medium
349
350
351      // create a new NFT
352      var newNFT <- create NFT(
353        id: aiSportsMinter.totalSupply,
354        name: name,
355        description: description,
356        edition: edition,
357        thumbnail: thumbnail,
358        royalties: royalties,
359        metadata: metadata,
360      )
361
362      aiSportsMinter.totalSupply = aiSportsMinter.totalSupply + UInt64(1)
363      
364      return <-newNFT
365    }
366  }
367
368  init() {
369    self.totalSupply = 0
370    self.totalBurned = 0
371
372    // Set the named paths
373    self.CollectionStoragePath = /storage/aiSportsMinterCollection
374    self.CollectionPublicPath = /public/aiSportsMinterCollection
375    self.MinterStoragePath = /storage/aiSportsMinterStorage //if we change this contract name to aiSports from aiSportsMinter, this storage should be /storage/aiSportsMinter
376
377    // Create a Collection resource and save it to storage
378    let collection <- create Collection()
379    self.account.storage.save(<-collection, to: self.CollectionStoragePath)
380
381    // create a public capability for the collection
382    let collectionCap = self.account.capabilities.storage.issue<&aiSportsMinter.Collection>(self.CollectionStoragePath)
383    self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
384
385    // Create a Minter resource and save it to storage
386    let minter <- create NFTMinter()
387    self.account.storage.save(<-minter, to: self.MinterStoragePath)
388  }
389}
390