Smart Contract
ExampleNFT
A.5c7bf74b92c567f7.ExampleNFT
1/*
2*
3* This contract has been modified from the default implementation to adjust for our needs for testing
4
5*/
6import NonFungibleToken from 0x1d7e57aa55817448
7import MetadataViews from 0x1d7e57aa55817448
8import ViewResolver from 0x1d7e57aa55817448
9
10pub contract ExampleNFT: NonFungibleToken, ViewResolver {
11
12 /// Total supply of ExampleNFTs in existence
13 pub var totalSupply: UInt64
14
15 /// The event that is emitted when the contract is created
16 pub event ContractInitialized()
17
18 /// The event that is emitted when an NFT is withdrawn from a Collection
19 pub event Withdraw(id: UInt64, from: Address?)
20
21 /// The event that is emitted when an NFT is deposited to a Collection
22 pub event Deposit(id: UInt64, to: Address?)
23
24 /// Storage and Public Paths
25 pub let CollectionStoragePath: StoragePath
26 pub let CollectionPublicPath: PublicPath
27 pub let MinterStoragePath: StoragePath
28
29 /// The core resource that represents a Non Fungible Token.
30 /// New instances will be created using the NFTMinter resource
31 /// and stored in the Collection resource
32 ///
33 pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
34
35 /// The unique ID that each NFT has
36 pub let id: UInt64
37
38 /// Metadata fields
39 pub let name: String
40 pub let description: String
41 pub let thumbnail: String
42 access(self) let royalties: [MetadataViews.Royalty]
43 access(self) let metadata: {String: AnyStruct}
44
45 init(
46 id: UInt64,
47 name: String,
48 description: String,
49 thumbnail: String,
50 royalties: [MetadataViews.Royalty],
51 metadata: {String: AnyStruct},
52 ) {
53 self.id = id
54 self.name = name
55 self.description = description
56 self.thumbnail = thumbnail
57 self.royalties = royalties
58 self.metadata = metadata
59 }
60
61 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
62 ///
63 /// @return An array of Types defining the implemented views. This value will be used by
64 /// developers to know which parameter to pass to the resolveView() method.
65 ///
66 pub fun getViews(): [Type] {
67 return [
68 Type<MetadataViews.Display>(),
69 Type<MetadataViews.Royalties>(),
70 Type<MetadataViews.Editions>(),
71 Type<MetadataViews.ExternalURL>(),
72 Type<MetadataViews.NFTCollectionData>(),
73 Type<MetadataViews.NFTCollectionDisplay>(),
74 Type<MetadataViews.Serial>(),
75 Type<MetadataViews.Traits>()
76 ]
77 }
78
79 /// Function that resolves a metadata view for this token.
80 ///
81 /// @param view: The Type of the desired view.
82 /// @return A structure representing the requested view.
83 ///
84 pub fun resolveView(_ view: Type): AnyStruct? {
85 switch view {
86 case Type<MetadataViews.Display>():
87 return MetadataViews.Display(
88 name: self.name,
89 description: self.description,
90 thumbnail: MetadataViews.HTTPFile(
91 url: self.thumbnail
92 )
93 )
94 case Type<MetadataViews.Editions>():
95 // There is no max number of NFTs that can be minted from this contract
96 // so the max edition field value is set to nil
97 let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil)
98 let editionList: [MetadataViews.Edition] = [editionInfo]
99 return MetadataViews.Editions(
100 editionList
101 )
102 case Type<MetadataViews.Serial>():
103 return MetadataViews.Serial(
104 self.id
105 )
106 case Type<MetadataViews.Royalties>():
107 return MetadataViews.Royalties(
108 self.royalties
109 )
110 case Type<MetadataViews.ExternalURL>():
111 return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
112 case Type<MetadataViews.NFTCollectionData>():
113 return MetadataViews.NFTCollectionData(
114 storagePath: ExampleNFT.CollectionStoragePath,
115 publicPath: ExampleNFT.CollectionPublicPath,
116 providerPath: /private/exampleNFTCollection,
117 publicCollection: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic}>(),
118 publicLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
119 providerLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
120 createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
121 return <-ExampleNFT.createEmptyCollection()
122 })
123 )
124 case Type<MetadataViews.NFTCollectionDisplay>():
125 let media = MetadataViews.Media(
126 file: MetadataViews.HTTPFile(
127 url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
128 ),
129 mediaType: "image/svg+xml"
130 )
131 return MetadataViews.NFTCollectionDisplay(
132 name: "The Example Collection",
133 description: "This collection is used as an example to help you develop your next Flow NFT.",
134 externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
135 squareImage: media,
136 bannerImage: media,
137 socials: {
138 "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
139 }
140 )
141 case Type<MetadataViews.Traits>():
142 // exclude mintedTime and foo to show other uses of Traits
143 let excludedTraits = ["mintedTime", "foo"]
144 let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits)
145
146 // mintedTime is a unix timestamp, we should mark it with a displayType so platforms know how to show it.
147 let mintedTimeTrait = MetadataViews.Trait(name: "mintedTime", value: self.metadata["mintedTime"]!, displayType: "Date", rarity: nil)
148 traitsView.addTrait(mintedTimeTrait)
149
150 // foo is a trait with its own rarity
151 let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common")
152 let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity)
153 traitsView.addTrait(fooTrait)
154
155 return traitsView
156 }
157 return nil
158 }
159 }
160
161 /// Defines the methods that are particular to this NFT contract collection
162 ///
163 pub resource interface ExampleNFTCollectionPublic {
164 pub fun deposit(token: @NonFungibleToken.NFT)
165 pub fun getIDs(): [UInt64]
166 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
167 pub fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
168 post {
169 (result == nil) || (result?.id == id):
170 "Cannot borrow ExampleNFT reference: the ID of the returned reference is incorrect"
171 }
172 }
173 }
174
175 /// The resource that will be holding the NFTs inside any account.
176 /// In order to be able to manage NFTs any account will need to create
177 /// an empty collection first
178 ///
179 pub resource Collection: ExampleNFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
180 // dictionary of NFT conforming tokens
181 // NFT is a resource type with an `UInt64` ID field
182 pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
183
184 init () {
185 self.ownedNFTs <- {}
186 }
187
188 /// Removes an NFT from the collection and moves it to the caller
189 ///
190 /// @param withdrawID: The ID of the NFT that wants to be withdrawn
191 /// @return The NFT resource that has been taken out of the collection
192 ///
193 pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
194 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
195
196 emit Withdraw(id: token.id, from: self.owner?.address)
197
198 return <-token
199 }
200
201 /// Adds an NFT to the collections dictionary and adds the ID to the id array
202 ///
203 /// @param token: The NFT resource to be included in the collection
204 ///
205 pub fun deposit(token: @NonFungibleToken.NFT) {
206 let token <- token as! @ExampleNFT.NFT
207
208 let id: UInt64 = token.id
209
210 // add the new token to the dictionary which removes the old one
211 let oldToken <- self.ownedNFTs[id] <- token
212
213 emit Deposit(id: id, to: self.owner?.address)
214
215 destroy oldToken
216 }
217
218 /// Helper method for getting the collection IDs
219 ///
220 /// @return An array containing the IDs of the NFTs in the collection
221 ///
222 pub fun getIDs(): [UInt64] {
223 return self.ownedNFTs.keys
224 }
225
226 /// Gets a reference to an NFT in the collection so that
227 /// the caller can read its metadata and call its methods
228 ///
229 /// @param id: The ID of the wanted NFT
230 /// @return A reference to the wanted NFT resource
231 ///
232 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
233 return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
234 }
235
236 /// Gets a reference to an NFT in the collection so that
237 /// the caller can read its metadata and call its methods
238 ///
239 /// @param id: The ID of the wanted NFT
240 /// @return A reference to the wanted NFT resource
241 ///
242 pub fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
243 if self.ownedNFTs[id] != nil {
244 // Create an authorized reference to allow downcasting
245 let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
246 return ref as! &ExampleNFT.NFT
247 }
248
249 return nil
250 }
251
252 /// Gets a reference to the NFT only conforming to the `{MetadataViews.Resolver}`
253 /// interface so that the caller can retrieve the views that the NFT
254 /// is implementing and resolve them
255 ///
256 /// @param id: The ID of the wanted NFT
257 /// @return The resource reference conforming to the Resolver interface
258 ///
259 pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
260 let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
261 let exampleNFT = nft as! &ExampleNFT.NFT
262 return exampleNFT as &AnyResource{MetadataViews.Resolver}
263 }
264
265 destroy() {
266 destroy self.ownedNFTs
267 }
268 }
269
270 /// Allows anyone to create a new empty collection
271 ///
272 /// @return The new Collection resource
273 ///
274 pub fun createEmptyCollection(): @NonFungibleToken.Collection {
275 return <- create Collection()
276 }
277
278 /// Resource that an admin or something similar would own to be
279 /// able to mint new NFTs
280 ///
281 pub resource NFTMinter {
282
283 /// Mints a new NFT with a new ID and deposit it in the
284 /// recipients collection using their collection reference
285 ///
286 /// @param recipient: A capability to the collection where the new NFT will be deposited
287 /// @param name: The name for the NFT metadata
288 /// @param description: The description for the NFT metadata
289 /// @param thumbnail: The thumbnail for the NFT metadata
290 /// @param royalties: An array of Royalty structs, see MetadataViews docs
291 ///
292 pub fun mintNFT(
293 recipient: &{NonFungibleToken.CollectionPublic},
294 name: String,
295 description: String,
296 thumbnail: String,
297 royalties: [MetadataViews.Royalty]
298 ) {
299 let metadata: {String: AnyStruct} = {}
300 let currentBlock = getCurrentBlock()
301 metadata["mintedBlock"] = currentBlock.height
302 metadata["mintedTime"] = currentBlock.timestamp
303 metadata["minter"] = recipient.owner!.address
304
305 // this piece of metadata will be used to show embedding rarity into a trait
306 metadata["foo"] = "bar"
307
308 // create a new NFT
309 var newNFT <- create NFT(
310 id: ExampleNFT.totalSupply,
311 name: name,
312 description: description,
313 thumbnail: thumbnail,
314 royalties: royalties,
315 metadata: metadata,
316 )
317
318 // deposit it in the recipient's account using their reference
319 recipient.deposit(token: <-newNFT)
320
321 ExampleNFT.totalSupply = ExampleNFT.totalSupply + UInt64(1)
322 }
323 }
324
325 /// Function that resolves a metadata view for this contract.
326 ///
327 /// @param view: The Type of the desired view.
328 /// @return A structure representing the requested view.
329 ///
330 pub fun resolveView(_ view: Type): AnyStruct? {
331 switch view {
332 case Type<MetadataViews.NFTCollectionData>():
333 return MetadataViews.NFTCollectionData(
334 storagePath: ExampleNFT.CollectionStoragePath,
335 publicPath: ExampleNFT.CollectionPublicPath,
336 providerPath: /private/exampleNFTCollection,
337 publicCollection: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic}>(),
338 publicLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
339 providerLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
340 createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
341 return <-ExampleNFT.createEmptyCollection()
342 })
343 )
344 case Type<MetadataViews.NFTCollectionDisplay>():
345 let media = MetadataViews.Media(
346 file: MetadataViews.HTTPFile(
347 url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
348 ),
349 mediaType: "image/svg+xml"
350 )
351 return MetadataViews.NFTCollectionDisplay(
352 name: "The Example Collection",
353 description: "This collection is used as an example to help you develop your next Flow NFT.",
354 externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
355 squareImage: media,
356 bannerImage: media,
357 socials: {
358 "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
359 }
360 )
361 }
362 return nil
363 }
364
365 /// Function that returns all the Metadata Views implemented by a Non Fungible Token
366 ///
367 /// @return An array of Types defining the implemented views. This value will be used by
368 /// developers to know which parameter to pass to the resolveView() method.
369 ///
370 pub fun getViews(): [Type] {
371 return [
372 Type<MetadataViews.NFTCollectionData>(),
373 Type<MetadataViews.NFTCollectionDisplay>()
374 ]
375 }
376
377 init() {
378 // Initialize the total supply
379 self.totalSupply = 0
380
381 // Set the named paths
382 self.CollectionStoragePath = /storage/exampleNFTCollection
383 self.CollectionPublicPath = /public/exampleNFTCollection
384 self.MinterStoragePath = /storage/exampleNFTMinter
385
386 // Create a Collection resource and save it to storage
387 let collection <- create Collection()
388 self.account.save(<-collection, to: self.CollectionStoragePath)
389
390 // create a public capability for the collection
391 self.account.link<&ExampleNFT.Collection{NonFungibleToken.CollectionPublic, ExampleNFT.ExampleNFTCollectionPublic, MetadataViews.ResolverCollection}>(
392 self.CollectionPublicPath,
393 target: self.CollectionStoragePath
394 )
395
396 // Create a Minter resource and save it to storage
397 let minter <- create NFTMinter()
398 self.account.save(<-minter, to: self.MinterStoragePath)
399
400 emit ContractInitialized()
401 }
402}
403