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  }