Smart Contract
MetaverseMarket
A.256599e1b091be12.MetaverseMarket
1// NFT Price - Will it be changable?
2// NFT MaxSupply - Will it be changable?
3// DELETE LISTED NFT?
4// CREATE A FIELD TO DISPLAY - NO SUPPLY - ON A LIST?
5
6//SPDX-License-Identifier : CC-BY-NC-4.0
7
8// testnet 0x635e88ae7f1d7c20
9import NonFungibleToken from 0x1d7e57aa55817448
10import MetadataViews from 0x1d7e57aa55817448
11import FungibleToken from 0xf233dcee88fe0abe
12
13// MetaverseMarket
14// NFT for MetaverseMarket
15//
16pub contract MetaverseMarket: NonFungibleToken {
17
18 // Events
19 //
20 pub event ContractInitialized()
21 pub event Withdraw(id: UInt64, from: Address?)
22 pub event Deposit(id: UInt64, to: Address?)
23 pub event Minted(id: UInt64, typeID: UInt64, metadata: {String:String})
24 pub event BatchMinted(ids: [UInt64], typeID: [UInt64], metadata: {String:String})
25 pub event NFTBurned(id: UInt64)
26 pub event NFTsBurned(ids: [UInt64])
27 pub event CategoryCreated(categoryName: String)
28 pub event SubCategoryCreated(subCategoryName: String)
29
30 // Named Paths
31 //
32 pub let CollectionStoragePath: StoragePath
33 pub let CollectionPublicPath: PublicPath
34 pub let AdminStoragePath: StoragePath
35
36 // totalSupply
37 // The total number of MetaverseMarkets that have been minted
38 //
39 pub var totalSupply: UInt64
40
41 // List with all categories and respective code
42 access(self) var categoriesList: {UInt64 :String}
43
44 // {CategoryName: [NFT To Sell ID]}
45 access(self) var categoriesNFTsToSell: {UInt64: [UInt64]}
46
47 //LISTINGS
48
49 // Dictionary with NFT List Data
50 access(self) var nftsToSell: {UInt64: OzoneListToSellMetadata}
51
52
53 pub struct OzoneListToSellMetadata {
54 //List ID that will came from the backend, all NFTs from same list will have same listId
55 pub let listId: UInt64
56 pub let name: String
57 pub let description: String
58 pub let categoryId: UInt64
59 pub let creator: Address
60 pub let fileName: String
61 pub let format: String
62 pub let fileIPFS: String
63 pub var price: UFix64
64 pub let maxSupply: UInt64
65 pub var minted: UInt64
66
67 pub fun addMinted(){
68 self.minted = self.minted + (1 as UInt64)
69 }
70
71 pub fun changePrice(newPrice: UFix64){
72 self.price = newPrice
73 }
74
75 init(_listId: UInt64, _name: String, _description: String, _categoryId: UInt64, _creator: Address, _fileName: String, _format: String, _fileIPFS: String, _price: UFix64, _maxSupply: UInt64){
76 self.listId = _listId
77 self.name = _name
78 self.description = _description
79 self.categoryId = _categoryId
80 self.creator = _creator
81 self.fileName = _fileName
82 self.format = _format
83 self.fileIPFS = _fileIPFS
84 self.price = _price
85 self.maxSupply = _maxSupply
86 self.minted = 0
87 }
88 }
89
90 // NFT
91 // MetaverseMarket as an NFT
92 //
93 pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
94 // The token's ID
95 //NFT CONTRACT GLOBAL ID -> Current TotalSupply
96 pub let id: UInt64
97
98 //Current List minted(List TotalSupply)
99 pub let uniqueListId: UInt64
100
101 //List ID that will came from the backend, all NFTs from same list will have same listId
102 pub let listId: UInt64
103
104 pub let name: String
105
106 pub let description: String
107
108 pub let categoryId: UInt64
109
110 pub let creator: Address
111
112 pub let fileName: String
113
114 pub let format: String
115
116 pub let fileIPFS: String
117
118
119
120 pub fun getViews(): [Type] {
121 return [
122 Type<MetadataViews.Display>(),
123 Type<MetadataViews.NFTCollectionData>(),
124 Type<MetadataViews.Royalties>(),
125 Type<MetadataViews.ExternalURL>(),
126 Type<MetadataViews.NFTCollectionDisplay>(),
127 Type<MetadataViews.Serial>()
128 ]
129 }
130
131 pub fun resolveView(_ view: Type): AnyStruct? {
132 switch view {
133 case Type<MetadataViews.Display>():
134 return MetadataViews.Display(
135 name: self.name,
136 description: self.description,
137 thumbnail: MetadataViews.IPFSFile(
138 cid: self.fileIPFS,
139 path: nil
140 )
141 )
142 case Type<MetadataViews.NFTCollectionData>():
143 return MetadataViews.NFTCollectionData(
144 storagePath: MetaverseMarket.CollectionStoragePath,
145 publicPath: MetaverseMarket.CollectionPublicPath,
146 providerPath: /private/ProvenancedCollectionsV9,
147 publicCollection: Type<&MetaverseMarket.Collection{MetaverseMarket.MetaverseMarketCollectionPublic}>(),
148 publicLinkedType: Type<&MetaverseMarket.Collection{MetaverseMarket.MetaverseMarketCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver}>(),
149 providerLinkedType: Type<&MetaverseMarket.Collection{MetaverseMarket.MetaverseMarketCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider}>(),
150 createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
151 return <-MetaverseMarket.createEmptyCollection()
152 })
153 )
154 }
155
156 return nil
157 }
158
159 // initializer
160 //
161 init(initID: UInt64, uniqueListId: UInt64, listId: UInt64, name: String, categoryId: UInt64, description: String , creator: Address, fileName: String, format: String, fileIPFS: String) {
162 self.id = initID
163 self.uniqueListId = uniqueListId
164 self.listId = listId
165 self.name = name
166 self.description = description
167 self.categoryId = categoryId
168 self.creator = creator
169 self.fileName = fileName
170 self.format = format
171 self.fileIPFS = fileIPFS
172
173 }
174
175 // If the NFT is burned, emit an event to indicate
176 // to outside observers that it has been destroyed
177 destroy() {
178 emit NFTBurned(id: self.id)
179 }
180 }
181
182 // This is the interface that users can cast their MetaverseMarket Collection as
183 // to allow others to deposit MetaverseMarket into their Collection. It also allows for reading
184 // the details of MetaverseMarket in the Collection.
185 pub resource interface MetaverseMarketCollectionPublic {
186 pub fun deposit(token: @NonFungibleToken.NFT)
187 pub fun getIDs(): [UInt64]
188 pub fun getNFTs(): &{UInt64: NonFungibleToken.NFT}
189 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
190 pub fun borrowMetaverseMarket(id: UInt64): &MetaverseMarket.NFT? {
191 // If the result isn't nil, the id of the returned reference
192 // should be the same as the argument to the function
193 post {
194 (result == nil) || (result?.id == id):
195 "Cannot borrow MetaverseMarket reference: The ID of the returned reference is incorrect"
196 }
197 }
198 }
199
200 // Collection
201 // A collection of MetaverseMarket NFTs owned by an account
202 //
203 pub resource Collection: MetaverseMarketCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic {
204 // dictionary of NFT conforming tokens
205 // NFT is a resource type with an `UInt64` ID field
206 //
207 pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
208
209 // withdraw
210 // Removes an NFT from the collection and moves it to the caller
211 //
212 pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
213 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
214
215 emit Withdraw(id: token.id, from: self.owner?.address)
216
217 return <-token
218 }
219
220 // deposit
221 // Takes a NFT and adds it to the collections dictionary
222 // and adds the ID to the id array
223 //
224 pub fun deposit(token: @NonFungibleToken.NFT) {
225 let token <- token as! @MetaverseMarket.NFT
226
227 let id: UInt64 = token.id
228
229 // add the new token to the dictionary which removes the old one
230 let oldToken <- self.ownedNFTs[id] <- token
231
232 emit Deposit(id: id, to: self.owner?.address)
233
234 destroy oldToken
235 }
236
237 // getIDs
238 // Returns an array of the IDs that are in the collection
239 //
240 pub fun getIDs(): [UInt64] {
241 return self.ownedNFTs.keys
242 }
243
244 pub fun getNFTs(): &{UInt64: NonFungibleToken.NFT} {
245 return (&self.ownedNFTs as auth &{UInt64: NonFungibleToken.NFT}?)!
246
247 }
248
249 // borrowNFT
250 // Gets a reference to an NFT in the collection
251 // so that the caller can read its metadata and call its methods
252 //
253 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
254 return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
255 }
256
257 // borrowMetaverseMarket
258 // Gets a reference to an NFT in the collection as a MetaverseMarket,
259 // exposing all of its fields (including the typeID).
260 // This is safe as there are no functions that can be called on the MetaverseMarket.
261 //
262 pub fun borrowMetaverseMarket(id: UInt64): &MetaverseMarket.NFT? {
263 if self.ownedNFTs[id] != nil {
264 let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
265 return ref as! &MetaverseMarket.NFT
266 } else {
267 return nil
268 }
269 }
270
271 // destructor
272 destroy() {
273 emit NFTsBurned(ids: self.ownedNFTs.keys)
274 destroy self.ownedNFTs
275 }
276
277 // initializer
278 //
279 init () {
280 self.ownedNFTs <- {}
281 }
282 }
283
284 // createEmptyCollection
285 // public function that anyone can call to create a new empty collection
286 //
287 pub fun createEmptyCollection(): @NonFungibleToken.Collection {
288 return <- create Collection()
289 }
290
291 // NFTAdmin
292 // Resource that an admin or something similar would own to be
293 // able to mint new NFTs
294 //
295 pub resource Admin {
296
297 pub fun createCategory(categoryId: UInt64, categoryName: String){
298
299 if UInt64(MetaverseMarket.categoriesList.length + 1) != categoryId {
300 panic("Category ID already exists")
301 }
302
303 MetaverseMarket.categoriesList[categoryId] = categoryName
304
305 MetaverseMarket.categoriesNFTsToSell[categoryId] = []
306
307 emit CategoryCreated(categoryName: categoryName)
308 }
309
310 pub fun createList(
311 listId: UInt64,
312 name: String,
313 description: String,
314 categoryId: UInt64,
315 creator: Address,
316 fileName: String,
317 format: String,
318 fileIPFS: String,
319 price: UFix64,
320 maxSupply: UInt64
321 ){
322
323 if UInt64(MetaverseMarket.nftsToSell.length + 1) != listId {
324 panic("NFT List ID already exists")
325 }
326
327 let list = OzoneListToSellMetadata(
328 _listId: listId,
329 _name: name,
330 _description: description,
331 _categoryId: categoryId,
332 _creator: creator,
333 _fileName: fileName,
334 _format: format,
335 _fileIPFS: fileIPFS,
336 _price: price,
337 _maxSupply: maxSupply
338 )
339
340 MetaverseMarket.nftsToSell[listId] = list
341
342 //Add the list to the categoriesNFTsToSell
343 MetaverseMarket.categoriesNFTsToSell[categoryId]!.append(listId)
344
345 }
346
347 // mintNFT
348 // Mints a new NFT with a new ID
349 // and deposit it in the recipients collection using their collection reference
350 //
351 pub fun mintNFT(recipient: &{NonFungibleToken.CollectionPublic}, payment: @FungibleToken.Vault, listedNftId: UInt64) {
352 pre{
353 MetaverseMarket.nftsToSell[listedNftId] != nil: "Listed ID does not exists!"
354 payment.balance == MetaverseMarket.nftsToSell[listedNftId]!.price: "Incorrect price!"
355 MetaverseMarket.nftsToSell[listedNftId]!.maxSupply != MetaverseMarket.nftsToSell[listedNftId]!.minted: "Max Supply reached!"
356 }
357
358 let list = MetaverseMarket.nftsToSell[listedNftId]!
359 MetaverseMarket.nftsToSell[listedNftId]!.addMinted()
360
361 // Get a reference to the recipient's Receiver
362 let receiverRef = getAccount(list.creator)
363 .getCapability(/public/flowTokenReceiver)
364 .borrow<&{FungibleToken.Receiver}>()
365 ?? panic("Could not borrow receiver reference to the recipient's Vault")
366
367 // Get a reference to the recipient's Receiver
368 let royaltyReceiver = getAccount(MetaverseMarket.account.address)
369 .getCapability(/public/flowTokenReceiver)
370 .borrow<&{FungibleToken.Receiver}>()
371 ?? panic("Could not borrow receiver reference to the recipient's Vault")
372
373 let royalty <- payment.withdraw(amount: payment.balance * 0.1)
374
375
376 royaltyReceiver.deposit(from: <- royalty)
377 receiverRef.deposit(from: <- payment)
378
379
380
381 // deposit it in the recipient's account using their reference
382 recipient.deposit(token: <- create MetaverseMarket.NFT(
383 initID: MetaverseMarket.totalSupply,
384 uniqueListId: list.minted,
385 listId: list.listId,
386 name: list.name,
387 categoryId: list.categoryId,
388 description: list.description,
389 creator: list.creator,
390 fileName: list.fileName,
391 format: list.format,
392 fileIPFS: list.fileIPFS
393 ))
394
395 MetaverseMarket.totalSupply = MetaverseMarket.totalSupply + (1 as UInt64)
396 }
397
398 //TransferNft, mint and transfer to Account NFT
399 pub fun transferNFT(recipient: &{NonFungibleToken.CollectionPublic}, listedNftId: UInt64) {
400 pre{
401 MetaverseMarket.nftsToSell[listedNftId] != nil: "Listed ID does not exists!"
402 MetaverseMarket.nftsToSell[listedNftId]!.maxSupply != MetaverseMarket.nftsToSell[listedNftId]!.minted: "Max Supply reached!"
403 }
404
405 let list = MetaverseMarket.nftsToSell[listedNftId]!
406 MetaverseMarket.nftsToSell[listedNftId]!.addMinted()
407
408 // deposit it in the recipient's account using their reference
409 recipient.deposit(token: <- create MetaverseMarket.NFT(
410 initID: MetaverseMarket.totalSupply,
411 uniqueListId: list.minted,
412 listId: list.listId,
413 name: list.name,
414 categoryId: list.categoryId,
415 description: list.description,
416 creator: list.creator,
417 fileName: list.fileName,
418 format: list.format,
419 fileIPFS: list.fileIPFS
420 ))
421
422 MetaverseMarket.totalSupply = MetaverseMarket.totalSupply + (1 as UInt64)
423 }
424 }
425
426
427
428 // fetch
429 // Get a reference to a MetaverseMarket from an account's Collection, if available.
430 // If an account does not have a MetaverseMarket.Collection, panic.
431 // If it has a collection but does not contain the itemID, return nil.
432 // If it has a collection and that collection contains the itemID, return a reference to that.
433 //
434 pub fun fetch(_ from: Address, itemID: UInt64): &MetaverseMarket.NFT? {
435 let collection = getAccount(from)
436 .getCapability(MetaverseMarket.CollectionPublicPath)
437 .borrow<&MetaverseMarket.Collection{MetaverseMarket.MetaverseMarketCollectionPublic}>()
438 ?? panic("Couldn't get collection")
439 // We trust MetaverseMarket.Collection.borowMetaverseMarket to get the correct itemID
440 // (it checks it before returning it).
441 return collection.borrowMetaverseMarket(id: itemID)
442 }
443
444 pub fun getAllNftsFromAccount(_ from: Address): &{UInt64: NonFungibleToken.NFT}? {
445 let collection = getAccount(from)
446 .getCapability(MetaverseMarket.CollectionPublicPath)
447 .borrow<&MetaverseMarket.Collection{MetaverseMarket.MetaverseMarketCollectionPublic}>()
448 ?? panic("Couldn't get collection")
449 return collection.getNFTs()
450 }
451
452 pub fun getCategories(): {UInt64:String} {
453 return MetaverseMarket.categoriesList
454 }
455
456 pub fun getCategoriesIds(): [UInt64] {
457 return MetaverseMarket.categoriesList.keys
458 }
459
460 pub fun getCategorieName(id: UInt64): String {
461 return MetaverseMarket.categoriesList[id] ?? panic("Category does not exists")
462 }
463
464 pub fun getCategoriesListLength(): UInt64 {
465 return UInt64(MetaverseMarket.categoriesList.length)
466 }
467
468 pub fun getNftToSellListLength(): UInt64{
469 return UInt64(MetaverseMarket.nftsToSell.length)
470 }
471
472 pub fun getCategoriesNFTsToSell(categoryId: UInt64): [UInt64]?{
473 return MetaverseMarket.categoriesNFTsToSell[categoryId]
474 }
475
476 pub fun getNftToSellData(listId: UInt64): OzoneListToSellMetadata? {
477 return MetaverseMarket.nftsToSell[listId]
478 }
479
480 pub fun getAllListToSell(): [UInt64]{
481 return MetaverseMarket.nftsToSell.keys
482 }
483
484 // initializer
485 //
486 init() {
487 // Set our named paths
488 self.CollectionStoragePath = /storage/NftMetaverseMarketCollectionVersionOne
489 self.CollectionPublicPath = /public/NftMetaverseMarketCollectionVersionOne
490 self.AdminStoragePath = /storage/metaverseMarketAdmin
491
492 self.categoriesList = {}
493
494 self.categoriesNFTsToSell = {}
495
496 self.nftsToSell = {}
497
498 // Initialize the total supply
499 self.totalSupply = 0
500
501 // Create a Admin resource and save it to storage
502 let admin <- create Admin()
503 self.account.save(<-admin, to: self.AdminStoragePath)
504
505 // Create and link collection to this account
506 self.account.save(<- self.createEmptyCollection(), to: self.CollectionStoragePath)
507 self.account.link<&MetaverseMarket.Collection{NonFungibleToken.CollectionPublic, MetaverseMarket.MetaverseMarketCollectionPublic}>(self.CollectionPublicPath, target: self.CollectionStoragePath)
508
509 emit ContractInitialized()
510 }
511}
512