Smart Contract
Metaverse
A.256599e1b091be12.Metaverse
1//SPDX-License-Identifier : CC-BY-NC-4.0
2
3import NonFungibleToken from 0x1d7e57aa55817448
4import MetadataViews from 0x1d7e57aa55817448
5import FungibleToken from 0xf233dcee88fe0abe
6import ViewResolver from 0x1d7e57aa55817448
7
8// Metaverse
9// NFT for Metaverse
10//
11access(all) contract Metaverse: NonFungibleToken {
12
13 // Events
14 //
15 access(all) event ContractInitialized()
16 access(all) event Withdraw(id: UInt64, from: Address?)
17 access(all) event Deposit(id: UInt64, to: Address?)
18 access(all) event Minted(id: UInt64, typeID: UInt64)
19 access(all) event BatchMinted(ids: [UInt64], typeID: [UInt64])
20 access(all) event NFTBurned(id: UInt64)
21 access(all) event NFTsBurned(ids: [UInt64])
22
23 // Named Paths
24 //
25 access(all) let CollectionStoragePath: StoragePath
26 access(all) let CollectionPublicPath: PublicPath
27 access(all) let MinterStoragePath: StoragePath
28
29 // totalSupply
30 // The total number of Metaverses that have been minted
31 //
32 access(all) var totalSupply: UInt64
33
34 /// Function that resolves a metadata view for this contract.
35 ///
36 /// @param view: The Type of the desired view.
37 /// @return A structure representing the requested view.
38 ///
39 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
40 switch viewType {
41 case Type<MetadataViews.NFTCollectionData>():
42 return MetadataViews.NFTCollectionData(
43 storagePath: Metaverse.CollectionStoragePath,
44 publicPath: Metaverse.CollectionPublicPath,
45 publicCollection: Type<&Metaverse.Collection>(),
46 publicLinkedType: Type<&Metaverse.Collection>(),
47 createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
48 return <-Metaverse.createEmptyCollection(nftType: Type<@Metaverse.NFT>())
49 })
50 )
51 case Type<MetadataViews.NFTCollectionDisplay>():
52 let squareMedia = MetadataViews.Media(
53 file: MetadataViews.HTTPFile(
54 url: "https://d19wottuqbmkwr.cloudfront.net/nft/banners1.jpg"
55 ),
56 mediaType: "image"
57 )
58 let bannerMedia = MetadataViews.Media(
59 file: MetadataViews.HTTPFile(
60 url: "https://d19wottuqbmkwr.cloudfront.net/nft/banners2.jpg"
61 ),
62 mediaType: "image"
63 )
64 return MetadataViews.NFTCollectionDisplay(
65 name: "Ozone Metaverse",
66 description: "Ozone is the enterprise grade platform for virtual worlds building. Simple to use - Powerful - 100% browser based.",
67 externalURL: MetadataViews.ExternalURL("https://ozonemetaverse.io"),
68 squareImage: squareMedia,
69 bannerImage: bannerMedia,
70 socials: {
71 "twitter": MetadataViews.ExternalURL("https://twitter.com/ozonemetaverse"),
72 "discord": MetadataViews.ExternalURL("https://discord.gg/ozonemetaverse")
73 }
74 )
75 }
76 return nil
77 }
78
79 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
80 ///
81 /// @return An array of Types defining the implemented views. This value will be used by
82 /// developers to know which parameter to pass to the resolveView() method.
83 ///
84 access(all) view fun getContractViews(resourceType: Type?): [Type] {
85 return [
86 Type<MetadataViews.NFTCollectionData>(),
87 Type<MetadataViews.NFTCollectionDisplay>(),
88 Type<MetadataViews.ExternalURL>()
89 ]
90 }
91
92 // NFT
93 // Metaverse as an NFT
94 //
95 access(all) resource NFT: NonFungibleToken.NFT {
96 // The token's ID
97 access(all) let id: UInt64
98 // The token's type, e.g. 3 == Hat
99 access(all) let typeID: UInt64
100 // Token's metadata as a string dictionary
101 access(self) let metadata: {String: String}
102
103 // initializer
104 //
105 init(initID: UInt64, initTypeID: UInt64, initMetadata: {String: String}) {
106 self.id = initID
107 self.typeID = initTypeID
108 self.metadata = initMetadata
109 }
110
111 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
112 return <- Metaverse.createEmptyCollection(nftType: Type<@Metaverse.NFT>())
113 }
114
115 access(all) view fun getViews(): [Type] {
116 return [
117 Type<MetadataViews.Display>(),
118 Type<MetadataViews.ExternalURL>(),
119 Type<MetadataViews.Royalties>(),
120 Type<MetadataViews.NFTCollectionData>(),
121 Type<MetadataViews.NFTCollectionDisplay>(),
122 Type<MetadataViews.Traits>()
123 ]
124 }
125
126 access(all) fun resolveView(_ view: Type): AnyStruct? {
127 switch view {
128 case Type<MetadataViews.Display>():
129 let name = (self.metadata["name"] as! String)
130 let description = (self.metadata["description"] as! String)
131 let url: String = (self.metadata["imageUrl"] as! String)
132
133 return MetadataViews.Display(
134 name: name,
135 description: description,
136 thumbnail: MetadataViews.HTTPFile(
137 url: url
138 )
139 )
140 case Type<MetadataViews.ExternalURL>():
141 return MetadataViews.ExternalURL("https://ozonemetaverse.io/")
142 case Type<MetadataViews.Royalties>():
143 let royalties : [MetadataViews.Royalty] = []
144 royalties.append(MetadataViews.Royalty(receiver: getAccount(Metaverse.account.address).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver), cut: 0.1, description: "Ozone Metaverse Secondary Sale Royalty"))
145 return MetadataViews.Royalties(royalties)
146 case Type<MetadataViews.NFTCollectionData>():
147 return MetadataViews.NFTCollectionData(
148 storagePath: Metaverse.CollectionStoragePath,
149 publicPath: Metaverse.CollectionPublicPath,
150 publicCollection: Type<&Metaverse.Collection>(),
151 publicLinkedType: Type<&Metaverse.Collection>(),
152 createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
153 return <-Metaverse.createEmptyCollection(nftType: Type<@Metaverse.NFT>())
154 })
155 )
156 case Type<MetadataViews.NFTCollectionDisplay>():
157 let squareMedia = MetadataViews.Media(
158 file: MetadataViews.HTTPFile(
159 url: "https://d19wottuqbmkwr.cloudfront.net/nft/banners1.jpg"
160 ),
161 mediaType: "image"
162 )
163 let bannerMedia = MetadataViews.Media(
164 file: MetadataViews.HTTPFile(
165 url: "https://d19wottuqbmkwr.cloudfront.net/nft/banners2.jpg"
166 ),
167 mediaType: "image"
168 )
169 return MetadataViews.NFTCollectionDisplay(
170 name: "Ozone Metaverse",
171 description: "Ozone is the enterprise grade platform for virtual worlds building. Simple to use - Powerful - 100% browser based.",
172 externalURL: MetadataViews.ExternalURL("https://ozonemetaverse.io"),
173 squareImage: squareMedia,
174 bannerImage: bannerMedia,
175 socials: {
176 "twitter": MetadataViews.ExternalURL("https://twitter.com/ozonemetaverse"),
177 "discord": MetadataViews.ExternalURL("https://discord.gg/ozonemetaverse")
178 }
179 )
180 case Type<MetadataViews.Traits>():
181 let districtNameTrait = MetadataViews.Trait(
182 name: "District Name",
183 value: self.metadata["districtName"],
184 displayType: nil,
185 rarity: nil
186 )
187 let landRarityTrait = MetadataViews.Trait(
188 name: "Land Rarity",
189 value: self.metadata["landRarity"],
190 displayType: nil,
191 rarity: nil
192 )
193 return MetadataViews.Traits([
194 districtNameTrait,
195 landRarityTrait
196 ])
197 }
198
199 return nil
200 }
201
202 // get complete metadata
203 access(all) view fun getMetadata() : {String:AnyStruct} {
204 return self.metadata;
205 }
206
207 // get metadata field by key
208 access(all) view fun getMetadataField(key:String) : AnyStruct? {
209 if let value = self.metadata[key] {
210 return value
211 }
212 return nil;
213 }
214 }
215
216 // This is the interface that users can cast their Metaverse Collection as
217 // to allow others to deposit Metaverse into their Collection. It also allows for reading
218 // the details of Metaverse in the Collection.
219 access(all) resource interface MetaverseCollectionPublic: NonFungibleToken.Collection {
220 access(all) fun deposit(token: @{NonFungibleToken.NFT})
221 access(all) view fun getNFTs(): &{UInt64: {NonFungibleToken.NFT}}
222 access(all) view fun borrowMetaverse(id: UInt64): &Metaverse.NFT? {
223 // If the result isn't nil, the id of the returned reference
224 // should be the same as the argument to the function
225 post {
226 (result == nil) || (result?.id == id):
227 "Cannot borrow Metaverse reference: The ID of the returned reference is incorrect"
228 }
229 }
230 }
231
232 // Collection
233 // A collection of Metaverse NFTs owned by an account
234 //
235 access(all) resource Collection: MetaverseCollectionPublic {
236 // dictionary of NFT conforming tokens
237 // NFT is a resource type with an `UInt64` ID field
238 //
239 access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
240
241 // withdraw
242 // Removes an NFT from the collection and moves it to the caller
243 //
244 access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
245 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
246
247 emit Withdraw(id: token.id, from: self.owner?.address)
248
249 return <-token
250 }
251
252 /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
253 access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
254 let supportedTypes: {Type: Bool} = {}
255 supportedTypes[Type<@Metaverse.NFT>()] = true
256 return supportedTypes
257 }
258
259 /// Returns whether or not the given type is accepted by the collection
260 /// A collection that can accept any type should just return true by default
261 access(all) view fun isSupportedNFTType(type: Type): Bool {
262 return type == Type<@Metaverse.NFT>()
263 }
264
265 // deposit
266 // Takes a NFT and adds it to the collections dictionary
267 // and adds the ID to the id array
268 //
269 access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
270 let token <- token as! @Metaverse.NFT
271
272 let id: UInt64 = token.id
273
274 // add the new token to the dictionary which removes the old one
275 let oldToken <- self.ownedNFTs[id] <- token
276
277 emit Deposit(id: id, to: self.owner?.address)
278
279 destroy oldToken
280 }
281
282 // getIDs
283 // Returns an array of the IDs that are in the collection
284 //
285 access(all) view fun getIDs(): [UInt64] {
286 return self.ownedNFTs.keys
287 }
288
289 access(all) view fun getNFTs(): &{UInt64: {NonFungibleToken.NFT}} {
290 return (&self.ownedNFTs)
291
292 }
293
294 // borrowNFT
295 // Gets a reference to an NFT in the collection
296 // so that the caller can read its metadata and call its methods
297 //
298 access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
299 return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
300 }
301
302 // borrowMetaverse
303 // Gets a reference to an NFT in the collection as a Metaverse,
304 // exposing all of its fields (including the typeID).
305 // This is safe as there are no functions that can be called on the Metaverse.
306 //
307 access(all) view fun borrowMetaverse(id: UInt64): &Metaverse.NFT? {
308 if self.ownedNFTs[id] != nil {
309 let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
310 return ref as! &Metaverse.NFT
311 } else {
312 return nil
313 }
314 }
315
316 access(all) view fun borrowNFTSafe(id: UInt64): &NFT? {
317 post {
318 result == nil || result!.id == id: "The returned reference's ID does not match the requested ID"
319 }
320
321 return self.ownedNFTs[id] != nil
322 ? (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! as! &Metaverse.NFT
323 : nil
324 }
325
326 access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
327 let nft = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
328 let metaverseNft = nft as! &Metaverse.NFT
329 return metaverseNft
330 }
331
332 // initializer
333 //
334 init () {
335 self.ownedNFTs <- {}
336 }
337
338 /// createEmptyCollection creates an empty Collection of the same type
339 /// and returns it to the caller
340 /// @return A an empty collection of the same type
341 access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
342 return <-Metaverse.createEmptyCollection(nftType: Type<@Metaverse.NFT>())
343 }
344 }
345
346 // createEmptyCollection
347 // public function that anyone can call to create a new empty collection
348 //
349 access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
350 return <- create Collection()
351 }
352
353 // NFTMinter
354 // Resource that an admin or something similar would own to be
355 // able to mint new NFTs
356 //
357 access(all) resource NFTMinter {
358
359 // mintNFT
360 // Mints a new NFT with a new ID
361 // and deposit it in the recipients collection using their collection reference
362 //
363 access(all) fun mintNFT(recipient: &{NonFungibleToken.CollectionPublic}, typeID: UInt64, metadata: {String:String}) {
364 emit Minted(id: Metaverse.totalSupply, typeID: typeID)
365
366 // deposit it in the recipient's account using their reference
367 recipient.deposit(token: <-create Metaverse.NFT(initID: Metaverse.totalSupply, initTypeID: typeID, initMetadata: metadata))
368
369 Metaverse.totalSupply = Metaverse.totalSupply + 1
370 }
371
372 // bachtMintNFT
373 // Mints a batch of NFTs
374 // and deposit it in the recipients collection using their collection reference
375 //
376 access(all) fun batchMintNFT(recipient: &{NonFungibleToken.CollectionPublic}, typeID: [UInt64], metadata: {String:String}) {
377 let idsTab: [UInt64] = []
378 let quantity = UInt64(typeID.length)
379 var i: UInt64 = 0
380 while i < quantity {
381
382 // deposit it in the recipient's account using their reference
383 recipient.deposit(token: <-create Metaverse.NFT(initID: Metaverse.totalSupply, initTypeID: typeID[i], initMetadata: metadata))
384
385 idsTab.append(Metaverse.totalSupply)
386
387 Metaverse.totalSupply = Metaverse.totalSupply + 1
388
389 i = i + 1
390 }
391
392 emit BatchMinted(ids: idsTab, typeID: typeID)
393 }
394 }
395
396 // fetch
397 // Get a reference to a Metaverse from an account's Collection, if available.
398 // If an account does not have a Metaverse.Collection, panic.
399 // If it has a collection but does not contain the itemID, return nil.
400 // If it has a collection and that collection contains the itemID, return a reference to that.
401 //
402 access(all) view fun fetch(_ from: Address, itemID: UInt64): &Metaverse.NFT? {
403 let collection = getAccount(from)
404 .capabilities.get<&Metaverse.Collection>(Metaverse.CollectionPublicPath)
405 .borrow()
406 ?? panic("Couldn't get collection")
407 // We trust Metaverse.Collection.borowMetaverse to get the correct itemID
408 // (it checks it before returning it).
409 return collection.borrowMetaverse(id: itemID)
410 }
411
412 access(all) view fun getAllNftsFromAccount(_ from: Address): &{UInt64: {NonFungibleToken.NFT}}? {
413 let collection = getAccount(from)
414 .capabilities.get<&Metaverse.Collection>(Metaverse.CollectionPublicPath)
415 .borrow()
416 ?? panic("Couldn't get collection")
417 return collection.getNFTs()
418 }
419
420 // initializer
421 //
422 init() {
423 // Set our named paths
424 self.CollectionStoragePath = /storage/metaverseCollectionVersionTwo
425 self.CollectionPublicPath = /public/metaverseCollectionVersionTwo
426 self.MinterStoragePath = /storage/metaverseMinterVersionTwo
427
428 // Initialize the total supply
429 self.totalSupply = 0
430
431 // Create a Minter resource and save it to storage
432 let minter <- create NFTMinter()
433 self.account.storage.save(<-minter, to: self.MinterStoragePath)
434
435 // Create and link collection to this account
436 self.account.storage.save(<-self.createEmptyCollection(nftType: Type<@Metaverse.NFT>()), to: self.CollectionStoragePath)
437 let cap = self.account.capabilities.storage.issue<&Metaverse.Collection>(self.CollectionStoragePath)
438 self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
439 emit ContractInitialized()
440 }
441}
442