Skip to content

Instantly share code, notes, and snippets.

@kharioki
Created June 28, 2022 14:41
Show Gist options
  • Save kharioki/1f1d282844600d0752c28555700ec2a2 to your computer and use it in GitHub Desktop.
Save kharioki/1f1d282844600d0752c28555700ec2a2 to your computer and use it in GitHub Desktop.
A sample NFT on Flow network
/*
*
* This is an example implementation of a Flow Non-Fungible Token
* It is not part of the official standard but it assumed to be
* similar to how many NFTs would implement the core functionality.
*
* This contract does not implement any sophisticated classification
* system for its NFTs. It defines a simple NFT with minimal metadata.
*
*/
import NonFungibleToken from "./NonFungibleToken.cdc"
import MetadataViews from "./MetadataViews.cdc"
pub contract ExampleNFT: NonFungibleToken {
pub var totalSupply: UInt64
pub event ContractInitialized()
pub event Withdraw(id: UInt64, from: Address?)
pub event Deposit(id: UInt64, to: Address?)
pub let CollectionStoragePath: StoragePath
pub let CollectionPublicPath: PublicPath
pub let MinterStoragePath: StoragePath
pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
pub let id: UInt64
pub let name: String
pub let description: String
pub let thumbnail: String
access(self) let royalties: [MetadataViews.Royalty]
access(self) let metadata: {String: AnyStruct}
init(
id: UInt64,
name: String,
description: String,
thumbnail: String,
royalties: [MetadataViews.Royalty],
metadata: {String: AnyStruct},
) {
self.id = id
self.name = name
self.description = description
self.thumbnail = thumbnail
self.royalties = royalties
self.metadata = metadata
}
pub fun getViews(): [Type] {
return [
Type<MetadataViews.Display>(),
Type<MetadataViews.Royalties>(),
Type<MetadataViews.Editions>(),
Type<MetadataViews.ExternalURL>(),
Type<MetadataViews.NFTCollectionData>(),
Type<MetadataViews.NFTCollectionDisplay>(),
Type<MetadataViews.Serial>(),
Type<MetadataViews.Traits>()
]
}
pub fun resolveView(_ view: Type): AnyStruct? {
switch view {
case Type<MetadataViews.Display>():
return MetadataViews.Display(
name: self.name,
description: self.description,
thumbnail: MetadataViews.HTTPFile(
url: self.thumbnail
)
)
case Type<MetadataViews.Editions>():
// There is no max number of NFTs that can be minted from this contract
// so the max edition field value is set to nil
let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil)
let editionList: [MetadataViews.Edition] = [editionInfo]
return MetadataViews.Editions(
editionList
)
case Type<MetadataViews.Serial>():
return MetadataViews.Serial(
self.id
)
case Type<MetadataViews.Royalties>():
return MetadataViews.Royalties(
self.royalties
)
case Type<MetadataViews.ExternalURL>():
return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
case Type<MetadataViews.NFTCollectionData>():
return MetadataViews.NFTCollectionData(
storagePath: ExampleNFT.CollectionStoragePath,
publicPath: ExampleNFT.CollectionPublicPath,
providerPath: /private/exampleNFTCollection,
publicCollection: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic}>(),
publicLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(),
providerLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(),
createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
return <-ExampleNFT.createEmptyCollection()
})
)
case Type<MetadataViews.NFTCollectionDisplay>():
let media = MetadataViews.Media(
file: MetadataViews.HTTPFile(
url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
),
mediaType: "image/svg+xml"
)
return MetadataViews.NFTCollectionDisplay(
name: "The Example Collection",
description: "This collection is used as an example to help you develop your next Flow NFT.",
externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
squareImage: media,
bannerImage: media,
socials: {
"twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
}
)
case Type<MetadataViews.Traits>():
// exclude mintedTime and foo to show other uses of Traits
let excludedTraits = ["mintedTime", "foo"]
let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits)
// mintedTime is a unix timestamp, we should mark it with a displayType so platforms know how to show it.
let mintedTimeTrait = MetadataViews.Trait(name: "mintedTime", value: self.metadata["mintedTime"]!, displayType: "Date", rarity: nil)
traitsView.addTrait(mintedTimeTrait)
// foo is a trait with its own rarity
let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common")
let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity)
traitsView.addTrait(fooTrait)
return traitsView
}
return nil
}
}
pub resource interface ExampleNFTCollectionPublic {
pub fun deposit(token: @NonFungibleToken.NFT)
pub fun getIDs(): [UInt64]
pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
pub fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
post {
(result == nil) || (result?.id == id):
"Cannot borrow ExampleNFT reference: the ID of the returned reference is incorrect"
}
}
}
pub resource Collection: ExampleNFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
// dictionary of NFT conforming tokens
// NFT is a resource type with an `UInt64` ID field
pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
init () {
self.ownedNFTs <- {}
}
// withdraw removes an NFT from the collection and moves it to the caller
pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
emit Withdraw(id: token.id, from: self.owner?.address)
return <-token
}
// deposit takes a NFT and adds it to the collections dictionary
// and adds the ID to the id array
pub fun deposit(token: @NonFungibleToken.NFT) {
let token <- token as! @ExampleNFT.NFT
let id: UInt64 = token.id
// add the new token to the dictionary which removes the old one
let oldToken <- self.ownedNFTs[id] <- token
emit Deposit(id: id, to: self.owner?.address)
destroy oldToken
}
// getIDs returns an array of the IDs that are in the collection
pub fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
}
// borrowNFT gets a reference to an NFT in the collection
// so that the caller can read its metadata and call its methods
pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
}
pub fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
if self.ownedNFTs[id] != nil {
// Create an authorized reference to allow downcasting
let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
return ref as! &ExampleNFT.NFT
}
return nil
}
pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
let exampleNFT = nft as! &ExampleNFT.NFT
return exampleNFT as &AnyResource{MetadataViews.Resolver}
}
destroy() {
destroy self.ownedNFTs
}
}
// public function that anyone can call to create a new empty collection
pub fun createEmptyCollection(): @NonFungibleToken.Collection {
return <- create Collection()
}
// Resource that an admin or something similar would own to be
// able to mint new NFTs
//
pub resource NFTMinter {
// mintNFT mints a new NFT with a new ID
// and deposit it in the recipients collection using their collection reference
pub fun mintNFT(
recipient: &{NonFungibleToken.CollectionPublic},
name: String,
description: String,
thumbnail: String,
royalties: [MetadataViews.Royalty]
) {
let metadata: {String: AnyStruct} = {}
let currentBlock = getCurrentBlock()
metadata["mintedBlock"] = currentBlock.height
metadata["mintedTime"] = currentBlock.timestamp
metadata["minter"] = recipient.owner!.address
// this piece of metadata will be used to show embedding rarity into a trait
metadata["foo"] = "bar"
// create a new NFT
var newNFT <- create NFT(
id: ExampleNFT.totalSupply,
name: name,
description: description,
thumbnail: thumbnail,
royalties: royalties,
metadata: metadata,
)
// deposit it in the recipient's account using their reference
recipient.deposit(token: <-newNFT)
ExampleNFT.totalSupply = ExampleNFT.totalSupply + UInt64(1)
}
}
init() {
// Initialize the total supply
self.totalSupply = 0
// Set the named paths
self.CollectionStoragePath = /storage/exampleNFTCollection
self.CollectionPublicPath = /public/exampleNFTCollection
self.MinterStoragePath = /storage/exampleNFTMinter
// Create a Collection resource and save it to storage
let collection <- create Collection()
self.account.save(<-collection, to: self.CollectionStoragePath)
// create a public capability for the collection
self.account.link<&ExampleNFT.Collection{NonFungibleToken.CollectionPublic, ExampleNFT.ExampleNFTCollectionPublic, MetadataViews.ResolverCollection}>(
self.CollectionPublicPath,
target: self.CollectionStoragePath
)
// Create a Minter resource and save it to storage
let minter <- create NFTMinter()
self.account.save(<-minter, to: self.MinterStoragePath)
emit ContractInitialized()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment