Smart Contract
aiSportsMinter
A.abe5a2bf47ce5bf3.aiSportsMinter
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