Smart Contract

XvsX

A.f2af175e411dfff8.XvsX

Deployed

2h ago
Feb 28, 2026, 11:20:45 PM UTC

Dependents

0 imports
1 /**
2  This program is free software: you can redistribute it and/or modify
3  it under the terms of the GNU General Public License as published by
4  the Free Software Foundation, either version 3 of the License, or
5  (at your option) any later version.
6
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  GNU General Public License for more details.
11
12  You should have received a copy of the GNU General Public License
13  along with this program.  If not, see <https://www.gnu.org/licenses/>.
14**/
15import NonFungibleToken from 0x1d7e57aa55817448
16import MetadataViews from 0x1d7e57aa55817448
17import FungibleToken from 0xf233dcee88fe0abe
18
19pub contract XvsX: NonFungibleToken {
20  pub var totalSupply: UInt64
21
22  pub event ContractInitialized()
23  pub event Withdraw(id: UInt64, from: Address?)
24  pub event Deposit(id: UInt64, to: Address?)
25  pub event Minted(id: UInt64, file: MetadataViews.IPFSFile, metadata: {String:String})
26  pub event Burned(id: UInt64, address: Address?)
27
28  pub let CollectionStoragePath: StoragePath
29  pub let CollectionPublicPath: PublicPath
30  pub let MinterStoragePath: StoragePath
31
32  pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
33    pub let id: UInt64
34    pub let file: MetadataViews.IPFSFile
35    access(self) let royalties: [MetadataViews.Royalty]
36    access(self) let metadata: {String:String}
37
38    init(
39      file: MetadataViews.IPFSFile,
40      royalties: [MetadataViews.Royalty],
41      metadata: {String:String}
42    ) {
43      self.id = XvsX.totalSupply
44      self.file = file
45      self.royalties = royalties
46      self.metadata = metadata
47
48      emit Minted(
49        id: self.id,
50        file: self.file,
51        metadata: self.metadata
52      )
53
54      XvsX.totalSupply = XvsX.totalSupply + 1
55    }
56  
57    pub fun getViews(): [Type] {
58      return [
59        Type<MetadataViews.Display>(),
60        Type<MetadataViews.Royalties>(),
61        Type<MetadataViews.Editions>(),
62        Type<MetadataViews.ExternalURL>(),
63        Type<MetadataViews.NFTCollectionData>(),
64        Type<MetadataViews.NFTCollectionDisplay>(),
65        Type<MetadataViews.Serial>(),
66        Type<MetadataViews.Traits>()
67      ]
68    }
69
70    pub fun resolveView(_ view: Type): AnyStruct? {
71      switch view {
72        case Type<MetadataViews.Display>():
73          return MetadataViews.Display(
74            name: "XvsX NFT",
75            description: "XvsX NFT #".concat(self.id.toString()),
76            thumbnail: MetadataViews.HTTPFile(
77              url: "https://ipfs.tenzingai.com/ipfs/".concat(self.file.cid)
78            )
79          )
80
81        case Type<MetadataViews.Editions>():
82          // There is no max number of NFTs that can be minted from this contract
83          // so the max edition field value is set to nil
84          return MetadataViews.Editions([
85            MetadataViews.Edition(
86              name: "XvsX NFT Edition", 
87              number: self.id, 
88              max: nil
89            )
90          ])
91
92        case Type<MetadataViews.Serial>():
93          return MetadataViews.Serial(self.id)
94
95        case Type<MetadataViews.Royalties>():
96          let receivers: {Address:UFix64} = {
97            0x61a56aa81654c8a7: 0.005
98          }
99
100          let royalties: [MetadataViews.Royalty] = []
101          for address in receivers.keys {
102            let receiver = getAccount(address)
103              .getCapability<&{FungibleToken.Receiver}>(
104                MetadataViews.getRoyaltyReceiverPublicPath()
105              )
106            if receiver.check() {
107              royalties.append(
108                MetadataViews.Royalty(
109                  receiver: receiver, 
110                  cut: receivers[address]!, 
111                  description: ""
112                )
113              )
114            }
115          }
116
117          return MetadataViews.Royalties(royalties)
118
119        case Type<MetadataViews.ExternalURL>():
120          return MetadataViews.ExternalURL("https://ipfs.tenzingai.com/ipfs/".concat(self.file.cid))
121
122        case Type<MetadataViews.NFTCollectionData>():
123          return MetadataViews.NFTCollectionData(
124            storagePath: XvsX.CollectionStoragePath,
125            publicPath: XvsX.CollectionPublicPath,
126            providerPath: /private/XvsXCollection,
127            publicCollection: Type<&XvsX.Collection{NonFungibleToken.CollectionPublic,MetadataViews.ResolverCollection}>(),
128            publicLinkedType: Type<&XvsX.Collection{NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
129            providerLinkedType: Type<&XvsX.Collection{NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
130            createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
131                return <-XvsX.createEmptyCollection()
132            })
133          )
134
135        case Type<MetadataViews.NFTCollectionDisplay>():
136          let media = MetadataViews.Media(
137            file: MetadataViews.HTTPFile(url: "https://ipfs.tenzingai.com/ipfs/QmV1HKNKBriM3s2E9K73StS4u2A6ZKAEAZS1W8bQAKxBFk"),
138            mediaType: "image/png"
139          )
140          return MetadataViews.NFTCollectionDisplay(
141            name: "The XvsX NFT Collection",
142            description: "",
143            externalURL: MetadataViews.ExternalURL("https://mpc.xvsxsports.com"),
144            squareImage: media,
145            bannerImage: media,
146            socials: {}
147          )
148
149        case Type<MetadataViews.Traits>():
150          return MetadataViews.dictToTraits(dict: self.metadata, excludedNames: [])
151      }
152      return nil
153    }
154
155    destroy() {
156      emit Burned(id: self.id, address: self.owner?.address)
157    }
158  }
159
160  pub resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
161    // dictionary of NFT conforming tokens
162    // NFT is a resource type with an `UInt64` ID field
163    pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
164
165    init () {
166      self.ownedNFTs <- {}
167    }
168
169    // withdraw removes an NFT from the collection and moves it to the caller
170    pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
171      let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
172
173      emit Withdraw(id: token.id, from: self.owner?.address)
174
175      return <-token
176    }
177
178    // deposit takes a NFT and adds it to the collections dictionary
179    // and adds the ID to the id array
180    pub fun deposit(token: @NonFungibleToken.NFT) {
181      let token <- token as! @XvsX.NFT
182
183      let id: UInt64 = token.id
184
185      // add the new token to the dictionary which removes the old one
186      let oldToken <- self.ownedNFTs[id] <- token
187
188      emit Deposit(id: id, to: self.owner?.address)
189
190      destroy oldToken
191    }
192
193    // transfer takes an NFT ID and a reference to a recipient's collection
194    // and transfers the NFT corresponding to that ID to the recipient
195    pub fun transfer(id: UInt64, recipient: &{NonFungibleToken.CollectionPublic}) {
196      post {
197        self.ownedNFTs[id] == nil: "The specified NFT was not transferred"
198        recipient.borrowNFT(id: id) != nil: "Recipient did not receive the intended NFT"
199      }
200
201      let nft <- self.withdraw(withdrawID: id)
202      
203      recipient.deposit(token: <- nft)
204    }
205
206    // burn destroys an NFT
207    pub fun burn(id: UInt64) {
208      post {
209        self.ownedNFTs[id] == nil: "The specified NFT was not burned"
210      }
211
212      // This will emit a burn event
213      destroy <- self.withdraw(withdrawID: id)
214    }
215
216    // getIDs returns an array of the IDs that are in the collection
217    pub fun getIDs(): [UInt64] {
218      return self.ownedNFTs.keys
219    }
220
221    // borrowNFT gets a reference to an NFT in the collection
222    // so that the caller can read its metadata and call its methods
223    pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
224      if let nft = &self.ownedNFTs[id] as &NonFungibleToken.NFT? {
225        return nft
226      }
227      panic("NFT not found in collection.")
228    }
229
230    pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
231      if let nft = &self.ownedNFTs[id] as auth &NonFungibleToken.NFT? {
232        return nft as! &XvsX.NFT
233      }
234      panic("NFT not found in collection.")
235    }
236
237    destroy() {
238      destroy self.ownedNFTs
239    }
240  }
241
242  // public function that anyone can call to create a new empty collection
243  pub fun createEmptyCollection(): @NonFungibleToken.Collection {
244    return <- create Collection()
245  }
246
247  // Resource that an admin or something similar would own to be
248  // able to mint new NFTs
249  //
250  pub resource NFTMinter {
251    // transformMetadata ensures that the NFT metadata follows a particular
252    // schema. At the moment, it is much more convenient to use functions
253    // rather than structs to enforce a metadata schema because functions 
254    // are much more flexible, easier to maintain, and safer to update.
255    access(self) fun transformMetadata(_ metadata: {String:String}): {String:String} {
256      pre {
257        metadata.containsKey("nftType")
258        : "Metadata does not conform to schema"
259      }
260      return {
261        "nftType": metadata["nftType"]!
262      }
263    }
264
265    // mintNFT mints a new NFT with a new ID
266    // and deposit it in the recipients collection using their collection reference
267    pub fun mintNFT(
268      recipient: &{NonFungibleToken.CollectionPublic},
269      file: MetadataViews.IPFSFile,
270      royalties: [MetadataViews.Royalty],
271      metadata: {String:String},
272    ) {
273      // create a new NFT
274      let newNFT <- create NFT(
275        file: file,
276        royalties: royalties,
277        metadata: self.transformMetadata(metadata)
278      )
279
280      // deposit it in the recipient's account using their reference
281      recipient.deposit(token: <-newNFT)
282    }
283  }
284
285  init() {
286    // Initialize the total supply
287    self.totalSupply = 0
288
289    // Set the named paths
290    self.CollectionStoragePath = /storage/XvsXCollection
291    self.CollectionPublicPath = /public/XvsXCollection
292    self.MinterStoragePath = /storage/XvsXMinter
293
294    // Create a Collection resource and save it to storage
295    let collection <- create Collection()
296    self.account.save(<-collection, to: self.CollectionStoragePath)
297
298    // create a public capability for the collection
299    self.account.link<&XvsX.Collection{NonFungibleToken.CollectionPublic,MetadataViews.ResolverCollection}>(
300      self.CollectionPublicPath, 
301      target: self.CollectionStoragePath
302    )
303
304    // Create a Minter resource and save it to storage
305    let minter <- create NFTMinter()
306    self.account.save(<-minter, to: self.MinterStoragePath)
307
308    emit ContractInitialized()
309  }
310}
311