Smart Contract

ExampleNFT

A.5c7bf74b92c567f7.ExampleNFT

Valid From

85,078,326

Deployed

2d ago
Feb 26, 2026, 05:16:18 AM UTC

Dependents

0 imports
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