Technology community sharing | Cadence NFT new standard MetadataViews introduction

On January 5, 2022, the Flow NFT warehouse merged the new metadata standard. This metadata standard was jointly completed by Versus, Find founder Bjarte S. Karlsen, and DapperLabs  engineer  Joshua Hannan  in the Flip-Fest event. After nearly three After months of discussion and improvement, it was finally merged into Flow's official NFT standard library. This article will introduce the standard details related to MetadataView from the code details.

written in front

  • This standard upgrade is optional and will not affect the NonFungibleToken contract, nor will it affect the business logic of the originally deployed contract

  • Contracts that have been deployed on the main network need to be upgraded to apply the new Metadata standard

  • The resources covered by the upgrade will affect the type inherited by Collection, and will also affect NFT resources

  • The Metadata standard defines a set of flexible interface implementations that can support any type or custom Metadata format

  • Support an NFT with multiple Metadata types, and provide a unified reading method

  • The Metadata standard is defined by a brand new contract. To implement the standard contract, a new contract called MetadataViews needs to be introduced in addition to NonFungibleToken.

MetadataViews contract details

MetadataViews consists of two interface definitions and four recommended Metadata structs (which may be added later):

Interface

  • Resolver - inherited by NFT resources

  • ResolverCollection - Inherited by resolving NFT collections

Struct

  • Display

  • File

  • HTTPFile

  • IPFSFile

■ MetadataViews.Resolver


// A Resolver provides access to a set of metadata views.

//

// A struct or resource (e.g. an NFT) can implement this interface

// to provide access to the views that it supports.

//
pub resource interface Resolver {

  pub fun getViews(): [Type] // getViews 返回 NFT 所支持的不同类型的 metadata 数据

  pub fun resolveView(_ view: Type): AnyStruct? // 根据具体的 View 类型获取到 NFT 所实现的 Struct

}

Resolver needs to be inherited by NFT or NFT's data structure and implement the two functions defined in it

 MetadataViews.ResolverCollection

  • getViews —— returns the View type in the form of an array, and is also the different types of metadata data supported by NFT

  • resolveView —— According to the specific type, get the struct data of NFT

    
    // ExampleNFT
    // 1. 比原有的实现增加了 MetadataViews.Resolver 接口,并实现了其定义的两个函数
    pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver {
    
      pub let id: UInt64
    
      // 2. 为实现 MetadataViews.Display 所添加的属性,如果需要实现其他类型的 metadata 需要添加相应的字段
      pub let name: String
    
      pub let description: String
    
      pub let thumbnail: String
    
    
      /* init function ... */
    
      pub fun getViews(): [Type] {
    
        return [
    
          Type<MetadataViews.Display>() // 3. 可以定义多个 View 类型
    
        ]
    
      }
    
    
      pub fun resolveView(_ view: Type): AnyStruct? {
    
        // 4. 根据 view 类型,返回不同的 struct 结构,当然我们也可以自己定义返回结构
        switch view {
    
          case Type<MetadataViews.Display>():
    
            return MetadataViews.Display(
    
              name: self.name,
    
              description: self.description,
    
              thumbnail: MetadataViews.HTTPFile(
    
              url: self.thumbnail
    
            )
    
          )
    
        }
    
        return nil
    
      }
    
    }
    

  • Note one position, we can see that the NFT resource needs to implement the MetadataViews.Resolver interface, and implement the two functions defined by it

  • Note 2 position implements the interface and requires NFT to be able to return a specified type of Struct, so it is also necessary to define the fields required to return the structure, so NFT adds MetadataViews.Display name, description and thumbnail

  • Note three positions getViews returns an array composed of types. There is only one MetadataViews.Display type. Of course, we can also add multiple types, such as File, HTTPFile, IPFSFile, and of course the corresponding fields must be implemented accordingly.

  • Note four positions resolveView will return different data structures according to the incoming View type, if not, it will return empty directly, so when writing query scripts, you need to obtain the View type supported by NFT through getViews first, and then according to the supported type , call resolveView to get the metadata data structure.

  • 
    / A ResolverCollection is a group of view resolvers index by ID.
    
    //
    
    pub resource interface ResolverCollection {
    
      pub fun borrowViewResolver(id: UInt64): &{Resolver} // 通过 NFT id 获得 Resolver
    
      pub fun getIDs(): [UInt64] // 获得集合中的 NFT ids
    
    }

    ResolverCollection needs to be inherited by the Collection resource and implement two of its functions:

  • borrowViewResolver —— The Resolver supported by the specific NFT id can be any type of AnyResource

  • getIDs —— Get the NFT ids in the collection

  • // 1. 增加 MetadataViews.ResolverCollection 继承并实现其函数
    pub resource Collection: ExampleNFTCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
    
      pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
      /* init function and props  */
    
      // getIDs returns an array of the IDs that are in the collection
      pub fun getIDs(): [UInt64] {
        return self.ownedNFTs.keys
      }
    
      /* other function  */
      // 2. 这里能够返回任意类型的 MetadataViews.Resolver 实现,拥有 getViews 和 resolveView 的方法获得 NFT 的数据
      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
      }
    }

    *Note 1 position:  The NFT Collection resource implements the MetadataViews.ResolverCollection interface, and implements the two functions defined by it: We usually query the details by obtaining the user's Collection resource, including getIDs and Resolver, which will be discussed later It is detailed in the query interface.

    *Note 2 position: borrowViewResolver returns any type of any type of View Resolver according to the NFT id, including getViews and resolveView methods

    ■  View Structs

    MetadataViews.Display - basic metadata implementation, including NFT basic information

  • name

  • description

  • thumbnail - returns any type of File structure - either HTTPFile or IPFSFile

  • 
    pub struct Display {
    
      // The name of the object.
    
      // This field will be displayed in lists and therefore should
    
      // be short an concise.
    
      pub let name: String
    
      // A written description of the object.
    
      // This field will be displayed in a detailed view of the object,
    
      // so can be more verbose (e.g. a paragraph instead of a single line).
    
      pub let description: String
    
      // A small thumbnail representation of the object.
    
      // This field should be a web-friendly file (i.e JPEG, PNG)
    
      // that can be displayed in lists, link previews, etc.
    
      pub let thumbnail: AnyStruct{File}
    
    
      init(
    
        name: String,
    
        description: String,
    
        thumbnail: AnyStruct{File}
    
      ) {
    
        self.name = name
    
        self.description = description
    
        self.thumbnail = thumbnail
    
      }
    
    }

    MetadataViews.File —— the structural interface of the basic File, including the return of the uri function

    
    // File is a generic interface that represents a file stored on or off chain.
    
    // Files can be used to references images, videos and other media.
    
    pub struct interface File {
    
      pub fun uri(): String
    
    }

    MetadataViews.HTTPFile —— image URL information accessed through the Http protocol

  • Contains url information

    
    // HTTPFile is a file that is accessible at an HTTP (or HTTPS) URL.
    
    pub struct HTTPFile: File {
    
      pub let url: String
    
    
      init(url: String) {
    
        self.url = url
    
      }
    
      pub fun uri(): String {
    
        return self.url
    
      }
    
    }

    MetadataViews.IPFSFile —— a file storage compatible with the IPFS protocol, including the return of cid and path

  • cid

  • path

    
    // IPFSThumbnail returns a thumbnail image for an object
    
    // stored as an image file in IPFS.
    
    // IPFS images are referenced by their content identifier (CID)
    
    // rather than a direct URI. A client application can use this CID
    
    // to find and load the image via an IPFS gateway.
    
    pub struct IPFSFile: File {
    
      // CID is the content identifier for this IPFS file.
    
      // Ref: <https://docs.ipfs.io/concepts/content-addressing/>
    
      pub let cid: String
    
      // Path is an optional path to the file resource in an IPFS directory.
    
      // This field is only needed if the file is inside a directory.
    
      // Ref: <https://docs.ipfs.io/concepts/file-systems/>
    
      pub let path: String?
    
    
      init(cid: String, path: String?) {
    
        self.cid = cid
    
        self.path = path
    
      }
    
      // This function returns the IPFS native URL for this file.
    
      // Ref: <https://docs.ipfs.io/how-to/address-ipfs-on-web/#native-urls>
    
      pub fun uri(): String {
    
        if let path = self.path {
    
          return "ipfs://".concat(self.cid).concat("/").concat(path)
    
        }
    
        return "ipfs://".concat(self.cid)
    
        }
    
    }

    query script

  • The query script needs to further convert the resource type to obtain the basic metadata information. Here we take the query script of ExampleNFT as an example

  • import ExampleNFT from "../../contracts/ExampleNFT.cdc"
    
    import MetadataViews from "./MetadataViews.cdc"
    
    
    // 1. 定义 NFT 数据的读取结构,为脚本函数的返回值定义类型
    pub struct NFT {
    
      pub let name: String
    
      pub let description: String
    
      pub let thumbnail: String
    
      pub let owner: Address
    
      pub let type: String
    
    
    
      init(
    
        name: String,
    
        description: String,
    
        thumbnail: String,
    
        owner: Address,
    
        nftType: String,
    
      ) {
    
        self.name = name
    
        self.description = description
    
        self.thumbnail = thumbnail
    
        self.owner = owner
    
        self.type = nftType
    
      }
    
    }
    
    
      // 2. 脚本执行函数,返回之前定义的 NFT 结构,传入地址 + NFT id 参数获取 NFT 数据
      pub fun main(address: Address, id: UInt64): NFT {
    
        let account = getAccount(address)
    
    
        // 借用用户账户下的 Collection 资源
        let collection = account
    
        .getCapability(ExampleNFT.CollectionPublicPath)
    
        .borrow<&{ExampleNFT.ExampleNFTCollectionPublic}>()
    
        ?? panic("Could not borrow a reference to the collection")
    
    
        // 通过 NFT id 获取到 NFT 资源
        let nft = collection.borrowExampleNFT(id: id)!
    
    
    
        // Get the basic display information for this NFT
        // 3. 因为我们的 NFT 已经实现了 MetadataViews.Resolver 接口, 所以这里调用 resolveView 能够获取到具体的 View 类型
    
        let view = nft.resolveView(Type<MetadataViews.Display>())!
    
    
        // 将返回的 `AnyStruct` 转换成具体的 struct
        let display = view as! MetadataViews.Display
        // 补充 NFT 所需要的额外信息
        let owner: Address = nft.owner!.address!
        // 获得 NFT 类型
        let nftType = nft.getType()
    
    
        // 返回 NFT 结构
        return NFT(
    
          name: display.name,
    
          description: display.description,
    
          thumbnail: display.thumbnail.uri(),
    
          owner: owner,
    
          nftType: nftType.identifier,
    
        )
    
    }

    * Note 1 position: Define an NFT data structure as the return value of the query script

    * Note 2 position:  By passing in the address and NFT id as parameters, query the metadata of the NFT. Here we need to know the NFT IDs owned by the address in advance. Through getIDs, we can get the NFT ids owned by the current user and then query

    * Note three positions: Because our NFT has implemented the MetadataViews.Resolver interface, so calling resolveView here can get the specific View type. Before that, we also need to know which Structs the NFT implements, so we also need to getViews through getViews Obtain. In the example, it is assumed that we know that NFT implements MetadataViews.Display, so the cast symbol ! is used to convert AnyStruct to view as! MetadataViews.Display

    In the process of actual use, the code may be more complicated. Here is just a targeted query for the implementation of the simple Example NFT. In the process of integration, specific situations need to be dealt with in detail.

    Summarize

    The new version of the Metadata standard has a great impact on NFT, which is why the author decided to write an introduction. It can help contract developers, third-party applications, and basic service developers implement NFT assets according to unified interfaces and specifications data query. In the absence of standards in the past, the diversity of NFT assets will lead to increased query complexity. Based on the NFT contract that implements the MetadataViews standard, it can reduce the complexity of business development based on NFT assets, reduce the cost of integration, and improve the development of innovative businesses Efficiency, the most important thing is to open up the space for the composability of NFT assets. Similar to the promotion of DeFi by the ERC20 standard, the Metadata standard can provide more possibilities for asset composability.

Guess you like

Origin blog.csdn.net/weixin_57551966/article/details/125088593