Smart Contract

ExampleNFT

A.93c18b0282a6b82c.ExampleNFT

Valid From

85,991,785

Deployed

3d ago
Feb 24, 2026, 11:58:34 PM UTC

Dependents

1 imports
1/* 
2*
3*  This is an example implementation of a Flow Non-Fungible Token
4*  using the V2 standard.
5*  It is not part of the official standard but it assumed to be
6*  similar to how many NFTs would implement the core functionality.
7*
8*  This contract does not implement any sophisticated classification
9*  system for its NFTs. It defines a simple NFT with minimal metadata.
10*   
11*/
12
13import NonFungibleToken from 0x1d7e57aa55817448
14import ViewResolver from 0x1d7e57aa55817448
15import MetadataViews from 0x1d7e57aa55817448
16
17access(all) contract ExampleNFT: NonFungibleToken {
18
19    /// Path where the minter should be stored
20    /// The standard paths for the collection are stored in the collection resource type
21    access(all) let MinterStoragePath: StoragePath
22
23    /// We choose the name NFT here, but this type can have any name now
24    /// because the interface does not require it to have a specific name any more
25    access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
26
27        access(all) let id: UInt64
28
29        /// From the Display metadata view
30        access(all) let name: String
31        access(all) let description: String
32        access(all) let thumbnail: String
33
34        /// For the Royalties metadata view
35        access(self) let royalties: [MetadataViews.Royalty]
36
37        /// Generic dictionary of traits the NFT has
38        access(self) let metadata: {String: AnyStruct}
39    
40        init(
41            name: String,
42            description: String,
43            thumbnail: String,
44            royalties: [MetadataViews.Royalty],
45            metadata: {String: AnyStruct},
46        ) {
47            self.id = self.uuid
48            self.name = name
49            self.description = description
50            self.thumbnail = thumbnail
51            self.royalties = royalties
52            self.metadata = metadata
53        }
54
55        /// createEmptyCollection creates an empty Collection
56        /// and returns it to the caller so that they can own NFTs
57        /// @{NonFungibleToken.Collection}
58        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
59            return <-ExampleNFT.createEmptyCollection(nftType: Type<@ExampleNFT.NFT>())
60        }
61    
62        access(all) view fun getViews(): [Type] {
63            return [
64                Type<MetadataViews.Display>(),
65                Type<MetadataViews.Royalties>(),
66                Type<MetadataViews.Editions>(),
67                Type<MetadataViews.ExternalURL>(),
68                Type<MetadataViews.NFTCollectionData>(),
69                Type<MetadataViews.NFTCollectionDisplay>(),
70                Type<MetadataViews.Serial>(),
71                Type<MetadataViews.Traits>()
72            ]
73        }
74
75        access(all) fun resolveView(_ view: Type): AnyStruct? {
76            switch view {
77                case Type<MetadataViews.Display>():
78                    return MetadataViews.Display(
79                        name: self.name,
80                        description: self.description,
81                        thumbnail: MetadataViews.HTTPFile(
82                            url: self.thumbnail
83                        )
84                    )
85                case Type<MetadataViews.Editions>():
86                    // There is no max number of NFTs that can be minted from this contract
87                    // so the max edition field value is set to nil
88                    let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil)
89                    let editionList: [MetadataViews.Edition] = [editionInfo]
90                    return MetadataViews.Editions(
91                        editionList
92                    )
93                case Type<MetadataViews.Serial>():
94                    return MetadataViews.Serial(
95                        self.id
96                    )
97                case Type<MetadataViews.Royalties>():
98                    return MetadataViews.Royalties(
99                        self.royalties
100                    )
101                case Type<MetadataViews.ExternalURL>():
102                    return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
103                case Type<MetadataViews.NFTCollectionData>():
104                    return ExampleNFT.resolveContractView(resourceType: Type<@ExampleNFT.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
105                case Type<MetadataViews.NFTCollectionDisplay>():
106                    return ExampleNFT.resolveContractView(resourceType: Type<@ExampleNFT.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
107                case Type<MetadataViews.Traits>():
108                    // exclude mintedTime and foo to show other uses of Traits
109                    let excludedTraits = ["mintedTime", "foo"]
110                    let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits)
111
112                    // foo is a trait with its own rarity
113                    let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common")
114                    let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity)
115                    traitsView.addTrait(fooTrait)
116                    
117                    return traitsView
118            }
119            return nil
120        }
121    }
122
123    access(all) resource Collection: NonFungibleToken.Collection {
124        /// dictionary of NFT conforming tokens
125        /// NFT is a resource type with an `UInt64` ID field
126        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
127
128        access(all) var storagePath: StoragePath
129        access(all) var publicPath: PublicPath
130
131        init () {
132            self.ownedNFTs <- {}
133            let identifier = "cadenceExampleNFTCollection"
134            self.storagePath = StoragePath(identifier: identifier)!
135            self.publicPath = PublicPath(identifier: identifier)!
136        }
137
138        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
139        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
140            let supportedTypes: {Type: Bool} = {}
141            supportedTypes[Type<@ExampleNFT.NFT>()] = true
142            return supportedTypes
143        }
144
145        /// Returns whether or not the given type is accepted by the collection
146        /// A collection that can accept any type should just return true by default
147        access(all) view fun isSupportedNFTType(type: Type): Bool {
148           if type == Type<@ExampleNFT.NFT>() {
149            return true
150           } else {
151            return false
152           }
153        }
154
155        /// withdraw removes an NFT from the collection and moves it to the caller
156        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
157            let token <- self.ownedNFTs.remove(key: withdrawID)
158                ?? panic("Could not withdraw an NFT with the provided ID from the collection")
159
160            return <-token
161        }
162
163        /// deposit takes a NFT and adds it to the collections dictionary
164        /// and adds the ID to the id array
165        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
166            let token <- token as! @ExampleNFT.NFT
167
168            // add the new token to the dictionary which removes the old one
169            let oldToken <- self.ownedNFTs[token.id] <- token
170
171            destroy oldToken
172        }
173
174        /// getIDs returns an array of the IDs that are in the collection
175        access(all) view fun getIDs(): [UInt64] {
176            return self.ownedNFTs.keys
177        }
178
179        /// Gets the amount of NFTs stored in the collection
180        access(all) view fun getLength(): Int {
181            return self.ownedNFTs.keys.length
182        }
183
184        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
185            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
186        }
187
188        /// Borrow the view resolver for the specified NFT ID
189        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
190            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
191                return nft as &{ViewResolver.Resolver}
192            }
193            return nil
194        }
195
196        /// createEmptyCollection creates an empty Collection of the same type
197        /// and returns it to the caller
198        /// @return A an empty collection of the same type
199        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
200            return <-ExampleNFT.createEmptyCollection(nftType: Type<@ExampleNFT.NFT>())
201        }
202    }
203
204    /// createEmptyCollection creates an empty Collection for the specified NFT type
205    /// and returns it to the caller so that they can own NFTs
206    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
207        return <- create Collection()
208    }
209
210    /// Function that returns all the Metadata Views implemented by a Non Fungible Token
211    ///
212    /// @return An array of Types defining the implemented views. This value will be used by
213    ///         developers to know which parameter to pass to the resolveView() method.
214    ///
215    access(all) view fun getContractViews(resourceType: Type?): [Type] {
216        return [
217            Type<MetadataViews.NFTCollectionData>(),
218            Type<MetadataViews.NFTCollectionDisplay>()
219        ]
220    }
221
222    /// Function that resolves a metadata view for this contract.
223    ///
224    /// @param view: The Type of the desired view.
225    /// @return A structure representing the requested view.
226    ///
227    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
228        switch viewType {
229            case Type<MetadataViews.NFTCollectionData>():
230                let collectionData = MetadataViews.NFTCollectionData(
231                    storagePath: /storage/cadenceExampleNFTCollection,
232                    publicPath: /public/cadenceExampleNFTCollection,
233                    publicCollection: Type<&ExampleNFT.Collection>(),
234                    publicLinkedType: Type<&ExampleNFT.Collection>(),
235                    createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
236                        return <-ExampleNFT.createEmptyCollection(nftType: Type<@ExampleNFT.NFT>())
237                    })
238                )
239                return collectionData
240            case Type<MetadataViews.NFTCollectionDisplay>():
241                let media = MetadataViews.Media(
242                    file: MetadataViews.HTTPFile(
243                        url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
244                    ),
245                    mediaType: "image/svg+xml"
246                )
247                return MetadataViews.NFTCollectionDisplay(
248                    name: "The Example Collection",
249                    description: "This collection is used as an example to help you develop your next Flow NFT.",
250                    externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
251                    squareImage: media,
252                    bannerImage: media,
253                    socials: {
254                        "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
255                    }
256                )
257        }
258        return nil
259    }
260
261    /// Resource that an admin or something similar would own to be
262    /// able to mint new NFTs
263    ///
264    access(all) resource NFTMinter {
265
266        /// mintNFT mints a new NFT with a new ID
267        /// and returns it to the calling context
268        access(all) fun mintNFT(
269            name: String,
270            description: String,
271            thumbnail: String,
272            royalties: [MetadataViews.Royalty]
273        ): @ExampleNFT.NFT {
274
275            let metadata: {String: AnyStruct} = {}
276            let currentBlock = getCurrentBlock()
277            metadata["mintedBlock"] = currentBlock.height
278            metadata["mintedTime"] = currentBlock.timestamp
279
280            // this piece of metadata will be used to show embedding rarity into a trait
281            metadata["foo"] = "bar"
282
283            // create a new NFT
284            var newNFT <- create NFT(
285                name: name,
286                description: description,
287                thumbnail: thumbnail,
288                royalties: royalties,
289                metadata: metadata,
290            )
291
292            return <-newNFT
293        }
294    }
295
296    init() {
297
298        // Set the named paths
299        self.MinterStoragePath = /storage/cadenceExampleNFTMinter
300
301        // Create a Collection resource and save it to storage
302        let collection <- create Collection()
303        let defaultStoragePath = collection.storagePath
304        let defaultPublicPath = collection.publicPath
305        self.account.storage.save(<-collection, to: defaultStoragePath)
306
307        // create a public capability for the collection
308        let collectionCap = self.account.capabilities.storage.issue<&ExampleNFT.Collection>(defaultStoragePath)
309        self.account.capabilities.publish(collectionCap, at: defaultPublicPath)
310
311        // Create a Minter resource and save it to storage
312        let minter <- create NFTMinter()
313        self.account.storage.save(<-minter, to: self.MinterStoragePath)
314    }
315}
316