Smart Contract

FooBar

A.3eebe1cb4a1126b2.FooBar

Valid From

122,358,911

Deployed

5d ago
Feb 22, 2026, 09:57:29 AM UTC

Dependents

16 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 FooBar: NonFungibleToken {
18
19    /// Standard Paths
20    access(all) let CollectionStoragePath: StoragePath
21    access(all) let CollectionPublicPath: PublicPath
22
23    /// Path where the minter should be stored
24    /// The standard paths for the collection are stored in the collection resource type
25    access(all) let MinterStoragePath: StoragePath
26
27    /// We choose the name NFT here, but this type can have any name now
28    /// because the interface does not require it to have a specific name any more
29    access(all) resource NFT: NonFungibleToken.NFT {
30
31        access(all) let id: UInt64
32
33        /// From the Display metadata view
34        access(all) let name: String
35        access(all) let description: String
36        access(all) let thumbnail: String
37
38        /// For the Royalties metadata view
39        access(self) let royalties: [MetadataViews.Royalty]
40
41        /// Generic dictionary of traits the NFT has
42        access(self) let metadata: {String: AnyStruct}
43
44        init(
45            name: String,
46            description: String,
47            thumbnail: String,
48            royalties: [MetadataViews.Royalty],
49            metadata: {String: AnyStruct},
50        ) {
51            self.id = self.uuid
52            self.name = name
53            self.description = description
54            self.thumbnail = thumbnail
55            self.royalties = royalties
56            self.metadata = metadata
57        }
58
59        /// createEmptyCollection creates an empty Collection
60        /// and returns it to the caller so that they can own NFTs
61        /// @{NonFungibleToken.Collection}
62        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
63            return <-FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>())
64        }
65
66        access(all) view 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                Type<MetadataViews.EVMBridgedMetadata>()
77            ]
78        }
79
80        access(all) fun resolveView(_ view: Type): AnyStruct? {
81            switch view {
82                case Type<MetadataViews.Display>():
83                    return MetadataViews.Display(
84                        name: self.name,
85                        description: self.description,
86                        thumbnail: MetadataViews.HTTPFile(
87                            url: self.thumbnail
88                        )
89                    )
90                case Type<MetadataViews.Editions>():
91                    // There is no max number of NFTs that can be minted from this contract
92                    // so the max edition field value is set to nil
93                    let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil)
94                    let editionList: [MetadataViews.Edition] = [editionInfo]
95                    return MetadataViews.Editions(
96                        editionList
97                    )
98                case Type<MetadataViews.Serial>():
99                    return MetadataViews.Serial(
100                        self.id
101                    )
102                case Type<MetadataViews.Royalties>():
103                    return MetadataViews.Royalties(
104                        self.royalties
105                    )
106                case Type<MetadataViews.ExternalURL>():
107                    return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
108                case Type<MetadataViews.NFTCollectionData>():
109                    return FooBar.resolveContractView(resourceType: Type<@FooBar.NFT>(), viewType: Type<MetadataViews.NFTCollectionData>())
110                case Type<MetadataViews.NFTCollectionDisplay>():
111                    return FooBar.resolveContractView(resourceType: Type<@FooBar.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>())
112                case Type<MetadataViews.Traits>():
113                    // exclude mintedTime and foo to show other uses of Traits
114                    let excludedTraits = ["mintedTime", "foo"]
115                    let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits)
116
117                    // mintedTime is a unix timestamp, we should mark it with a displayType so platforms know how to show it.
118                    let mintedTimeTrait = MetadataViews.Trait(name: "mintedTime", value: self.metadata["mintedTime"]!, displayType: "Date", rarity: nil)
119                    traitsView.addTrait(mintedTimeTrait)
120
121                    // foo is a trait with its own rarity
122                    let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common")
123                    let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity)
124                    traitsView.addTrait(fooTrait)
125
126                    return traitsView
127                case Type<MetadataViews.EVMBridgedMetadata>():
128                    // Implementing this view gives the project control over how the bridged NFT is represented as an
129                    // ERC721 when bridged to EVM on Flow via the public infrastructure bridge.
130
131                    // Get the contract-level name and symbol values
132                    let contractLevel = FooBar.resolveContractView(
133                            resourceType: nil,
134                            viewType: Type<MetadataViews.EVMBridgedMetadata>()
135                        ) as! MetadataViews.EVMBridgedMetadata?
136                        ?? panic("Could not resolve contract-level EVMBridgedMetadata")
137                    // Compose the token-level URI based on a base URI and the token ID, pointing to a JSON file. This
138                    // would be a file you've uploaded and are hosting somewhere - in this case HTTP, but this could be
139                    // IPFS, S3, a data URL containing the JSON directly, etc.
140                    let baseURI = "https://example-nft.onflow.org/token-metadata/"
141                    let uriValue = self.id.toString().concat(".json")
142
143                    return MetadataViews.EVMBridgedMetadata(
144                        name: contractLevel.name,
145                        symbol: contractLevel.symbol,
146                        uri: MetadataViews.URI(
147                            baseURI: baseURI, // defining baseURI results in a concatenation of baseURI and value
148                            value: self.id.toString().concat(".json")
149                        )
150                    )
151
152            }
153            return nil
154        }
155    }
156
157    // Deprecated: Only here for backward compatibility.
158    access(all) resource interface FooBarCollectionPublic {}
159
160    access(all) resource Collection: NonFungibleToken.Collection, FooBarCollectionPublic {
161        /// dictionary of NFT conforming tokens
162        /// NFT is a resource type with an `UInt64` ID field
163        access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
164
165        init () {
166            self.ownedNFTs <- {}
167        }
168
169        /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
170        access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
171            let supportedTypes: {Type: Bool} = {}
172            supportedTypes[Type<@FooBar.NFT>()] = true
173            return supportedTypes
174        }
175
176        /// Returns whether or not the given type is accepted by the collection
177        /// A collection that can accept any type should just return true by default
178        access(all) view fun isSupportedNFTType(type: Type): Bool {
179            return type == Type<@FooBar.NFT>()
180        }
181
182        /// withdraw removes an NFT from the collection and moves it to the caller
183        access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
184            let token <- self.ownedNFTs.remove(key: withdrawID)
185                ?? panic("Could not withdraw an NFT with the provided ID from the collection")
186
187            return <-token
188        }
189
190        /// deposit takes a NFT and adds it to the collections dictionary
191        /// and adds the ID to the id array
192        access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
193            let token <- token as! @FooBar.NFT
194            let id = token.id
195
196            // add the new token to the dictionary which removes the old one
197            let oldToken <- self.ownedNFTs[token.id] <- token
198
199            destroy oldToken
200
201            // This code is for testing purposes only
202            // Do not add to your contract unless you have a specific
203            // reason to want to emit the NFTUpdated event somewhere
204            // in your contract
205            let authTokenRef = (&self.ownedNFTs[id] as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT}?)!
206            //authTokenRef.updateTransferDate(date: getCurrentBlock().timestamp)
207            FooBar.emitNFTUpdated(authTokenRef)
208        }
209
210        /// getIDs returns an array of the IDs that are in the collection
211        access(all) view fun getIDs(): [UInt64] {
212            return self.ownedNFTs.keys
213        }
214
215        /// Gets the amount of NFTs stored in the collection
216        access(all) view fun getLength(): Int {
217            return self.ownedNFTs.length
218        }
219
220        access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
221            return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
222        }
223
224        /// Borrow the view resolver for the specified NFT ID
225        access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
226            if let nft = &self.ownedNFTs[id] as &{NonFungibleToken.NFT}? {
227                return nft as &{ViewResolver.Resolver}
228            }
229            return nil
230        }
231
232        /// createEmptyCollection creates an empty Collection of the same type
233        /// and returns it to the caller
234        /// @return A an empty collection of the same type
235        access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
236            return <-FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>())
237        }
238    }
239
240    /// createEmptyCollection creates an empty Collection for the specified NFT type
241    /// and returns it to the caller so that they can own NFTs
242    access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
243        return <- create Collection()
244    }
245
246    /// Function that returns all the Metadata Views implemented by a Non Fungible Token
247    ///
248    /// @return An array of Types defining the implemented views. This value will be used by
249    ///         developers to know which parameter to pass to the resolveView() method.
250    ///
251    access(all) view fun getContractViews(resourceType: Type?): [Type] {
252        return [
253            Type<MetadataViews.NFTCollectionData>(),
254            Type<MetadataViews.NFTCollectionDisplay>(),
255            Type<MetadataViews.EVMBridgedMetadata>()
256        ]
257    }
258
259    /// Function that resolves a metadata view for this contract.
260    ///
261    /// @param view: The Type of the desired view.
262    /// @return A structure representing the requested view.
263    ///
264    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
265        switch viewType {
266            case Type<MetadataViews.NFTCollectionData>():
267                let collectionData = MetadataViews.NFTCollectionData(
268                    storagePath: self.CollectionStoragePath,
269                    publicPath: self.CollectionPublicPath,
270                    publicCollection: Type<&FooBar.Collection>(),
271                    publicLinkedType: Type<&FooBar.Collection>(),
272                    createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} {
273                        return <-FooBar.createEmptyCollection(nftType: Type<@FooBar.NFT>())
274                    })
275                )
276                return collectionData
277            case Type<MetadataViews.NFTCollectionDisplay>():
278                let media = MetadataViews.Media(
279                    file: MetadataViews.HTTPFile(
280                        url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
281                    ),
282                    mediaType: "image/svg+xml"
283                )
284                return MetadataViews.NFTCollectionDisplay(
285                    name: "The Example Collection",
286                    description: "This collection is used as an example to help you develop your next Flow NFT.",
287                    externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
288                    squareImage: media,
289                    bannerImage: media,
290                    socials: {
291                        "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
292                    }
293                )
294            case Type<MetadataViews.EVMBridgedMetadata>():
295                // Implementing this view gives the project control over how the bridged NFT is represented as an ERC721
296                // when bridged to EVM on Flow via the public infrastructure bridge.
297
298                // Compose the contract-level URI. In this case, the contract metadata is located on some HTTP host,
299                // but it could be IPFS, S3, a data URL containing the JSON directly, etc.
300                return MetadataViews.EVMBridgedMetadata(
301                    name: "FooBar",
302                    symbol: "XMPL",
303                    uri: MetadataViews.URI(
304                        baseURI: nil, // setting baseURI as nil sets the given value as the uri field value
305                        value: "https://example-nft.onflow.org/contract-metadata.json"
306                    )
307                )
308        }
309        return nil
310    }
311
312    /// Resource that an admin or something similar would own to be
313    /// able to mint new NFTs
314    ///
315    access(all) resource NFTMinter {
316
317        /// mintNFT mints a new NFT with a new ID
318        /// and returns it to the calling context
319        access(all) fun mintNFT(
320            name: String,
321            description: String,
322            thumbnail: String,
323            royalties: [MetadataViews.Royalty]
324        ): @FooBar.NFT {
325
326            let metadata: {String: AnyStruct} = {}
327            let currentBlock = getCurrentBlock()
328            metadata["mintedBlock"] = currentBlock.height
329            metadata["mintedTime"] = currentBlock.timestamp
330
331            // this piece of metadata will be used to show embedding rarity into a trait
332            metadata["foo"] = "bar"
333
334            // create a new NFT
335            var newNFT <- create NFT(
336                name: name,
337                description: description,
338                thumbnail: thumbnail,
339                royalties: royalties,
340                metadata: metadata,
341            )
342
343            return <-newNFT
344        }
345    }
346
347    init() {
348
349        // Set the named paths
350        self.CollectionStoragePath = /storage/FooBarCollection
351        self.CollectionPublicPath = /public/FooBarCollection
352        self.MinterStoragePath = /storage/FooBarMinter
353
354        // Create a Collection resource and save it to storage
355        let collection <- create Collection()
356        self.account.storage.save(<-collection, to: self.CollectionStoragePath)
357
358        // create a public capability for the collection
359        let collectionCap = self.account.capabilities.storage.issue<&FooBar.Collection>(self.CollectionStoragePath)
360        self.account.capabilities.publish(collectionCap, at: self.CollectionPublicPath)
361
362        // Create a Minter resource and save it to storage
363        let minter <- create NFTMinter()
364        self.account.storage.save(<-minter, to: self.MinterStoragePath)
365    }
366}