From 21e0d2472bc9c8b491457203128541e3a88f6bbb Mon Sep 17 00:00:00 2001 From: William Entriken Date: Fri, 23 Feb 2018 02:48:05 -0500 Subject: [PATCH 1/2] Rename to assets --- EIPS/eip-721.md | 272 ++++++++++++++++++++++++------------------------ 1 file changed, 138 insertions(+), 134 deletions(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index 9fc9fbe5aca269..0fd68f607c1467 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -2,7 +2,7 @@ ``` EIP: -Title: ERC-721 Deed Standard +Title: ERC-721 Distinguishable Assets Registry Author: William Entriken , Dieter Shirley , Jacob Evans , Nastassia Sachs Type: Standard Category ERC @@ -13,25 +13,25 @@ Requires: ERC-165 ## Simple Summary -A standard interface for deeds. Deeds express ownership of *things*. +A standard interface for any Distinguishable Assets Registry ("DAR"). A DAR tracks ownership of a set of assets that are mutually distinguishable. These assets may also be known as non-fungible tokens. ## Abstract -**This is a standard interface for smart contracts to handle deeds.** The scope of this interface includes **interrogating the smart contract about deeds**. It also allows **transferring deeds**. We consider use cases of deeds used by individuals as well as consignment to third party brokers/wallets/auctioneers. +Tracking the ownership of physical or digital distinguishable assets on a blockchain has a broad range of applications. We consider use cases of assets being owned and transacted by individuals as well as consignment to third party brokers/wallets/auctioneers ("operators"). -The *things* to which deeds express ownership are an implementation detail. While writing this standard we considered a diverse univerise of *things*, and we know you will dream up many more: +The kind of assets to which a DAR expresses ownership are an implementation detail. While writing this standard we considered a diverse univerise of assets, and we know you will dream up many more: - Physical property — houses, unique artwork -- Digital property — unique pictures of kittens, collectable cards +- Virtual collectables — unique pictures of kittens, collectable cards - "Negative value" assets — loans, burdens and other responsibilities -In general, all houses are distinct and no two kittens are alike. Therefore you must track each deed separately; it is insufficient to simply count the deeds you own. +In general, all houses are distinct and no two kittens are alike. Therefore you must track each asset separately; it is insufficient to simply count the assets you own. ## Motivation -A standard interface allows wallet/broker/auction applications to work with any deed on Ethereum. We provide for simple deed contracts as well as contracts that track an *arbitrarily large* number of deeds. Additional applications are discussed below. +A standard interface allows wallet/broker/auction applications to work with any DAR on Ethereum. We provide for simple DAR contracts as well as contracts that track an *arbitrarily large* number of assets. Additional applications are discussed below. -This standard is inspired by [the ERC-20 token standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) and builds on two years of experience since EIP-20 was created. EIP-20 is insufficient for tracking deed ownership because each deed is distinct (non-fungible) whereas each of a quantity of tokens is identical (fungible). +This standard is inspired by [the ERC-20 token standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) and builds on two years of experience since EIP-20 was created. EIP-20 is insufficient for tracking asset ownership because each asset is distinct (non-fungible) whereas each of a quantity of tokens is identical (fungible). Differences between this standard and EIP-20 are examined below. @@ -46,78 +46,79 @@ pragma solidity ^0.4.20; import "./ERC165.sol"; -/// @title Required part of ERC-721 Deed Standard +/// @title Required part of ERC-721 Distinguishable Assets Registry /// @author William Entriken (https://phor.net) /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md /// Note: the ERC-165 (DRAFT) identifier for this interface is 0xb3a99827 interface ERC721 /* is ERC165 */ { - /// @dev This emits when ownership of any deed changes by any mechanism. - /// This event emits when deeds are created (`from` == 0) and destroyed - /// (`to` == 0). Exception: during contract creation, any number of deeds + /// @dev This emits when ownership of any asset changes by any mechanism. + /// This event emits when assets are created (`from` == 0) and destroyed + /// (`to` == 0). Exception: during contract creation, any number of assets /// may be created and assigned without emitting Transfer. At the time of - /// any transfer, the "approved deed controller" is implicitly reset to the - /// zero address. - event Transfer(address indexed _from, address indexed _to, uint256 _deedId); + /// any transfer, the approved address for that assetis implicitly reset + /// to the zero address. + event Transfer(address indexed _from, address indexed _to, uint256 _assetId); - /// @dev This emits when the "approved deed controller" for a deed is - /// changed or reaffirmed. The zero address indicates there is no approved - /// deed controller. When a Transfer event emits, this also indicates the - /// approved deed controller (if any) is reset to none. - event Approval(address indexed _owner, address indexed _approved, uint256 _deedId); + /// @dev This emits when the approved address for an asset is changed or + /// reaffirmed. The zero address indicates there is no approved address for + /// that asset. When a Transfer event emits, this also indicates the + /// approved address is reset to none. + event Approval(address indexed _owner, address indexed _approved, uint256 _assetId); /// @dev This emits when a third party ("operator") is enabled or disable for - /// an owner. The operator may manage all deeds of the owner. + /// an owner. The operator may manage all assets of the owner. event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); - /// @notice Count all deeds assigned to an owner - /// @dev Deeds assigned to zero address are considered invalid, and this + /// @notice Count all assets assigned to an owner + /// @dev Assets assigned to zero address are considered invalid, and this /// function throws for queries about the zero address. /// @param _owner An address for whom to query the balance - /// @return The number of deeds owned by `_owner`, possibly zero + /// @return The number of assets owned by `_owner`, possibly zero function balanceOf(address _owner) external view returns (uint256 _balance); - /// @notice Find the owner of a deed - /// @param _deedId The identifier for a deed we are inspecting - /// @dev Deeds assigned to zero address are considered invalid, and queries + /// @notice Find the owner of an asset + /// @param _assetId The identifier for an asset we are inspecting + /// @dev Assets assigned to zero address are considered invalid, and queries /// about them do throw. - /// @return The address of the owner of the deed - function ownerOf(uint256 _deedId) external view returns (address _owner); - - /// @notice Transfers the ownership of a deed -- warning the caller is - /// responsible to confirm that the sender is capable of receiving deeds - /// otherwise the deed may become inaccessible! - /// @dev Throws unless `msg.sender` is the current deed owner, the "delegate - /// operator" of the current deed owner, or the "approved deed controller". - /// Throws if `_to` currently owns the deed. Throws if `_to` is the zero - /// address. - /// @param _to The new owner for the deed - /// @param _deedId The deed to transfer - function transfer(address _to, uint256 _deedId) external payable; - - /// @notice Transfers the ownership of a given deed from one address to - /// another address - /// @dev Throws unless `msg.sender` is the current deed owner, the "delegate - /// operator" of the current deed owner, or the "approved deed controller". - /// Throws if `_to` currently owns the deed. Throws if `_to` is the zero - /// address. Throws if the deed is not currently owned by _from. - /// @param _from The current owner for the deed - /// @param _to The new owner for the deed - /// @param _deedId The deed to transfer - function transferFrom(address _from, address _to, uint256 _deedId) external payable; - - /// @notice Set or reaffirm the "approved deed controller" for a deed - /// @dev The zero address indicates there is no approved deed controller. - /// @dev Throws unless `msg.sender` is the current deed owner, or the - /// "delegate operator" of the current deed owner. - /// @param _approved The new approved deed controller - /// @param _deedId The deed to approve - function approve(address _approved, uint256 _deedId) external payable; + /// @return The address of the owner of the asset + function ownerOf(uint256 _assetId) external view returns (address _owner); + + /// @notice Transfers the ownership of an asset -- warning the caller is + /// responsible to confirm that the sender is capable of receiving assets + /// otherwise the asset may become inaccessible! + /// @dev Throws unless `msg.sender` is the current asset owner, an operator + /// of the current asset owner, or the approved address of the asset. + /// Throws if `_to` currently owns the asset. + /// @param _to The new owner for the asset + /// @param _assetId The asset to transfer + function transfer(address _to, uint256 _assetId) external payable; + + /// @notice Transfers the ownership of a given asset from one address to + /// another address -- warning the caller is responsible to confirm that + /// the sender is capable of receiving assets otherwise the asset may become + /// inaccessible! + /// @dev Throws unless `msg.sender` is the current asset owner, an operator + /// of the current asset owner, or the approved address of the asset. + /// Throws if `_to` currently owns the asset. Throws if the asset is not + /// currently owned by _from. + /// @param _from The current owner for the asset + /// @param _to The new owner for the asset + /// @param _assetId The asset to transfer + function transferFrom(address _from, address _to, uint256 _assetId) external payable; + + /// @notice Set or reaffirm the authorized address for as asset + /// @dev The zero address indicates there is no authorized address. + /// @dev Throws unless `msg.sender` is the current asset owner, or an + /// operator of the current asset owner. + /// @param _approved The new authorized address + /// @param _assetId The asset to approve + function approve(address _approved, uint256 _assetId) external payable; /// @notice Enable or disable approval for a third party ("operator") to manage - /// all your deeds. + /// all your asset. /// @dev Emits the ApprovalForAll event /// @param _operator Address to add to the set of authorized operators. - /// @param _approved True if the operators is approved, false to revoke approval + /// @param _approved True to appove an operators, false to revoke approval function setApprovalForAll(address _operateor, boolean _approved) payable; // CONFORMANCE TO ERC-165 (DRAFT) ////////////////////////////////////////// @@ -132,25 +133,25 @@ interface ERC721 /* is ERC165 */ { } ``` -The **metadata extension** is OPTIONAL for ERC-721 implementations (see "caveats", below). This allows your contract to be interrogated for its name and for details about the *things*. +The **metadata extension** is OPTIONAL for ERC-721 implementations (see "caveats", below). This allows your contract to be interrogated for its name and for details about the assets. ```solidity -/// @title Optional metadata extension to ERC-721 Deed Standard +/// @title Optional metadata extension to ERC-721 Distinguishable Assets Registry /// @author William Entriken (https://phor.net) /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md /// Note: the ERC-165 (DRAFT) identifier for this interface is 0x2a786f11 interface ERC721Metadata /* is ERC721 */ { - /// @notice A descriptive name for a collection of deeds in this contract + /// @notice A descriptive name for a collection of assets in this contract function name() external pure returns (string _name); - /// @notice An abbreviated name for deeds in this contract + /// @notice An abbreviated name for assets in this contract function symbol() external pure returns (string _symbol); - /// @notice A distinct Uniform Resource Identifier (URI) for a given deed. - /// @dev Throws if `_deedId` is not a valid deed. URIs are defined in RFC + /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. + /// @dev Throws if `_assetId` is not a valid asset. URIs are defined in RFC /// 3986. The URI may point to a JSON file that conforms to the "ERC721 /// Metadata JSON Schema". - function deedUri(uint256 _deedId) external view returns (string _deedUri); + function assetURI(uint256 _assetId) external view returns (string _assetURI); } ``` @@ -158,67 +159,68 @@ This is the "ERC721 Metadata JSON Schema" referenced above. Learn more about [JS ```json { - "title": "Deed Metadata", + "title": "Asset Metadata", "type": "object", "properties": { "name": { "type": "string" - "description": "Identifies the thing to which the deed grants ownership", + "description": "Identifies the asset to which the DAR grants ownership", }, "description": { "type": "string" - "description": "Describes the thing to which the deed grants ownership", + "description": "Describes the asset to which the DAR grants ownership", }, "image": { "type": "string" - "description": "A URI pointing to a resource with mime type image/* representing the thing to which the deeds grants ownership. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which the DAR grants ownership. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.", } } } ``` -The **enumeration extension** is OPTIONAL for ERC-721 implementations (see "caveats", below). This allows your contract to publish the the full list of deeds and make them discoverable. +The **enumeration extension** is OPTIONAL for ERC-721 implementations (see "caveats", below). This allows your contract to publish the the full list of assets and make them discoverable. ```solidity -/// @title Optional enumeration extension to ERC-721 Deed Standard +/// @title Optional enumeration extension to ERC-721 Distinguishable Assets Registry /// @author William Entriken (https://phor.net) /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md /// Note: the ERC-165 (DRAFT) identifier for this interface is 0x5576ab6a interface ERC721Enumerable /* is ERC721 */ { - /// @notice Count deeds tracked by this contract - /// @return A count of valid deeds tracked by this contract, where each one of + /// @notice Count assets tracked by this contract + /// @return A count of valid assets 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 _count); - /// @notice Enumerate active deeds - /// @dev Throws if `_index` >= `countOfDeeds()`. - /// Otherwise must not throw. - /// @param _index A counter less than `countOfDeeds()` - /// @return The identifier for the `_index`th deed, (sort order not + /// @notice Enumerate active assets + /// @dev Throws if `_index` >= `totalSupply()`. + /// @param _index A counter less than `totalSupply()` + /// @return The identifier for the `_index`th asset, (sort order not /// specified) - function deedByIndex(uint256 _index) external view returns (uint256 _deedId); + function assetByIndex(uint256 _index) external view returns (uint256 _assetId); - /// @notice Count of owners which own at least one deed - /// Must not throw. - /// @return A count of the number of owners which own deeds + /// @notice Count of owners which own at least one asset + /// @return A count of the number of owners which own assets function countOfOwners() external view returns (uint256 _count); /// @notice Enumerate owners /// @dev Throws if `_index` >= `countOfOwners()` - /// Otherwise must not throw. /// @param _index A counter less than `countOfOwners()` /// @return The address of the `_index`th owner (sort order not specified) function ownerByIndex(uint256 _index) external view returns (address _owner); - /// @notice Enumerate deeds assigned to an owner - /// @dev Throws if `_index` >= `countOfDeedsByOwner(_owner)` or if - /// `_owner` is the zero address, representing invalid deeds. - /// Otherwise this must not throw. - /// @param _owner An address where we are interested in deeds owned by them - /// @param _index A counter less than `countOfDeedsByOwner(_owner)` - /// @return The identifier for the `_index`th deed assigned to `_owner`, + /// @notice Count assets of an owner + /// @param _owner The address to check + /// @return The number of valid assets owned by this owner + function countOfAssetsByOwner(address _owner) external view returns (uint256 _count); + + /// @notice Enumerate assets assigned to an owner + /// @dev Throws if `_index` >= `countOfAssetsByOwner(_owner)` or if + /// `_owner` is the zero address, representing invalid assets. + /// @param _owner An address where we are interested in assets owned by them + /// @param _index A counter less than `countOfAssetsByOwner(_owner)` + /// @return The identifier for the `_index`th asset assigned to `_owner`, /// (sort order not specified) - function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId); + function assetOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _assetId); } ``` @@ -226,8 +228,8 @@ interface ERC721Enumerable /* is ERC721 */ { The 0.4.20 Solidity interface grammar is not expressive enough to document the ERC-721 specification. A contract which complies with ERC-721 MUST also abide by the following: -- [Solidity issue #3412](https://github.com/ethereum/solidity/issues/3412): The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation must meet the mutability guarantee in this interface or you may meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.20 is that you can edit this interface to add stricter mutability before inheriting from your contract. -- [Solidity issue #3419](https://github.com/ethereum/solidity/issues/3419): A contract that implements `ERC721Metadata` or `ERC721Enumerable` SHALL also implement `ERC721`. sERC-721 implements the requirements of interface [ERC-165](https://github.com/ethereum/EIPs/pull/881). +- [Solidity issue #3412](https://github.com/ethereum/solidity/issues/3412): The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation must meet the mutability guarantee in this interface or you may meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. Workraound: until Solidity fixes this issue, you can edit this interface to add stricter mutability before inheriting from your contract. +- [Solidity issue #3419](https://github.com/ethereum/solidity/issues/3419): A contract that implements `ERC721Metadata` or `ERC721Enumerable` SHALL also implement `ERC721`. ERC-721 implements the requirements of interface [ERC-165](https://github.com/ethereum/EIPs/pull/881). - [Solidity issue #2330](https://github.com/ethereum/solidity/issues/2330): If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. - Solidity issues [#3494](https://github.com/ethereum/solidity/issues/3494), [#3544](https://github.com/ethereum/solidity/issues/3544): Use of `this.*.selector` is marked as a warning by Solidity, a future version of Solidity will not mark this as an error. @@ -235,49 +237,51 @@ The 0.4.20 Solidity interface grammar is not expressive enough to document the E ## Rationale -There are many proposed uses of Ethereum smart contracts that depend on tracking individual deeds (non-fungible tokens). Examples of existing or planned NFTs are LAND in [Decentraland](https://decentraland.org/), the eponymous punks in [CryptoPunks](https://www.larvalabs.com/cryptopunks), and in-game items using systems like [Dmarket](https://www.dmarket.io/) or [EnjinCoin](https://enjincoin.io/). Future uses include tracking real-world non-fungible assets, like real-estate (as envisioned by companies like [Ubitquity](https://www.ubitquity.io/) or [Propy](https://tokensale.propy.com/)). It is critical in each of these cases that these items are not "lumped together" as numbers in a ledger, but instead, each *thing* must have its ownership individually and atomically tracked. Regardless of the nature of these *things*, the ecosystem will be stronger if we have a standardized interface that allows for cross-functional deed management and sales platforms. +There are many proposed uses of Ethereum smart contracts that depend on tracking distinguishable assets. Examples of existing or planned NFTs are LAND in [Decentraland](https://decentraland.org/), the eponymous punks in [CryptoPunks](https://www.larvalabs.com/cryptopunks), and in-game items using systems like [Dmarket](https://www.dmarket.io/) or [EnjinCoin](https://enjincoin.io/). Future uses include tracking real-world assets, like real-estate (as envisioned by companies like [Ubitquity](https://www.ubitquity.io/) or [Propy](https://tokensale.propy.com/)). It is critical in each of these cases that these items are not "lumped together" as numbers in a ledger, but instead, each asset must have its ownership individually and atomically tracked. Regardless of the nature of these assets, the ecosystem will be stronger if we have a standardized interface that allows for cross-functional asset management and sales platforms. -**"Deed" word choice** - -The noun deed is defined in the Oxford Dictionary as: - -> A legal document that is signed and delivered, especially one regarding the ownership of property or legal rights. - -This definition is consistent with the fact that ERC-721 contracts track ownership of other *things*, such as houses, pictures of kittens or collectable cards. If you gain ownership of a house via ERC-721 then the ERC-721 contract manages the *deed* and the place where you live is the *thing*. +**"Asset" word choice** We chose specifically to avoid the word "token" because it has a well known, and different, meaning in the Ethereum ecosystem, specifically ERC-20 tokens. -*Alternatives considered: non-fungible token, title, token, asset, equity, ticket* +The word "asset" well describes all the use cases we envisioned above. -**Deed identifiers** +*Alternatives considered: non-fungible token, title, token, asset, equity, ticket, deed* -The basis of this standard is that every deed is identified by a unique 256-bit unsigned integer within its tracking contract. This ID number SHALL NOT change for the life of the contract. The pair `(contract address, deed ID)` will then be a globally unique and fully-qualified identifier for a specific deed within the Ethereum ecosystem. While some contracts may find it convenient to start with ID 0 and simply increment by one for each new deed, callers SHALL NOT assume that ID numbers have any specific pattern to them, and MUST treat the ID as a "black box". Also note that a deeds MAY become invalid (be destroyed). Please see the enumerations functions for a supported enumeration interface. +**Asset identifiers** + +The basis of this standard is that every asset is identified by a unique 256-bit unsigned integer within its DAR. This ID number SHALL NOT change for the life of the contract. The pair `(contract address, uint265 assetId)` will then be a globally unique and fully-qualified identifier for a specific asset within the Ethereum ecosystem. While some contracts may find it convenient to start with ID 0 and simply increment by one for each new asset, callers SHALL NOT assume that ID numbers have any specific pattern to them, and MUST treat the ID as a "black box". Also note that a assets MAY become invalid (be destroyed). Please see the enumerations functions for a supported enumeration interface. The choice of uint256 allows a wide variety of applications because UUIDs and sha3 hashes are directly convertible to uint256. **Transfer mechanism** -ERC-721 standardizes a single transfer function `transfer` and two levels of indirection: +ERC-721 standardizes a single transfer function `transferFrom`, one convenience function, and two mechanisms for indirection: + +* The `transfer` function is equivalent to `transferFrom` except that the `_from` is calculated automatically — note that if a specific asset has an approved address then `transfer` has a race condition + 1. A and B approve operator C + 2. A transfers to B + 3. C uses `transfer` to get the asset + 4. Depending on the order of 2. and 3. getting mined, it is ambiguous whom C gets the asset from -- The owner can transfer a deed to any address (except the zero address). -- The "approved deed controller" (if any) can transfer just like the owner. -- The "delegate operator" can transfer just like the owner and can assign the "approved deed controller". +- The owner can transfer an asset to any address (except the zero address). +- The authorized address for an asset can transfer just like the owner. +- An operator can transfer just like the owner and can assign the authorized address. -This provides a powerful set of tools for broker or auction applications to quickly use a *large* number of deeds. +This provides a powerful set of tools for broker or auction applications to quickly use a *large* number of assets. -The `transfer` documentation only specifies conditions when the transaction MUST throw. Your implementation MAY also throw on calls `transfer`, `approve` and `delegate` in other situations. This allows implementations to achieve interesting results: +The `transfer` documentation only specifies conditions when the transaction MUST throw. Your implementation MAY also throw on calls `transfer`, `transfer `, `approve` and `setApproveForAll` in other situations. This allows implementations to achieve interesting results: - **Disallow transfers if the contract is paused** — prior art, [Crypto Kitties](https://github.com/axiomzen/cryptokitties-bounty/blob/master/contracts/KittyOwnership.sol#L79) -- **Blacklist certain address from receiving deeds** — prior art, [Crypto Kitties, (lines 565, 566)](https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code). +- **Blacklist certain address from receiving assets** — prior art, [Crypto Kitties, (lines 565, 566)](https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code). - **Require every transaction to use the the approve-transfer workflow** — require `transfer` parameter `_to` to equal `msg.sender` -- **Charge a fee to both parties of a transaction** — require payment when calling `approve` with a non-zero `_approved` if it was previously the zero address, refund payment if calling `approve` with the zero address if it was previously a non-zero address, require payment when calling `transfer`, require `transfer` parameter `_to` to equal `msg.sender`, require `transfer` parameter `_to` to be the approved deed controller -- **Read only deed registry** — always throw from `transfer`, `approve` and `delegate` +- **Charge a fee to both parties of a transaction** — require payment when calling `approve` with a non-zero `_approved` if it was previously the zero address, refund payment if calling `approve` with the zero address if it was previously a non-zero address, require payment when calling `transfer`, require `transfer` parameter `_to` to equal `msg.sender`, require `transfer` parameter `_to` to be the authorized address +- **Read only DAR** — always throw from `transfer`, `approve` and `delegate` -Failed transactions will throw, a best practice identified in [ERC-233](https://github.com/ethereum/EIPs/issues/223) , [ERC-677](https://github.com/ethereum/EIPs/issues/677), [ERC-827](https://github.com/ethereum/EIPs/issues/827) and [OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/SafeERC20.sol). [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) defined an `allowance` feature, this caused a problem when was called and then later modified to a different amount, as [disucssed on OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/issues/438). In this deed standard, there is no allowance because every deed is unique, the quantity is none or one. Therefore we receive the benefits of ERC-20's original design without problems that have been later discovered. +Failed transactions will throw, a best practice identified in [ERC-233](https://github.com/ethereum/EIPs/issues/223) , [ERC-677](https://github.com/ethereum/EIPs/issues/677), [ERC-827](https://github.com/ethereum/EIPs/issues/827) and [OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/SafeERC20.sol). [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) defined an `allowance` feature, this caused a problem when was called and then later modified to a different amount, as [disucssed on OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/issues/438). In this standard, there is no allowance because every asset is unique, the quantity is none or one. Therefore we receive the benefits of ERC-20's original design without problems that have been later discovered. -Use of the `transfer` function where the caller is the owner of the deed is controversial as discussed in [ERC-233](https://github.com/ethereum/EIPs/issues/223) and [ERC-677](https://github.com/ethereum/EIPs/issues/677). But we take the position that misuse of the `transfer` function is an implementation mistake not a standards mistake. +Use of the `transfer` function where the caller is the owner of the asset is controversial as discussed in [ERC-233](https://github.com/ethereum/EIPs/issues/223) and [ERC-677](https://github.com/ethereum/EIPs/issues/677). But we take the position that misuse of the `transfer` function is an implementation mistake not a standards mistake. -Creating of new deeds and destruction of deeds is not included in the specification. Your contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying deeds. +Creating of new assets and destruction of assets is not included in the specification. Your contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying assets. *Alternatives considered: only allow two-step ERC-20 style transaction, require that `transfer` never throw, require all functions to return a boolean indicating the success of the operation.* @@ -289,33 +293,33 @@ We chose ERC-165 over competing standard ERC-820 because of maturity. We conside **Gas and complexity** (regarding the enumeration extension) -This specification contemplates implementations that manage a few and *arbitrarily large* numbers of deeds. If your application is able to grow then [avoid using for/while loops in your code](https://github.com/axiomzen/cryptokitties-bounty/issues/4). These indicate your contract may be unable to scale and gas costs will rise over time without bound. +This specification contemplates implementations that manage a few and *arbitrarily large* numbers of assets. If your application is able to grow then [avoid using for/while loops in your code](https://github.com/axiomzen/cryptokitties-bounty/issues/4). These indicate your contract may be unable to scale and gas costs will rise over time without bound. -[We have deployed](https://github.com/fulldecent/erc721-example) a contract to test net which instantiates and tracks 340282366920938463463374607431768211456 different deeds (2^128). That's enough to assign every IPV6 address to an Ethereum account owner, or to track ownership of nanobots a few micron in size and in aggregate totalling half the size of Earth. And you can query it from the blockchain. And every function takes less gas than [querying the ENS](https://ens.domains/). +[We have deployed](https://github.com/fulldecent/erc721-example) a contract to test net which instantiates and tracks 340282366920938463463374607431768211456 different assets (2^128). That's enough to assign every IPV6 address to an Ethereum account owner, or to track ownership of nanobots a few micron in size and in aggregate totalling half the size of Earth. And you can query it from the blockchain. And every function takes less gas than [querying the ENS](https://ens.domains/). -This illustration makes clear: the Deed Standard scales. +This illustration makes clear: the Distinguishable Assets Registry standard scales. -*Alternatives considered: remove the deed enumeration function if it requries a for-loop, return a Soldity array type from enumeration functions.* +*Alternatives considered: remove the asset enumeration function if it requries a for-loop, return a Soldity array type from enumeration functions.* **Privacy** -Wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which deeds an owner owns. +Wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which assets an owner owns. -It may be interesting to consider a use case where deeds are not enumerable or deeds are not enumerable by owner, such as a private registry of property ownership, or a partially-private registry. However, privacy cannot be attained because an attacker can simply (!) call `ownerOf` for every possible `deedId`. +It may be interesting to consider a use case where assets are not enumerable or assets are not enumerable by owner, such as a private registry of property ownership, or a partially-private registry. However, privacy cannot be attained because an attacker can simply (!) call `ownerOf` for every possible `assetId`. **Metadata choices** (metadata extension) -We have required `name` and `symbol` functions in the metadata extension. Every token EIP and draft we have reviewed ([ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md), [ERC-233](https://github.com/ethereum/EIPs/issues/223) , [ERC-677](https://github.com/ethereum/EIPs/issues/677), [ERC-827](https://github.com/ethereum/EIPs/issues/827)) included these functions. Deeds **are not** tokens, but let's adopt existing conventions when it makes sense! +We have required `name` and `symbol` functions in the metadata extension. Every token EIP and draft we have reviewed ([ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md), [ERC-233](https://github.com/ethereum/EIPs/issues/223) , [ERC-677](https://github.com/ethereum/EIPs/issues/677), [ERC-827](https://github.com/ethereum/EIPs/issues/827)) included these functions. Assets **are not** tokens, but let's adopt existing conventions when it makes sense! We remind implementation authors that the empty string is a valid response to `name` and `symbol` if you protest to the usage of this mechanism. We also remind everyone that the official contract for tracking 0xProject tokens (`ZRX`) is [0xe41d2489571d322189246dafa5ebde1f4699f498](https://etherscan.io/address/0xe41d2489571d322189246dafa5ebde1f4699f498). Another contract that advertises a name of `0xProject` and symbol `ZRX` is not the well-known (canonical) contract. -How a client may determine which token and deed contracts are well-known is outside the scope of this standard. +How a client may determine which DARs are well-known is outside the scope of this standard. -A mechanism is provided to associate deeds with URIs. We expect that many implementations will take advantage of this to provide metadata for each deed. The image size recommendation is taken from Instagram, they probably know much about image usability. The URI MAY be mutable (i.e. it changes from time to time). We considered a deed representing ownership of a real-world asset, in this case metadata about such an asset may naturally change. +A mechanism is provided to associate assets with URIs. We expect that many implementations will take advantage of this to provide metadata for each asset. The image size recommendation is taken from Instagram, they probably know much about image usability. The URI MAY be mutable (i.e. it changes from time to time). We considered a asset representing ownership of a real-world asset, in this case metadata about such an asset may naturally change. Metadata is returned as a string value. Currently this is only usable as calling from `web3`, not from other contracts. This is acceptable because we have not considered a use case where an on-blockchain application would query such information. -*Alternatives considered: put all metadata for each deed on the blockchain (too expensive), use URL templates to query metadata parts (URL templates do not work with all URL schemes, especially P2P URLs), [multiaddr network address](https://github.com/multiformats/multiaddr) (not mature enough)* +*Alternatives considered: put all metadata for each asset on the blockchain (too expensive), use URL templates to query metadata parts (URL templates do not work with all URL schemes, especially P2P URLs), [multiaddr network address](https://github.com/multiformats/multiaddr) (not mature enough)* **Community consensus** @@ -326,7 +330,7 @@ A significant amount of discussion occurred on [the original ERC-721](ERC-721 is - [@jadhavajay](https://github.com/jadhavajay) Ajay Jadhav from AyanWorks - [@superphly](https://github.com/superphly) Cody Marx Bailey - XRAM Capital / Sharing at hackathon Jan 20 / UN Future of Finance Hackathon. -A second event was held at ETHDenver 2018 to discuss deed standards (notes to be published). +A second event was held at ETHDenver 2018 to discuss distinguishable asset standards (notes to be published). We have been very inclusive in this process and invite anyone with questions or contributions into our discussion. However, this standard is written only to support the identified use cases which are listed herein. @@ -334,13 +338,13 @@ We have been very inclusive in this process and invite anyone with questions or We have adopted `balanceOf`, `totalSupply`, `name` and `symbol` semantics from the [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) specification. An implementation may also include a function `decimals` that returns `uint8 0` if its intention is to be more compatible with ERC-20 while supporting this standard. However, we find it contrived to require all ERC-721 implementations to support the `decimals` function. -Example deeds implementations as of February 2018: +Example DAR implementations as of February 2018: - [CryptoKitties](https://www.cryptokitties.co/) — Compatible with an earlier version of this standard. -- [CryptoPunks](https://www.larvalabs.com/cryptopunks) — Partially ERC-20 compatible, but not easily generalizable because it includes auction functionality directly in the contract and uses function names that explicitly refer to the deeds as "punks". +- [CryptoPunks](https://www.larvalabs.com/cryptopunks) — Partially ERC-20 compatible, but not easily generalizable because it includes auction functionality directly in the contract and uses function names that explicitly refer to the assets as "punks". - [Auctionhouse Asset Interface](https://github.com/dob/auctionhouse/blob/master/contracts/Asset.sol) — [@dob](https://github.com/dob) needed a generic interface for his Auctionhouse dapp (currently ice-boxed). His "Asset" contract is very simple, but is missing ERC-20 compatibility, `approve()` functionality, and metadata. This effort is referenced in the discussion for [EIP-173](https://github.com/ethereum/EIPs/issues/173). -Note: "Limited edition, collectible tokens" like [Curio Cards](https://mycuriocards.com/) and [Rare Pepe](https://rarepepewallet.com/) are *not* deeds. They're actually a collection of individual fungible tokens, each of which is tracked by its own smart contract with its own total supply (which may be `1` in extreme cases). +Note: "Limited edition, collectible tokens" like [Curio Cards](https://mycuriocards.com/) and [Rare Pepe](https://rarepepewallet.com/) are *not* distinguishable assets. They're actually a collection of individual fungible tokens, each of which is tracked by its own smart contract with its own total supply (which may be `1` in extreme cases). ## Test Cases @@ -356,7 +360,7 @@ ERC721ExampleDeed, by Nastassia Sachs: https://github.com/nastassiasachs/ERC721E XXXXERC721, by William Entriken: https://github.com/fulldecent/erc721-example -- Deployed on testnet with 1 billion deeds and supporting all lookups with the metadata extension. This demonstrates that scaling is NOT a problem. +- Deployed on testnet with 1 billion assets and supporting all lookups with the metadata extension. This demonstrates that scaling is NOT a problem. ## Copyright From 223e46c7ce39de62c54bb6a2f9d1e630d3aff079 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Mon, 26 Feb 2018 02:05:54 -0500 Subject: [PATCH 2/2] More draft wording changes --- EIPS/eip-721.md | 269 +++++++++++++++++++++++------------------------- 1 file changed, 130 insertions(+), 139 deletions(-) diff --git a/EIPS/eip-721.md b/EIPS/eip-721.md index e6f64102085d1e..19d811df56e5d2 100644 --- a/EIPS/eip-721.md +++ b/EIPS/eip-721.md @@ -2,7 +2,7 @@ ``` EIP: -Title: ERC-721 Distinguishable Assets Registry +Title: ERC-721 Non-Fungible Token Standard Author: William Entriken , Dieter Shirley , Jacob Evans , Nastassia Sachs Type: Standard Category ERC @@ -13,25 +13,25 @@ Requires: ERC-165 ## Simple Summary -A standard interface for any Distinguishable Assets Registry ("DAR"). A DAR tracks ownership of a set of assets that are mutually distinguishable. These assets may also be known as non-fungible tokens. +A standard interface for non-fungible tokens, also known as deeds. ## Abstract -Tracking the ownership of physical or digital distinguishable assets on a blockchain has a broad range of applications. We consider use cases of assets being owned and transacted by individuals as well as consignment to third party brokers/wallets/auctioneers ("operators"). +The following standard allows for the implementation of a standard API for NFTs within smart contracts. This standard provides basic functionality to track and transfer NFTs. -The kind of assets to which a DAR expresses ownership are an implementation detail. While writing this standard we considered a diverse univerise of assets, and we know you will dream up many more: +We considered use cases of NFTs being owned and transacted by individuals as well as consignment to third party brokers/wallets/auctioneers ("operators"). NFTs can represent ownership over digital or physical assets. We considered a diverse universe of assets, and we know you will dream up many more: - Physical property — houses, unique artwork - Virtual collectables — unique pictures of kittens, collectable cards - "Negative value" assets — loans, burdens and other responsibilities -In general, all houses are distinct and no two kittens are alike. Therefore you must track each asset separately; it is insufficient to simply count the assets you own. +In general, all houses are distinct and no two kittens are alike. NFTs are *distinguishable* and you must track the ownership of each one separately. ## Motivation -A standard interface allows wallet/broker/auction applications to work with any DAR on Ethereum. We provide for simple DAR contracts as well as contracts that track an *arbitrarily large* number of assets. Additional applications are discussed below. +A standard interface allows wallet/broker/auction applications to work with any NFT on Ethereum. We provide for simple ERC-721 smart contracts as well as contracts that track an *arbitrarily large* number of NFTs. Additional applications are discussed below. -This standard is inspired by [the ERC-20 token standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) and builds on two years of experience since EIP-20 was created. EIP-20 is insufficient for tracking asset ownership because each asset is distinct (non-fungible) whereas each of a quantity of tokens is identical (fungible). +This standard is inspired by [the ERC-20 token standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) and builds on two years of experience since EIP-20 was created. EIP-20 is insufficient for tracking NFTs because each asset is distinct (non-fungible) whereas each of a quantity of tokens is identical (fungible). Differences between this standard and EIP-20 are examined below. @@ -44,87 +44,85 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S ```solidity pragma solidity ^0.4.20; -/// @title Required part of ERC-721 Deed Standard +/// @title ERC-721 Non-Fungible Token Standard /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md /// Note: the ERC-165 identifier for this interface is 0xTODO_FILL_IN interface ERC721 /* is ERC165 */ { - /// @dev This emits when ownership of any asset changes by any mechanism. - /// This event emits when assets are created (`from` == 0) and destroyed - /// (`to` == 0). Exception: during contract creation, any number of assets + /// @dev This emits when ownership of any NFT changes by any mechanism. + /// This event emits when NFTs are created (`from` == 0) and destroyed + /// (`to` == 0). Exception: during contract creation, any number of NFTs /// may be created and assigned without emitting Transfer. At the time of - /// any transfer, the approved address for that asset (if any) is reset to none. - event Transfer(address indexed _from, address indexed _to, uint256 _assetId); + /// any transfer, the approved address for that NFT (if any) is reset to none. + event Transfer(address indexed _from, address indexed _to, uint256 _tokenId); - /// @dev This emits when the approved address for a single is changed or + /// @dev This emits when the approved address for an NFT is changed or /// reaffirmed. The zero address indicates there is no approved address. /// When a Transfer event emits, this also indicates that the approved - /// address for that asset (if any) is reset to none. - event Approval(address indexed _owner, address indexed _approved, uint256 _assetId); + /// address for that NFT (if any) is reset to none. + event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId); - /// @dev This emits when an operator is enabled or disable for an owner. - /// The operator may manage all assets of the owner. + /// @dev This emits when an operator is enabled or disabled for an owner. + /// The operator can manage all NFTs of the owner. event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); - /// @notice Count all assets assigned to an owner - /// @dev Assets assigned to zero address are considered invalid, and this + /// @notice Count all NFTs assigned to an owner + /// @dev NFTs assigned to the zero address are considered invalid, and this /// function throws for queries about the zero address. /// @param _owner An address for whom to query the balance - /// @return The number of assets owned by `_owner`, possibly zero - function balanceOf(address _owner) external view returns (uint256 _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 asset - /// @param _assetId The identifier for an asset we are inspecting - /// @dev Assets assigned to zero address are considered invalid, and queries + /// @notice Find the owner of an NFT + /// @param _tokenId The identifier for an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries /// about them do throw. - /// @return The address of the owner of the asset - function ownerOf(uint256 _assetId) external view returns (address _owner); + /// @return The address of the owner of the NFT + function ownerOf(uint256 _tokenId) external view returns (address); - /// @notice Transfer ownership of a asset -- THE CALLER IS RESPONSIBLE - /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING DEEDS OR ELSE + /// @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 asset owner, an authorized - /// operator, or the approved address for this asset. Throws if `_from` is - /// not the current owner of the asset. Throws if `_to` is the zero address. - /// Throws if `_deedId` is not a valid asset. - /// @param _from The new owner for the asset - /// @param _to The new owner for the asset - /// @param _assetId The deed to transfer - function unsafeTransfer(address _from, address _to, uint256 _assetId) external payable; - - /// @notice Transfers the ownership of a given asset from one address to - /// another address - /// @dev Throws unless `msg.sender` is the current asset owner, an authorized - /// operator, or the approved address for this asset. Throws if `_from` is - /// not the current owner of the asset. Throws if `_to` is the zero address. - /// Throws if `_assetId` is not a valid asset. When transfer is complete, - /// this function also calls `onNFTReceived` on `_to` and throws if the return - /// value is not `keccak256("ERC721_ONNFTRECEIVED")`. - /// @param _from The current owner for the asset - /// @param _to The new owner for the asset - /// @param _assetId The deed to transfer + /// @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 unsafeTransfer(address _from, address _to, uint256 _tokenId) external payable; + + /// @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 also + /// calls `onNFTReceived` on `_to` and throws if the return value is not + /// `keccak256("ERC721_ONNFTRECEIVED")`. + /// @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 transferFrom(address _from, address _to, uint256 _assetId, bytes[] data) external payable; + function transferFrom(address _from, address _to, uint256 _tokenId, bytes[] data) external payable; - /// @notice Transfers the ownership of a given asset from one address to - /// another address - /// @dev Throws unless `msg.sender` is the current asset owner, an authorized - /// operator, or the approved address for this asset. Throws if `_from` is - /// not the current owner of the asset. Throws if `_to` is the zero address. - /// Throws if `_assetId` is not a valid asset. When transfer is complete, - /// this function also calls `onNFTReceived` on `_to` and throws if the return - /// value is not `keccak256("ERC721_ONNFTRECEIVED")`. - /// @param _from The current owner for the asset - /// @param _to The new owner for the asset - /// @param _assetId The asset to transfer - function transferFrom(address _from, address _to, uint256 _assetId) external payable; - - /// @notice Set or reaffirm the approved address for a asset + /// @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 also + /// calls `onNFTReceived` on `_to` and throws if the return value is not + /// `keccak256("ERC721_ONNFTRECEIVED")`. + /// @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 Set or reaffirm the approved address for an NFT /// @dev The zero address indicates there is no approved address. - /// @dev Throws unless `msg.sender` is the current deed owner, or an authorized - /// operator of the current asset owner. - /// @param _approved The new approved deed controller - /// @param _assetId The deed to approve - function approve(address _approved, uint256 _assetId) external payable; + /// @dev 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 your asset. @@ -133,14 +131,14 @@ interface ERC721 /* is ERC165 */ { /// @param _approved True if the operators is approved, false to revoke approval function setApprovalForAll(address _operator, bool _approved) external; - /// @notice Get the approved address for a single deed - /// @dev Throws if `_deedId` is not a valid deed - /// @param _deedId The deed to find the approved address for - /// @return The approved address for this deed, or the zero address if there is none - function getApproved(uint256 _deedId) returns (address); + /// @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) returns (address); /// @notice Query if an address is an authorized operator for another address - /// @param _owner The address that owns the deeds + /// @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) returns (bool); @@ -161,38 +159,38 @@ A wallet/broker/auction application MUST implement the **wallet interface** if i ```solidity interface ERC721TokenReceiver { - /// @notice Handle the receipt of a token + /// @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. This function MUST use 50,000 gas or less. Return of other /// than the magic value MUST result in the transaction being reverted. /// Note: the contract address is always the message sender. /// @param _from The sending address - /// @param _tokenId The deed identifier which was sent + /// @param _tokenId The NFT identifier which is being transfered /// @param data Additional data with no specified format /// @return Always returns `keccak256("ERC721_ONNFTRECEIVED")`, unless throwing function onNFTReceived(address _from, uint256 _tokenId, bytes data) external returns(bytes4); } ``` -The **metadata extension** is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your contract to be interrogated for its name and for details about the *things*. +The **metadata extension** is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent. ```solidity -/// @title Optional metadata extension to ERC-721 Distinguishable Assets Registry +/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// Note: the ERC-165 identifier for this interface is 0x2a786f11 +/// Note: the ERC-165 identifier for this interface is 0xTODO_ADD_THIS interface ERC721Metadata /* is ERC721 */ { - /// @notice A descriptive name for a collection of assets in this contract + /// @notice A descriptive name for a collection of NFTs in this contract function name() external pure returns (string _name); - /// @notice An abbreviated name for assets in this contract + /// @notice An abbreviated name for NFTs in this contract function symbol() external pure returns (string _symbol); /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. - /// @dev Throws if `_assetId` is not a valid asset. URIs are defined in RFC + /// @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 assetURI(uint256 _assetId) external view returns (string _assetURI); + function tokenURI(uint256 _tokenId) external view returns (string); } ``` @@ -205,72 +203,67 @@ This is the "ERC721 Metadata JSON Schema" referenced above. Learn more about [JS "properties": { "name": { "type": "string", - "description": "Identifies the asset to which the DAR grants ownership", + "description": "Identifies the asset to which this NFT represents", }, "description": { "type": "string", - "description": "Describes the asset to which the DAR grants ownership", + "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 the DAR grants ownership. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.", + "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.", } } } ``` -The **enumeration extension** is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your contract to publish the the full list of assets and make them discoverable. +The **enumeration extension** is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your contract to publish its full list of NFTs and make them discoverable. ```solidity -/// @title Optional enumeration extension to ERC-721 Distinguishable Assets Registry +/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -/// Note: the ERC-165 identifier for this interface is 0x5576ab6a +/// Note: the ERC-165 identifier for this interface is 0xTODO_ADD_THIS interface ERC721Enumerable /* is ERC721 */ { - /// @notice Count assets tracked by this contract - /// @return A count of valid assets tracked by this contract, where each one of + /// @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 _count); + function totalSupply() external view returns (uint256); - /// @notice Enumerate active assets + /// @notice Enumerate valid NFTs /// @dev Throws if `_index` >= `totalSupply()`. /// @param _index A counter less than `totalSupply()` - /// @return The identifier for the `_index`th asset, (sort order not - /// specified) - function assetByIndex(uint256 _index) external view returns (uint256 _assetId); + /// @return The token identifier for the `_index`th NFT, + /// (sort order not specified) + function tokenByIndex(uint256 _index) external view returns (uint256); - /// @notice Count of owners which own at least one asset - /// @return A count of the number of owners which own assets - function countOfOwners() external view returns (uint256 _count); + /// @notice Count of owners which own at least one NFT + /// @return A count of the number of owners which own NFTs + function countOfOwners() external view returns (uint256); - /// @notice Enumerate owners + /// @notice Enumerate owners which own at least one NFT /// @dev Throws if `_index` >= `countOfOwners()` /// @param _index A counter less than `countOfOwners()` /// @return The address of the `_index`th owner (sort order not specified) - function ownerByIndex(uint256 _index) external view returns (address _owner); - - /// @notice Count assets of an owner - /// @param _owner The address to check - /// @return The number of valid assets owned by this owner - function countOfAssetsByOwner(address _owner) external view returns (uint256 _count); - - /// @notice Enumerate assets assigned to an owner - /// @dev Throws if `_index` >= `countOfAssetsByOwner(_owner)` or if - /// `_owner` is the zero address, representing invalid assets. - /// @param _owner An address where we are interested in assets owned by them - /// @param _index A counter less than `countOfAssetsByOwner(_owner)` - /// @return The identifier for the `_index`th asset assigned to `_owner`, + function ownerByIndex(uint256 _index) external view returns (address); + + /// @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 assetOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _assetId); + function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _tokenId); } ``` ### Caveats -The 0.4.20 Solidity interface grammar is not expressive enough to document the ERC-721 specification. A contract which complies with ERC-721 MUST also abide by the following: +The 0.4.20 Solidity interface grammar is not expressive enough to document the ERC-721 standard. A contract which complies with ERC-721 MUST also abide by the following: -- [Solidity issue #3412](https://github.com/ethereum/solidity/issues/3412): The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation must meet the mutability guarantee in this interface or you may meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.20 is that you can edit this interface to add stricter mutability before inheriting from your contract. +- [Solidity issue #3412](https://github.com/ethereum/solidity/issues/3412): The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.20 is that you can edit this interface to add stricter mutability before inheriting from your contract. - [Solidity issue #3419](https://github.com/ethereum/solidity/issues/3419): A contract that implements `ERC721Metadata` or `ERC721Enumerable` SHALL also implement `ERC721`. ERC-721 implements the requirements of interface [ERC-165](https://github.com/ethereum/EIPs/pull/881). -- [Solidity issue #2330](https://github.com/ethereum/solidity/issues/2330): If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. +- [Solidity issue #2330](https://github.com/ethereum/solidity/issues/2330): If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. As a workaround for version 0.4.20, you can edit this interface to switch to `public` before inheriting from your contract. - Solidity issues [#3494](https://github.com/ethereum/solidity/issues/3494), [#3544](https://github.com/ethereum/solidity/issues/3544): Use of `this.*.selector` is marked as a warning by Solidity, a future version of Solidity will not mark this as an error. *If a newer version of Solidity allows the caveats to be expressed in code, then this EIP MAY be updated and the caveats removed, such will be equivalent to the original specification.* @@ -279,29 +272,27 @@ The 0.4.20 Solidity interface grammar is not expressive enough to document the E There are many proposed uses of Ethereum smart contracts that depend on tracking distinguishable assets. Examples of existing or planned NFTs are LAND in [Decentraland](https://decentraland.org/), the eponymous punks in [CryptoPunks](https://www.larvalabs.com/cryptopunks), and in-game items using systems like [Dmarket](https://www.dmarket.io/) or [EnjinCoin](https://enjincoin.io/). Future uses include tracking real-world assets, like real-estate (as envisioned by companies like [Ubitquity](https://www.ubitquity.io/) or [Propy](https://tokensale.propy.com/)). It is critical in each of these cases that these items are not "lumped together" as numbers in a ledger, but instead, each asset must have its ownership individually and atomically tracked. Regardless of the nature of these assets, the ecosystem will be stronger if we have a standardized interface that allows for cross-functional asset management and sales platforms. -**"Asset" word choice** +**"NFT" word choice** -We chose specifically to avoid the word "token" because it has a well known, and different, meaning in the Ethereum ecosystem, specifically ERC-20 tokens. +"NFT" was satisfactory to nearly everyone surveyed and is widely applicable to a broad universe of distinguishable digital assets. We recongize that "deed" is very descriptive for certain applications of this standard (notably, physical property). -The word "asset" well describes all the use cases we envisioned above. +*Alternatives considered: distinguishable asset, title, token, asset, equity, ticket* -*Alternatives considered: non-fungible token, title, token, asset, equity, ticket, deed* +**NFT identifiers** -**Asset identifiers** +Every NFT is identified by a unique `uint265` ID inside the ERC-721 smart contract. This identifing number SHALL NOT change for the life of the contract. The pair `(contract address, uint265 tokenId)` will then be a globally unique and fully-qualified identifier for a specific asset on an Ethereum chain. While some ERC-721 smart contracts may find it convenient to start with ID 0 and simply increment by one for each new NFT, callers SHALL NOT assume that ID numbers have any specific pattern to them, and MUST treat the ID as a "black box". Also note that a NFTs MAY become invalid (be destroyed). Please see the enumerations functions for a supported enumeration interface. -The basis of this standard is that every asset is identified by a unique 256-bit unsigned integer within its DAR. This ID number SHALL NOT change for the life of the contract. The pair `(contract address, uint265 assetId)` will then be a globally unique and fully-qualified identifier for a specific asset within the Ethereum ecosystem. While some contracts may find it convenient to start with ID 0 and simply increment by one for each new asset, callers SHALL NOT assume that ID numbers have any specific pattern to them, and MUST treat the ID as a "black box". Also note that a assets MAY become invalid (be destroyed). Please see the enumerations functions for a supported enumeration interface. - -The choice of uint256 allows a wide variety of applications because UUIDs and sha3 hashes are directly convertible to uint256. +The choice of `uint256` allows a wide variety of applications because UUIDs and sha3 hashes are directly convertible to `uint256`. **Transfer mechanism** ERC-721 standardizes a safe transfer function `transferFrom` (overloaded with and without a `bytes` parameter) and an unsafe function `unsafeTransfer`. Transfers may be initiated by: -- The owner of an asset -- The approved address of an assec -- An authorized operator of the current owner of an asset +- The owner of an NFT +- The approved address of an NFT +- An authorized operator of the current owner of an NFT -Additionally, an authorized operator may set the approved address for an asset. This provides a powerful set of tools for wallet, broker or auction applications to quickly use a *large* number of deeds. +Additionally, an authorized operator may set the approved address for an NFT. This provides a powerful set of tools for wallet, broker and auction applications to quickly use a *large* number of NFTs. The transfer and accept functions documentation only specify conditions when the transaction MUST throw. Your implementation MAY also throw in other situations. This allows implementations to achieve interesting results: @@ -313,7 +304,7 @@ The transfer and accept functions documentation only specify conditions when the Failed transactions will throw, a best practice identified in [ERC-233](https://github.com/ethereum/EIPs/issues/223) , [ERC-677](https://github.com/ethereum/EIPs/issues/677), [ERC-827](https://github.com/ethereum/EIPs/issues/827) and [OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/SafeERC20.sol). [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) defined an `allowance` feature, this caused a problem when called and then later modified to a different amount, as [disucssed on OpenZeppelin](https://github.com/OpenZeppelin/zeppelin-solidity/issues/438). In ERC-721, there is no allowance because every deed is unique, the quantity is none or one. Therefore we receive the benefits of ERC-20's original design without problems that have been later discovered. -Creating of new deeds ("minting") and destruction of deeds ("burning") is not included in the specification. Your contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying deeds. +Creating of NFTs ("minting") and destruction NFTs ("burning") is not included in the specification. Your contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying NFTs. *Alternatives considered: only allow two-step ERC-20 style transaction, require that `transfer` never throw, require all functions to return a boolean indicating the success of the operation.* @@ -321,23 +312,23 @@ Creating of new deeds ("minting") and destruction of deeds ("burning") is not in We chose Standard Interface Detection [ERC-165](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) to expose the interfaces that a ERC-721 smart contract supports. -A future EIP may create a global registry of interfaces for contracts. We strongly support such an EIP and it would allow your contract to to implement `ERC721Enumerable`, `ERC721Metadata`, or other interfaces by delegating to a separate contract. +A future EIP may create a global registry of interfaces for contracts. We strongly support such an EIP and it would allow your ERC-721 implementation to implement `ERC721Enumerable`, `ERC721Metadata`, or other interfaces by delegating to a separate contract. **Gas and complexity** (regarding the enumeration extension) -This specification contemplates implementations that manage a few and *arbitrarily large* numbers of assets. If your application is able to grow then [avoid using for/while loops in your code](https://github.com/axiomzen/cryptokitties-bounty/issues/4). These indicate your contract may be unable to scale and gas costs will rise over time without bound. +This specification contemplates implementations that manage a few and *arbitrarily large* numbers of NFTs. If your application is able to grow then [avoid using for/while loops in your code](https://github.com/axiomzen/cryptokitties-bounty/issues/4). These indicate your contract may be unable to scale and gas costs will rise over time without bound. -[We have deployed](https://github.com/fulldecent/erc721-example) a contract to test net which instantiates and tracks 340282366920938463463374607431768211456 different deeds (2^128). That's enough to assign every IPV6 address to an Ethereum account owner, or to track ownership of nanobots a few micron in size and in aggregate totalling half the size of Earth. You can query it from the blockchain. And every function takes less gas than [querying the ENS](https://ens.domains/). +[We have deployed](https://github.com/fulldecent/erc721-example) a contract to Testnet which instantiates and tracks 340282366920938463463374607431768211456 different deeds (2^128). That's enough to assign every IPV6 address to an Ethereum account owner, or to track ownership of nanobots a few micron in size and in aggregate totalling half the size of Earth. You can query it from the blockchain. And every function takes less gas than [querying the ENS](https://ens.domains/). -This illustration makes clear: the Distinguishable Assets Registry standard scales. +This illustration makes clear: the ERC-721 standard scales. *Alternatives considered: remove the asset enumeration function if it requries a for-loop, return a Soldity array type from enumeration functions.* **Privacy** -Wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which assets an owner owns. +Wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which NFTs an owner owns. -It may be interesting to consider a use case where assets are not enumerable or assets are not enumerable by owner, such as a private registry of property ownership, or a partially-private registry. However, privacy cannot be attained because an attacker can simply (!) call `ownerOf` for every possible `assetId`. +It may be interesting to consider a use case where NFTs are not enumerable, such as a private registry of property ownership, or a partially-private registry. However, privacy cannot be attained because an attacker can simply (!) call `ownerOf` for every possible `tokenId`. **Metadata choices** (metadata extension) @@ -345,9 +336,9 @@ We have required `name` and `symbol` functions in the metadata extension. Every We remind implementation authors that the empty string is a valid response to `name` and `symbol` if you protest to the usage of this mechanism. We also remind everyone that the official contract for tracking 0xProject tokens (`ZRX`) is [0xe41d2489571d322189246dafa5ebde1f4699f498](https://etherscan.io/address/0xe41d2489571d322189246dafa5ebde1f4699f498). Another contract that advertises a name of `0xProject` and symbol `ZRX` is not the well-known (canonical) contract. -How a client may determine which DARs are well-known is outside the scope of this standard. +How a client may determine which ERC-721 smart contracts are well-known is outside the scope of this standard. -A mechanism is provided to associate deeds with URIs. We expect that many implementations will take advantage of this to provide metadata for each deed. The image size recommendation is taken from Instagram, they probably know much about image usability. The URI MAY be mutable (i.e. it changes from time to time). We considered a deed representing ownership of a house, in this case metadata about the house (image, occupants, etc.) may naturally change. +A mechanism is provided to associate NFTs with URIs. We expect that many implementations will take advantage of this to provide metadata for each NFT. The image size recommendation is taken from Instagram, they probably know much about image usability. The URI MAY be mutable (i.e. it changes from time to time). We considered an NFT representing ownership of a house, in this case metadata about the house (image, occupants, etc.) can naturally change. Metadata is returned as a string value. Currently this is only usable as calling from `web3`, not from other contracts. This is acceptable because we have not considered a use case where an on-blockchain application would query such information.