EIP721 - 非同质化代币标准

# 概要

一个不可互换代币的标准接口,也称为契约。

# 摘要

本标准说明了在智能合约中实现 NFT 的标准 API。 这一标准提供了追踪和传输 NFT 的基本功能。

我们考虑了由个人拥有及交易 NFT 使用情景,也包括了第三方经纪人/ 钱包/ 拍卖者(“经营者”)。 NFT 能够代表数字或物理资产的所有权。 我们考虑了各种各样的资产,并且我们知道你会联想到更多:

  • 有形财产 —— 房屋、独特的艺术品
  • 虚拟收藏品 —— 独特的小猫咪图片、可收藏卡片
  • “负值”资产 —— 贷款、负担和其他负债

一般而言,所有房屋都是不同的,没有两个小猫咪是相同的。 NFT 是可区分的 ,你必须单独跟踪每一个的所有权。

# 动机

标准接口允许钱包/经纪人/拍卖应用程序与以太坊上的任何 NFT 一起工作。 我们提供了简单的 ERC-721 智能合约以及追踪了 大量 NFT 的智能合约 。 下文将讨论其他应用。

这一标准受到 ERC-20 代币标准的启发,并建立在 EIP-20 创立以来两年的经验基础上。 EIP-20 不足以追踪NFT,因为每个资产都是不同的(不可互换),而代币都是相同的(可互换)。

下文审查了这一标准与 EIP-20 之间的差异。

# 技术规范

本文件中的关键词“须(MUST)”、“不得(MUST NOT)”、“须(REQUIRED)”、“须(SHALL)”、“不得(SHALL NOT)”、“应(SHOULD)”、“不应(SHOULD NOT)”、“建议(RECOMMENDED)”、“可(MAY)”和“可选(OPTIONAL)”按 RFC 2119 的规定进行解释。

** 每个 ERC-721 兼容的合同都必须实现 ERC721ERC165 接口 ** (但须遵守以下“警告”):

pragma solidity ^0.4.20;

/// @title ERC-721 非同质化代币标准
/// @dev 参照 https://eips.ethereum.org/EIPS/eip-721
/// 注意:此接口的 ERC-165 标识符为 0x80ac58cd。
interface ERC721 /* is ERC165 */ {
    /// @dev  当 NFT 的所有权被改变时,这个接口就会触发。
    /// 当 NFT 创建(`from` == 0) 或销毁(`to` == 0)时,此事件触发 例外:在合约创建期间,任何数量的 NFTs
    //// 都可以创建和分配,而无需出发 Transfer。     /// 在任何传输时,该 NFT 的核准地址(如果有的话)将被重置为零。
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    /// @dev 当已批准的 NFT 地址被更改或
    /// 确认时,将触发。 零地址表示没有核准的地址。
    /// 当传输事件发出时,这也表明已批准的 NFT
    /// 地址(如果有的话)将被重置为零。
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    /// @dev 当操作者对所有者启用或禁用时触发。
    // 操作者可以管理所有者的所有NFT。
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    /// @notice 计数分配给所有者的全部NFT
    /// @dev 分配给空地址的NFTs 均被视为无效, 和此
    /// 函数触发有关零地址的查询。
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size > 0). If so, it calls
    ///  `onERC721Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable;

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external;

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address);

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

钱包/ 经纪人/ 拍卖应用程序须实现钱包接口,才能接受安全转账。

/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface ERC721TokenReceiver {
    /// @notice Handle the receipt of an NFT
    /// @dev The ERC721 smart contract calls this function on the recipient
    ///  after a `transfer`. This function MAY throw to revert and reject the
    ///  transfer. Return of other than the magic value MUST result in the
    ///  transaction being reverted.
    ///  Note: the contract address is always the message sender.
    /// @param _operator The address which called `safeTransferFrom` function
    /// @param _from The address which previously owned the token
    /// @param _tokenId The NFT identifier which is being transferred
    /// @param _data Additional data with no specified format
    /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
    ///  unless throwing
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

对于 ERC-721 智能合约来说,元数据扩展(metadata extension)是可选的(请参阅下面的“注意事项”)。 这样就可以询问你的智能合约的名称和有关 NFT(非同质化代币)资产的详细信息。

/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface ERC721Metadata /* is ERC721 */ {
    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external view returns (string _name);

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external view returns (string _symbol);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns (string);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这是上面引用的 “JSON Schema 的 ERC721 元数据”。

{
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents"
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents"
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

对于 ERC-721 智能合约来说,枚举扩展(enumeration extension)是可选的(请参阅下面的“注意事项”)。 这让你的合约可以发布其 NFT 的完整列表,并让这些列表可被发现。

/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x780e9d63.
interface ERC721Enumerable /* is ERC721 */ {
    /// @notice Count NFTs tracked by this contract
    /// @return A count of valid NFTs tracked by this contract, where each one of
    ///  them has an assigned and queryable owner not equal to the zero address
    function totalSupply() external view returns (uint256);

    /// @notice Enumerate valid NFTs
    /// @dev Throws if `_index` >= `totalSupply()`.
    /// @param _index A counter less than `totalSupply()`
    /// @return The token identifier for the `_index`th NFT,
    ///  (sort order not specified)
    function tokenByIndex(uint256 _index) external view returns (uint256);

    /// @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `_index` >= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid NFTs.
    /// @param _owner An address where we are interested in NFTs owned by them
    /// @param _index A counter less than `balanceOf(_owner)`
    /// @return The token identifier for the `_index`th NFT assigned to `_owner`,
    ///   (sort order not specified)
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 注意事项

0.4.20 Solidity 接口语法的表达能力不足以记录 ERC-721 标准。 符合 ERC-721 的合约须遵守以下内容:

  • Solidity 问题 #3412:上述接口包括每个函数的显式可变性保证。可变性保证从弱到强依次为:payable、隐式不可支付、viewpure。你的操作须满足该接口中的可变性保证,且你可满足更强的保证。例如,此接口中的payable函数,在你的合约中可实现为不可付款(未指定状态可变性)。我们期待 Solidity 稍后的版本能让你从该接口继承更严格的合约,但 0.4.20 版本的解决方法是:从你的合约继承之前,你可以编辑此接口,以添加更严格的可变性。
  • Solidity 问题 #3419:可以实现 ERC721MetadataERC721Enumerable 的合约,也应该能够实现 ERC721。ERC-721 实现了接口 ERC-165 的要求。
  • Solidity 问题 #2330:如果一个函数在本规范中显示为 external ,那么,如果合约使用 public 可见性,则它是合规的。0.4.20 版本的解决方法是,你可以编辑此接口,在继承合约之前切换到 public
  • Solidity 问题 #3494、#3544:使用 this.*.selector,被 Solidity 标记为警告, Solidity 的未来版本将不会标记该错误。

如果新版本的 Solidity 允许代码中显示警告,那么此 EIP 可被更新且警告可被删除,这些将与最初的规范一致。

# 基本原理

以太坊中,依赖追踪可区分资产的智能合约有许多拟议用途。 现有的或计划中的 NFT 如:Decentraland 中的土地、加密朋克(CryptoPunks)中的同名朋克,以及使用 DMarket 或 EnjinCoin 等系统的游戏内物品。未来用途有追踪现实世界的资产,如房地产(正如 Ubitquity 或 Propy 等公司所设想的那样)。在以上每种情况下,作为分类帐中的数字,这些物品都不是“集中在一起”的。相反,每项资产的单独所有权必须单独、自动追踪,这一点至关重要。不论这些资产的性质是什么,如果我们有一个标准化的接口,能够实现跨功能的资产管理和销售平台,那么生态系统就会变得越来越强大。

"NFT" 选词

几乎所有接受调查的人都对“NFT”感到满意,“NFT”广泛适用于可区分的数字资产领域。 我们发现,此标准下的某些应用程序可以用“契约”一词来准确描述(特别是实体资产)。

可供考虑的其他选择:可区分资产、标题、代币、资产、股权、门票

NFT 标识符

每一个 NFT,由 ERC-721 智能合约中唯一的uint256 ID 标识。ID 号码在智能合约的生命周期内不会改变。两者 (contract address, uint256 tokenId) 将成为以太坊链上某一特定资产的唯一且完全合格的标识符。对一些 ERC-721 智能合约来说,从 ID 0 开始,每增加一个 NFT,NTF ID 加 1,虽然这样很方便,但调用者不应假设 ID 号码有任何特定的模式,须将 ID 视为“黑匣子”。此外,须注意 NFT 可能会失效(被销毁)。请参阅枚举函数,以了解所支持的枚举接口。

由于 UUIDs 和 sha3 哈希可直接转换为 uint256,许多类型的应用程序均可选择 uint256

转移机制

ERC-721 standardizes a safe transfer function safeTransferFrom (overloaded with and without a bytes parameter) and an unsafe function transferFrom. Transfers may be initiated by: 在 ERC-721 中, safeTransferFrom (加载或过载 bytes 参数)表示一个安全的转账函数;transferFrom 则表示一个不安全的转账函数。转账操作可由以下角色发起:

  • NFT 的所有者
  • NFT 的授权地址
  • NFT 当前所有者授权的操作者

此外,授权的操作者可设置某个 NFT 的授权地址。这为钱包、经纪人和拍卖程序提供了一个强大的设置工具,以快速使用大量的 NFT。

转账和收款函数的证明文件,只对交易必须抛出异常的条件做出了说明。抛出异常也可在其他情况下进行。这样可以获得一些有趣的结果:

  • 如合约暂停则禁止转移 —— 现有技术,加密猫(CryptoKitties)合约第 611 行
  • 将接收 NFT 的某些地址列入黑名单 —— 现有技术,加密猫合约,第 565、566 行
  • 禁止非安全转账 —— 除以下几种情况外,transferFrom 将会抛出:1. _tomsg.sender ;2. countOf(_to) 是非零;3. countOf(_to) 以前是非零(这些情况是安全的)
  • 向交易双方收取费用 —— 如果以前是零地址,用非零 _approved 调用 approve 时需支付费用;如果以前是非零地址,用零地址调用 approve 时需退回费用。调用任一转移函数时需付款,转移参数 _to 需等于 msg.sender,转账参数 _to 需成为 NFT 的批准地址
  • 只读的 NFT 注册表 —— 使用unsafeTransfer, transferFrom, approve 以及 setApprovalForAll 时均会抛出异常

交易失败将会抛出,这是 ERC-223, ERC-677, ERC-827 和 OpenZeppeliSafeERC20.sol 实现的最佳实践。ERC-20 定义了 allowance 功能,调用时会产生问题,然后修改为不同的数量,如 OpenZeppelin 中的问题 #438。在 ERC-721 中,由于每个 NFT 都是唯一的,所以没有定量,数量为零或一。 因此,如果不出现后来发现的问题,我们将从 ERC-20 的原始设计中获益。

规范中不包括创建 NFT(“铸造”)和销毁 NFT(“燃烧”)。你的合约可通过其他方式实现。请参阅 event 文档以了解创建或销毁 NFT 时需承担的责任。

我们对 onERC721Received 上的 operator (操作者)参数的必要性,提出了质疑。在可以设想的所有情况下,如果操作员很重要,他们可以将代币转移给自己,然后发送——那么操作员将成为 from 地址(发件人)。这看似是人为的,因为我们认为操作员是代币的临时所有者(转账给他们自己是多余的)。操作员发送代币时,由操作员亲自操作,而不是代表代币的持有者。 这就是操作员和之前的代币所有者,对代币接收者都很重要的原因。

可供考虑的其他选择:只允许两步式的 ERC-20 交易风格,要求转移函数永不抛出异常,要求所有函数返回表明操作成功的布尔值。

ERC-165 接口

我们选择标准接口查询 (ERC-165) 来展示 ERC-721 智能合约支持的接口。

未来的 EIP 可以为合约创建一个通用接口注册表。 我们大力支持这样的 EIP,通过代理给单独的合约,会让你的 ERC-721 实现来执行 ERC721EnumerableERC721Metadata 或其他接口。

Gas 和复杂性(关于枚举扩展)

此规范列举了实现,它们管理少量的以及任意大量的 NFT。如果你的应用程序可以增长,那么请避免在代码中使用 for/while (请参阅加密猫赏金问题 #4)。这表明你的合约可能无法扩展,而且 gas 的费用会随着时间的推移增加。

我们在测试网络 Testnet 部署了合约 XXXXERC721,它将 340282366920938463463374607431768211456 的不同行为 (2^128) 实例化并对其进行追踪。每个 IPV6 地址,足以分配给以太坊的帐户所有者,或者追踪纳米机器人的所有权,这些纳米机器人的大小有几微米,集成总数量约为地球的一半。 你可以从区块链中查询到,查询每个函数比查询 ENS 消耗的 gas 少。

该阐述变得更加明确:ERC-721 标准尺度。

可供考虑的其他选择:如果删除资产枚举函数需要 for 循环,那么从枚举函数返 Solidity 数组类型。

隐私

在动机部分,谈及了钱包/ 经纪人/ 拍卖商有强烈需求去确认一个所有者拥有哪些 NFT。

讨论不可枚举的 NFT 用例可能会很有趣,例如,财产所有权的私有注册表或部分私有注册表。 但是,由于攻击者可以轻松地 (!) 对每个可能的 tokenId 调用 ownerOf,所以无法得到隐私。

元数据选择(元数据扩展)

在元数据扩展中,我们需要 namesymbol 函数。我们审查的每一个代币的 EIP (以太坊改进提案)和草案(ERC-20, ERC-223, ERC-677, ERC-777, ERC-827)均包含这些函数。

我们要提醒执行作者,如果你反对使用此机制,那么空字符串对 namesymbol 函数是有效响应。还要提醒大家,任何智能合约都可以使用与你的合约相同的名称和符号。对于客户如何确定哪些 ERC-721 智能合约是众所周知的(规范的),则超出了本标准的范围。

机制是用来将 NFT 与 URI(统一资源标识符)相关联的。我们期望,许多实现能够利用这一点,为每个 NFT 提供元数据。图片大小推荐选自 Instagram,他们可能比较了解图片的可用性。URI 是可变的(它会随时改变)。我们假设 NFT 代表房屋的所有权,在这种情况下,房屋的元数据(图像、居住者等)自然发生变化。

元数据作为字符串值返回。目前,只适用于从 web3 调用,不能从其他合约调用。能实现这一点是因为,我们还未考虑到查询此类信息的区块链应用程序这种用例。

可供考虑的其他选择:将每项资产的所有元数据放在区块链上(比较昂贵),使用 URL 模板查询部分元数据(URL 模板不适用于所有 URL 方案,尤其是 P2P URL),多地址(multiaddr)网络地址(不够成熟)

社区共识

我们对原始 ERC-721问题进行了大量讨论,此外,还在 Gitter 上举行了第一次实时会议,本次会议极具代表性,并且进行了广泛宣传(如:在 Reddit、 Gitter #ERC 频道上以及针对原始 ERC-721 问题)。感谢各位参与者:

第二次活动于 2018 年在美国丹佛以太坊大会上(ETHDenver)举办,讨论了可区分资产标准(记录尚未发表)。

我们对于此过程非常包容,并邀请任何有问题或有贡献的人加入我们的讨论。但是,该标准仅适用于这里列出的已确认用例。

# 向后兼容

我们采用了 ERC-20 规范中的语义函数,如 balanceOftotalSupplynamesymbol。实现还包含一个 decimals 函数,如果其目标适用于这项标准时与 ERC-20 更兼容,则返回 uint8(0) 。但是,我们发现,它需要所有 ERC-721 实现以适应于 decimals 函数。

到 2018 年 2 月为止 NFT 实现案例:

  • 加密猫 —— 与此标准的较早版本兼容
  • 加密朋克 —— 与 ERC-20 部分兼容,但不易概括,因为合约直接包括了拍卖函数,所使用的函数名称明确指出资产为“朋克”。
  • 拍卖行资产接口 —— 对于拍卖行 ÐApp(现在叫做 ice-boxed),作者需要一个通用接口。他的“资产”合约非常简单,但缺失 ERC-20 兼容性、approve() 函数和元数据。本规范在 EIP-173 的讨论中已提及。

注意:如 Curio Cards 卡片、悲伤蛙(Rare Pepe)等“限量版可收集代币”是可区分资产。它们实际上是一套个人可替代代币,每种代币可通过其带有本身总供应量的智能合约追踪到(在极个别情况下可能为 1 )。

onERC721Received 函数专门应用于旧的部署合约,在某些特定条件下,尽管它们没有完成任何一个函数,可能会无意返回 1 (true(详见 Solidity 代理调用返回值(DelegateCallReturnValue)漏洞)。通过返回、检查魔法值,我们能够区分实际肯定回答与无意义的 true

# 测试案例

代币包含使用 Truffle 编写的测试案例。

# 实现

0xcert ERC721 -- 参考实现

  • MIT 许可,因此可以在你的工程中任意使用
  • 包含测试案例
  • 积极的漏洞赏金,如果发现错误,你将获得报酬

Su Squares —— 是一个广告平台,你可以在平台上出租空间和地点图片

  • 使用此标准或其实现方式,来完成 Su Squares 平台漏洞赏金项目以寻找问题
  • 执行完全标准及所有可选接口

ERC721 契约示例 —— 是一个实现案例

  • 使用 OpenZeppelin 项目格式实现

XXXXERC721, 作者是 William Entriken —— 是一个可扩展的实现案例

  • 在测试网络上部署 100 亿资产,支持所有元数据扩展的查找。这表明扩容不是问题。

# 参考资料

标准

  1. ERC-20 Token Standard.
  2. ERC-165 Standard Interface Detection.
  3. ERC-173 Owned Standard.
  4. ERC-223 (opens new window) Token Standard.
  5. ERC-677 (opens new window) transferAndCall Token Standard.
  6. ERC-827 (opens new window) Token Standard.
  7. Ethereum Name Service (ENS). https://ens.domains
  8. Instagram -- What's the Image Resolution? https://help.instagram.com/1631821640426723
  9. JSON Schema. https://json-schema.org/
  10. Multiaddr. https://github.com/multiformats/multiaddr
  11. RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt

Issues

  1. The Original ERC-721 Issue. https://github.com/ethereum/eips/issues/721
  2. Solidity Issue #2330 -- Interface Functions are External. https://github.com/ethereum/solidity/issues/2330
  3. Solidity Issue #3412 -- Implement Interface: Allow Stricter Mutability. https://github.com/ethereum/solidity/issues/3412
  4. Solidity Issue #3419 -- Interfaces Can't Inherit. https://github.com/ethereum/solidity/issues/3419
  5. Solidity Issue #3494 -- Compiler Incorrectly Reasons About the selector Function. https://github.com/ethereum/solidity/issues/3494
  6. Solidity Issue #3544 -- Cannot Calculate Selector of Function Named transfer. https://github.com/ethereum/solidity/issues/3544
  7. CryptoKitties Bounty Issue #4 -- Listing all Kitties Owned by a User is O(n^2). https://github.com/axiomzen/cryptokitties-bounty/issues/4
  8. OpenZeppelin Issue #438 -- Implementation of approve method violates ERC20 standard. https://github.com/OpenZeppelin/zeppelin-solidity/issues/438
  9. Solidity DelegateCallReturnValue Bug. https://solidity.readthedocs.io/en/develop/bugs.html#DelegateCallReturnValue

讨论

  1. Reddit (announcement of first live discussion). https://www.reddit.com/r/ethereum/comments/7r2ena/friday_119_live_discussion_on_erc_nonfungible/
  2. Gitter #EIPs (announcement of first live discussion). https://gitter.im/ethereum/EIPs?at=5a5f823fb48e8c3566f0a5e7
  3. ERC-721 (announcement of first live discussion). https://github.com/ethereum/eips/issues/721#issuecomment-358369377
  4. ETHDenver 2018. https://ethdenver.com

NFT 部署和其他项目

  1. CryptoKitties. https://www.cryptokitties.co
  2. 0xcert ERC-721 Token. https://github.com/0xcert/ethereum-erc721
  3. Su Squares. https://tenthousandsu.com
  4. Decentraland. https://decentraland.org
  5. CryptoPunks. https://www.larvalabs.com/cryptopunks
  6. DMarket. https://www.dmarket.io
  7. Enjin Coin. https://enjincoin.io
  8. Ubitquity. https://www.ubitquity.io
  9. Propy. https://tokensale.propy.com
  10. CryptoKitties Deployed Contract. https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code
  11. Su Squares Bug Bounty Program. https://github.com/fulldecent/su-squares-bounty
  12. XXXXERC721. https://github.com/fulldecent/erc721-example
  13. ERC721ExampleDeed. https://github.com/nastassiasachs/ERC721ExampleDeed
  14. Curio Cards. https://mycuriocards.com
  15. Rare Pepe. https://rarepepewallet.com
  16. Auctionhouse Asset Interface. https://github.com/dob/auctionhouse/blob/master/contracts/Asset.sol
  17. OpenZeppelin SafeERC20.sol Implementation. https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/SafeERC20.sol

# 版权

Copyright and related rights waived via CC0 (opens new window).

▲ Powered by Vercel