Alert Source Discuss
Standards Track: ERC

ERC-1046: tokenURI 互操作性

使用类似 ERC-721 的 tokenURI 扩展 ERC-20,并扩展 ERC-721 和 ERC-1155 以实现互操作性

Authors Tommy Nicholas (@tomasienrbc), Matt Russo (@mateosu), John Zettler (@JohnZettler), Matt Condon (@shrugs), Gavin John (@Pandapip1)
Created 2018-04-13
Requires EIP-20, EIP-721, EIP-1155

摘要

ERC-721 引入了一个 tokenURI 函数用于非同质化代币,以处理各种元数据,例如:

  • 缩略图
  • 标题
  • 描述
  • 特殊资产属性

此 ERC 向 ERC-20 添加了一个 tokenURI 函数,并扩展了 ERC-721ERC-1155,以实现所有三种类型的代币 URI 之间的互操作性。

动机

请参阅 ERC-721 中关于元数据扩展的说明。同样的论点也适用于 ERC-20。

能够使用类似的机制来提取 ERC-20、ERC-721、ERC-1155 和未来标准的元数据对于确定以下内容非常有用:

  • 合约是什么类型的代币(如果有);
  • 如何向用户展示代币,无论是在资产列表页面上还是在专用代币页面上;以及
  • 确定代币的功能

规范

本文档中使用的关键词“必须”,“禁止”,“需要”,“应该”,“不应该”,“推荐”,“不推荐”,“可以”和“可选”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。

互操作性元数据

以下 TypeScript 接口在后面的章节中使用:

/**
 * Interoperability metadata.
 * 互操作性元数据。
 * This can be extended by other proposals.
 * 这可以通过其他提案进行扩展。
 * 
 * All fields MUST be optional.
 * 所有字段都必须是可选的。
 * **Not every field has to be a boolean.** Any optional JSON-serializable object can be used by extensions.
 * **并非每个字段都必须是布尔值。** 任何可选的 JSON 可序列化对象都可以被扩展使用。
 */
interface InteroperabilityMetadata {
    /**
     * This MUST be true if this is ERC-1046 Token Metadata, otherwise, this MUST be omitted.
     * 如果这是 ERC-1046 代币元数据,则必须为 true,否则必须省略。
     * Setting this to true indicates to wallets that the address should be treated as an ERC-20 token.
     * 将其设置为 true 表示钱包应将该地址视为 ERC-20 代币。
     **/
    erc1046?: boolean | undefined;

    /**
     * This MUST be true if this is ERC-721 Token Metadata, otherwise, this MUST be omitted.
     * 如果这是 ERC-721 代币元数据,则必须为 true,否则必须省略。
     * Setting this to true indicates to wallets that the address should be treated as an ERC-721 token.
     * 将其设置为 true 表示钱包应将该地址视为 ERC-721 代币。
     **/
    erc721?: boolean | undefined;

    /**
     * This MUST be true if this is ERC-1155 Token Metadata, otherwise, this MUST be omitted.
     * 如果这是 ERC-1155 代币元数据,则必须为 true,否则必须省略。
     * Setting this to true indicates to wallets that the address should be treated as an ERC-1155 token.
     * 将其设置为 true 表示钱包应将该地址视为 ERC-1155 代币。
     **/
    erc1155?: boolean | undefined;
}

ERC-20 扩展

ERC-20 接口扩展

符合标准的合约必须实现以下 Solidity 接口:

pragma solidity ^0.8.0;

/// @title  ERC-20 Metadata Extension
/// @title  ERC-20 元数据扩展
interface ERC20TokenMetadata /* is ERC20 */ {
    /// @notice     Gets an ERC-721-like token URI
    /// @notice     获取类似 ERC-721 的代币 URI
    /// @dev        The resolved data MUST be in JSON format and support ERC-1046's ERC-20 Token Metadata Schema
    /// @dev        已解析的数据必须为 JSON 格式,并支持 ERC-1046 的 ERC-20 代币元数据架构
    function tokenURI() external view returns (string);
}

ERC-20 代币元数据架构

ERC-20 接口扩展部分中描述的 tokenURI 的已解析 JSON 必须符合以下 TypeScript 接口:

/**
 * Asset Metadata
 * 资产元数据
 */
interface ERC20TokenMetadata {
    /**
     * Interoperability, to differentiate between different types of tokens and their corresponding URIs.
     * 互操作性,用于区分不同类型的代币及其对应的 URI。
     **/
    interop: InteroperabilityMetadata;
    
    /**
     * The name of the ERC-20 token. 
     * ERC-20 代币的名称。
     * If the `name()` function is present in the ERC-20 token and returns a nonempty string, these MUST be the same value.
     * 如果 `name()` 函数存在于 ERC-20 代币中并返回一个非空字符串,则这些必须是相同的值。
     */
    name?: string;
    
    /**
     * The symbol of the ERC-20 token. 
     * ERC-20 代币的符号。
     * If the `symbol()` function is present in the ERC-20 token and returns a nonempty string, these MUST be the same value.
     * 如果 `symbol()` 函数存在于 ERC-20 代币中并返回一个非空字符串,则这些必须是相同的值。
     */
    symbol?: string;
    
    /**
     * The decimals of the ERC-20 token. 
     * ERC-20 代币的小数位数。
     * If the `decimals()` function is present in the ERC-20 token, these MUST be the same value.
     * 如果 `decimals()` 函数存在于 ERC-20 代币中,则这些必须是相同的值。
     * Defaults to 18 if neither this parameter nor the ERC-20 `decimals()` function are present.
     * 如果此参数和 ERC-20 `decimals()` 函数都不存在,则默认为 18。
     */
    decimals?: number;
    
    /**
     * Provides a short one-paragraph description of the ERC-20 token, without any markup or newlines.
     * 提供对 ERC-20 代币的简短一段描述,没有任何标记或换行符。
     */
    description?: string;
    
    /**
     * A URI pointing to a resource with mime type `image/*` that represents this token.
     * 指向具有 mime 类型 `image/*` 的资源的 URI,表示此代币。
     * If the image is a bitmap, it SHOULD have a width between 320 and 1080 pixels
     * 如果图像是位图,则其宽度应在 320 到 1080 像素之间
     * The image SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive.
     * 图像的纵横比应在 1.91:1 到 4:5 之间(包括两者)。
     */
    image?: string;
    
    /**
     * One or more URIs each pointing to a resource with mime type `image/*` that represents this token.
     * 一个或多个 URI,每个 URI 都指向具有 mime 类型 `image/*` 的资源,表示此代币。
     * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels
     * 如果图像是位图,则其宽度应在 320 到 1080 像素之间
     * Images SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive.
     * 图像的纵横比应在 1.91:1 到 4:5 之间(包括两者)。
     */
    images?: string[];
    
    /**
     * One or more URIs each pointing to a resource with mime type `image/*` that represent an icon for this token.
     * 一个或多个 URI,每个 URI 都指向具有 mime 类型 `image/*` 的资源,表示此代币的图标。
     * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels, and MUST have a height equal to its width
     * 如果图像是位图,则其宽度应在 320 到 1080 像素之间,并且高度必须等于其宽度
     * Images MUST have an aspect ratio of 1:1, and use a transparent background
     * 图像的纵横比必须为 1:1,并使用透明背景
     */
    icons?: string[];
}

ERC-721 扩展

对 ERC-721 元数据架构的扩展

实现 ERC-721 并使用其代币元数据 URI 的合约应使用以下 TypeScript 扩展来扩展元数据 URI:

interface ERC721TokenMetadataInterop extends ERC721TokenMetadata {
    /**
     * Interoperability, to avoid confusion between different token URIs
     * 互操作性,以避免不同代币 URI 之间的混淆
     **/
    interop: InteroperabilityMetadata;
}

ERC-1155 扩展

ERC-1155 接口扩展

使用元数据扩展的符合 ERC-1155 的合约应实现以下 Solidity 接口:

pragma solidity ^0.8.0;

/// @title  ERC-1155 Metadata URI Interoperability Extension
/// @title  ERC-1155 元数据 URI 互操作性扩展
interface ERC1155TokenMetadataInterop /* is ERC1155 */ {
    /// @notice         Gets an ERC-1046-compliant ERC-1155 token URI
    /// @notice         获取一个符合 ERC-1046 的 ERC-1155 代币 URI
    /// @param  tokenId The token ID to get the URI of
    /// @param  tokenId 要获取 URI 的代币 ID
    /// @dev            The resolved data MUST be in JSON format and support ERC-1046's Extension to the ERC-1155 Token Metadata Schema
    /// @dev            解析的数据必须为 JSON 格式,并支持 ERC-1046 的 ERC-1155 代币元数据架构的扩展
    ///                 This MUST be the same URI as the `uri(tokenId)` function, if present.
    ///                 如果存在,这必须与 `uri(tokenId)` 函数的 URI 相同。
    function tokenURI(uint256 tokenId) external view returns (string);
}

对 ERC-1155 元数据架构的扩展

实现 ERC-1155 并使用其代币元数据 URI 的合约建议使用以下扩展来扩展元数据 URI。实现 ERC-1155 接口扩展部分中描述的接口的合约必须使用以下 TypeScript 扩展:

interface ERC1155TokenMetadataInterop extends ERC1155TokenMetadata {
    /**
     * Interoperability, to avoid confusion between different token URIs
     * 互操作性,以避免不同代币 URI 之间的混淆
     **/
    interop: InteroperabilityMetadata;
}

其他建议

为了节省 gas,建议符合标准的合约不要实现 name()symbol()decimals() 函数,而是只将它们包含在元数据 URI 中。此外,对于 ERC-20 代币,如果小数位数为 18,则不建议在元数据中包含 decimals 字段。

理由

此 ERC 使开发人员可以更直接地向 ERC-20 代币添加元数据,并且对整个生态系统的干扰最小或没有干扰。使用相同的参数名称可以更轻松地重用代码。

此外,不使用 ERC-20 的 namesymboldecimals 函数的建议可以节省 gas。

内置的互操作性非常有用,因为否则可能不容易区分代币的类型。可以使用 ERC-165 完成互操作性,但静态调用对于钱包和网站来说效率不高,并且通常不够灵活。相反,在代币 URI 中包含互操作性数据可以提高灵活性,同时还可以提高性能。

向后兼容性

此 EIP 完全向后兼容,因为它的实现只是简单地扩展了 ERC-20 代币的功能,并且是可选的。此外,它为 ERC-721 和 ERC-1155 代币提出了向后兼容的建议。

安全注意事项

服务器端请求伪造 (SSRF)

钱包应该小心对待向 URL 发出任意请求。因此,建议钱包通过将特定方案和端口列入白名单来清理 URI。例如,一个易受攻击的钱包可能会被欺骗,从而修改本地托管的 redis 数据库中的数据。

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Tommy Nicholas (@tomasienrbc), Matt Russo (@mateosu), John Zettler (@JohnZettler), Matt Condon (@shrugs), Gavin John (@Pandapip1), "ERC-1046: tokenURI 互操作性," Ethereum Improvement Proposals, no. 1046, April 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1046.