Smart Contract
XvsX
A.f2af175e411dfff8.XvsX
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