github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/main/cadence/contracts/ExampleNFT.cdc (about) 1 // This is an example implementation of a Flow Non-Fungible Token 2 // It is not part of the official standard but it assumed to be 3 // very similar to how many NFTs would implement the core functionality. 4 import NonFungibleToken from 0xf8d6e0586b0a20c7 5 import MetadataViews from 0xf8d6e0586b0a20c7 6 7 pub contract ExampleNFT: NonFungibleToken { 8 9 pub var totalSupply: UInt64 10 11 pub event ContractInitialized() 12 pub event Withdraw(id: UInt64, from: Address?) 13 pub event Deposit(id: UInt64, to: Address?) 14 15 pub let CollectionStoragePath: StoragePath 16 pub let CollectionPublicPath: PublicPath 17 pub let MinterStoragePath: StoragePath 18 19 pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver { 20 pub let id: UInt64 21 22 pub let name: String 23 pub let description: String 24 pub let thumbnail: String 25 access(self) let royalties: [MetadataViews.Royalty] 26 access(self) let metadata: {String: AnyStruct} 27 28 init( 29 id: UInt64, 30 name: String, 31 description: String, 32 thumbnail: String, 33 royalties: [MetadataViews.Royalty], 34 metadata: {String: AnyStruct}, 35 ) { 36 self.id = id 37 self.name = name 38 self.description = description 39 self.thumbnail = thumbnail 40 self.royalties = royalties 41 self.metadata = metadata 42 } 43 44 pub fun getViews(): [Type] { 45 return [ 46 Type<MetadataViews.Display>(), 47 Type<MetadataViews.Royalties>(), 48 Type<MetadataViews.Editions>(), 49 Type<MetadataViews.ExternalURL>(), 50 Type<MetadataViews.NFTCollectionData>(), 51 Type<MetadataViews.NFTCollectionDisplay>(), 52 Type<MetadataViews.Serial>(), 53 Type<MetadataViews.Traits>() 54 ] 55 } 56 57 pub fun resolveView(_ view: Type): AnyStruct? { 58 switch view { 59 case Type<MetadataViews.Display>(): 60 return MetadataViews.Display( 61 name: self.name, 62 description: self.description, 63 thumbnail: MetadataViews.HTTPFile( 64 url: self.thumbnail 65 ) 66 ) 67 case Type<MetadataViews.Editions>(): 68 // There is no max number of NFTs that can be minted from this contract 69 // so the max edition field value is set to nil 70 let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil) 71 let editionList: [MetadataViews.Edition] = [editionInfo] 72 return MetadataViews.Editions( 73 editionList 74 ) 75 case Type<MetadataViews.Serial>(): 76 return MetadataViews.Serial( 77 self.id 78 ) 79 case Type<MetadataViews.Royalties>(): 80 return MetadataViews.Royalties( 81 self.royalties 82 ) 83 case Type<MetadataViews.ExternalURL>(): 84 return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString())) 85 case Type<MetadataViews.NFTCollectionData>(): 86 return MetadataViews.NFTCollectionData( 87 storagePath: ExampleNFT.CollectionStoragePath, 88 publicPath: ExampleNFT.CollectionPublicPath, 89 providerPath: /private/exampleNFTCollection, 90 publicCollection: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic}>(), 91 publicLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(), 92 providerLinkedType: Type<&ExampleNFT.Collection{ExampleNFT.ExampleNFTCollectionPublic,NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(), 93 createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection { 94 return <-ExampleNFT.createEmptyCollection() 95 }) 96 ) 97 case Type<MetadataViews.NFTCollectionDisplay>(): 98 let media = MetadataViews.Media( 99 file: MetadataViews.HTTPFile( 100 url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" 101 ), 102 mediaType: "image/svg+xml" 103 ) 104 return MetadataViews.NFTCollectionDisplay( 105 name: "The Example Collection", 106 description: "This collection is used as an example to help you develop your next Flow NFT.", 107 externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"), 108 squareImage: media, 109 bannerImage: media, 110 socials: { 111 "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain") 112 } 113 ) 114 case Type<MetadataViews.Traits>(): 115 // exclude mintedTime and foo to show other uses of Traits 116 let excludedTraits = ["mintedTime", "foo"] 117 let traitsView = MetadataViews.dictToTraits(dict: self.metadata, excludedNames: excludedTraits) 118 119 // mintedTime is a unix timestamp, we should mark it with a displayType so platforms know how to show it. 120 let mintedTimeTrait = MetadataViews.Trait(name: "mintedTime", value: self.metadata["mintedTime"]!, displayType: "Date", rarity: nil) 121 traitsView.addTrait(mintedTimeTrait) 122 123 // foo is a trait with its own rarity 124 let fooTraitRarity = MetadataViews.Rarity(score: 10.0, max: 100.0, description: "Common") 125 let fooTrait = MetadataViews.Trait(name: "foo", value: self.metadata["foo"], displayType: nil, rarity: fooTraitRarity) 126 traitsView.addTrait(fooTrait) 127 128 return traitsView 129 130 } 131 return nil 132 } 133 } 134 135 pub resource interface ExampleNFTCollectionPublic { 136 pub fun deposit(token: @NonFungibleToken.NFT) 137 pub fun getIDs(): [UInt64] 138 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT 139 pub fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? { 140 post { 141 (result == nil) || (result?.id == id): 142 "Cannot borrow ExampleNFT reference: the ID of the returned reference is incorrect" 143 } 144 } 145 } 146 147 pub resource Collection: ExampleNFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection { 148 // dictionary of NFT conforming tokens 149 // NFT is a resource type with an `UInt64` ID field 150 pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} 151 152 init () { 153 self.ownedNFTs <- {} 154 } 155 156 // withdraw removes an NFT from the collection and moves it to the caller 157 pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { 158 let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") 159 160 emit Withdraw(id: token.id, from: self.owner?.address) 161 162 return <-token 163 } 164 165 // deposit takes a NFT and adds it to the collections dictionary 166 // and adds the ID to the id array 167 pub fun deposit(token: @NonFungibleToken.NFT) { 168 let token <- token as! @ExampleNFT.NFT 169 170 let id: UInt64 = token.id 171 172 // add the new token to the dictionary which removes the old one 173 let oldToken <- self.ownedNFTs[id] <- token 174 175 emit Deposit(id: id, to: self.owner?.address) 176 177 destroy oldToken 178 } 179 180 // getIDs returns an array of the IDs that are in the collection 181 pub fun getIDs(): [UInt64] { 182 return self.ownedNFTs.keys 183 } 184 185 // borrowNFT gets a reference to an NFT in the collection 186 // so that the caller can read its metadata and call its methods 187 pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT { 188 return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)! 189 } 190 191 pub fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? { 192 if self.ownedNFTs[id] != nil { 193 // Create an authorized reference to allow downcasting 194 let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)! 195 return ref as! &ExampleNFT.NFT 196 } 197 198 return nil 199 } 200 201 pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} { 202 let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)! 203 let exampleNFT = nft as! &ExampleNFT.NFT 204 return exampleNFT as &AnyResource{MetadataViews.Resolver} 205 } 206 207 destroy() { 208 destroy self.ownedNFTs 209 } 210 } 211 212 // public function that anyone can call to create a new empty collection 213 pub fun createEmptyCollection(): @NonFungibleToken.Collection { 214 return <- create Collection() 215 } 216 217 // Resource that an admin or something similar would own to be 218 // able to mint new NFTs 219 // 220 pub resource NFTMinter { 221 222 // mintNFT mints a new NFT with a new ID 223 // and deposit it in the recipients collection using their collection reference 224 pub fun mintNFT( 225 recipient: &{NonFungibleToken.CollectionPublic}, 226 name: String, 227 description: String, 228 thumbnail: String, 229 royalties: [MetadataViews.Royalty] 230 ) { 231 let metadata: {String: AnyStruct} = {} 232 let currentBlock = getCurrentBlock() 233 metadata["mintedBlock"] = currentBlock.height 234 metadata["mintedTime"] = currentBlock.timestamp 235 metadata["minter"] = recipient.owner!.address 236 237 // this piece of metadata will be used to show embedding rarity into a trait 238 metadata["foo"] = "bar" 239 240 // create a new NFT 241 var newNFT <- create NFT( 242 id: ExampleNFT.totalSupply, 243 name: name, 244 description: description, 245 thumbnail: thumbnail, 246 royalties: royalties, 247 metadata: metadata, 248 ) 249 250 // deposit it in the recipient's account using their reference 251 recipient.deposit(token: <-newNFT) 252 253 ExampleNFT.totalSupply = ExampleNFT.totalSupply + UInt64(1) 254 } 255 } 256 257 init() { 258 // Initialize the total supply 259 self.totalSupply = 0 260 261 // Set the named paths 262 self.CollectionStoragePath = /storage/exampleNFTCollection 263 self.CollectionPublicPath = /public/exampleNFTCollection 264 self.MinterStoragePath = /storage/exampleNFTMinter 265 266 // Create a Collection resource and save it to storage 267 let collection <- create Collection() 268 self.account.save(<-collection, to: self.CollectionStoragePath) 269 270 // create a public capability for the collection 271 self.account.link<&ExampleNFT.Collection{NonFungibleToken.CollectionPublic, ExampleNFT.ExampleNFTCollectionPublic, MetadataViews.ResolverCollection}>( 272 self.CollectionPublicPath, 273 target: self.CollectionStoragePath 274 ) 275 276 // Create a Minter resource and save it to storage 277 let minter <- create NFTMinter() 278 self.account.save(<-minter, to: self.MinterStoragePath) 279 280 emit ContractInitialized() 281 } 282 }