mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-28 14:40:32 +00:00
Harmonize this standard to results from the ETHDenver event
This commit is contained in:
parent
792bf0520f
commit
1a1295c903
357
EIPS/eip-721.md
357
EIPS/eip-721.md
@ -13,19 +13,25 @@ Requires: ERC-165
|
||||
|
||||
## Simple Summary
|
||||
|
||||
A standard interface for deeds, also known as non-fungible tokens.
|
||||
A standard interface for deeds. Deeds express ownership of *things*.
|
||||
|
||||
## Abstract
|
||||
|
||||
**This is a standard interface for smart contracts to handle deed ownership.** Deeds can represent ownership of physical property, like houses, or digital property, like unique pictures of kittens. 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.
|
||||
**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.
|
||||
|
||||
The scope of this interface includes **interrogating the smart contract about deed ownership**. It also **allows deed owners to transfer assets**. The authors considered uses cases of individual deed ownership as well as custody by third party brokers/wallets/auctioneers.
|
||||
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:
|
||||
|
||||
- Physical property — houses, unique artwork
|
||||
- Digital property — 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.
|
||||
|
||||
## 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 a *large* number of deeds. Additional applications are discussed below.
|
||||
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.
|
||||
|
||||
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 token 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 deed ownership because each deed is distinct (non-fungible) whereas each of a quantity of tokens is identical (fungible).
|
||||
|
||||
Differences between this standard and EIP-20 are examined below.
|
||||
|
||||
@ -33,167 +39,142 @@ Differences between this standard and EIP-20 are examined below.
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
|
||||
|
||||
The **baseline specification** is REQUIRED for all ERC-721 implementations (subject to "caveats", below).
|
||||
The **baseline specification** is REQUIRED for all ERC-721 implementations (see "caveats", below).
|
||||
|
||||
```solidity
|
||||
pragma solidity ^0.4.20;
|
||||
|
||||
import "./ERC165.sol";
|
||||
|
||||
/// @title Interface for contracts conforming to ERC-721: Deed Standard
|
||||
/// @author William Entriken (https://phor.net), et. al.
|
||||
/// @dev Specification at https://github.com/ethereum/eips/XXXFinalUrlXXX
|
||||
/// @title Required part of ERC-721 Deed Standard
|
||||
/// @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
|
||||
/// 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);
|
||||
|
||||
// COMPLIANCE WITH ERC-165 (DRAFT) /////////////////////////////////////////
|
||||
/// @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);
|
||||
|
||||
// bytes4 internal constant INTERFACE_SIGNATURE_ERC165 = // 0x01ffc9a7
|
||||
// this.supportsInterface.selector;
|
||||
/// @dev This emits when the "delegate operator" for an account is changed
|
||||
/// or reaffirmed. The zero address indicates there is no delegate
|
||||
/// operator.
|
||||
event Delegation(address indexed _owner, address indexed _delegate);
|
||||
|
||||
/// @dev ERC-165 (draft) interface signature for itself
|
||||
// bytes4 internal constant INTERFACE_SIGNATURE_ERC721 = // xxxxxx
|
||||
// this.ownerOf.selector ^
|
||||
// this.countOfDeeds.selector ^
|
||||
// this.countOfDeedsByOwner.selector ^
|
||||
// this.deedOfOwnerByIndex.selector ^
|
||||
// this.approve.selector ^
|
||||
// this.takeOwnership.selector ^
|
||||
// this.transfer.selector;
|
||||
|
||||
// PUBLIC QUERY FUNCTIONS //////////////////////////////////////////////////
|
||||
/// @notice Count all deeds assigned to an owner
|
||||
/// @dev Deeds 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
|
||||
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 about them do throw.
|
||||
/// @return The non-zero address of the owner of deed `_deedId`, or `throw`
|
||||
/// if deed `_deedId` is not tracked by this contract
|
||||
/// @dev Deeds 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 Count deeds tracked by this contract
|
||||
/// @return A count of valid deeds tracked by this contract, where each one of
|
||||
/// them has an assigned and queryable owner not equal to the zero address
|
||||
function countOfDeeds() external view returns (uint256 _count);
|
||||
|
||||
/// @notice Count all deeds assigned to an owner
|
||||
/// @dev Throws if `_owner` is the zero address, representing invalid deeds.
|
||||
/// Otherwise this function must not throw.
|
||||
/// @param _owner An address where we are interested in deeds owned by them
|
||||
/// @return The number of deeds owned by `_owner`, possibly zero
|
||||
function countOfDeedsByOwner(address _owner) external view returns (uint256 _count);
|
||||
|
||||
/// @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`,
|
||||
/// (sort order not specified)
|
||||
function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId);
|
||||
|
||||
// TRANSFER MECHANISM //////////////////////////////////////////////////////
|
||||
|
||||
/// @dev This event 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
|
||||
/// transfers may occur without emitting `Transfer`. At the time of any transfer,
|
||||
/// the "approved taker" is implicitly reset to the zero address.
|
||||
event Transfer(address indexed from, address indexed to, uint256 indexed deedId);
|
||||
|
||||
/// @dev The Approve event emits to log the "approved taker" for a deed -- whether
|
||||
/// set for the first time, reaffirmed by setting the same value, or setting to
|
||||
/// a new value. The "approved taker" is the zero address if nobody can take the
|
||||
/// deed now or it is an address if that address can call `takeOwnership` to attempt
|
||||
/// taking the deed. Any change to the "approved taker" for a deed SHALL cause
|
||||
/// Approve to emit. However, an exception, the Approve event will not emit when
|
||||
/// Transfer emits, this is because Transfer implicitly denotes the "approved taker"
|
||||
/// is reset to the zero address.
|
||||
event Approval(address indexed from, address indexed to, uint256 indexed deedId);
|
||||
|
||||
/// @notice Set the "approved taker" for your deed, or revoke approval by
|
||||
/// setting the zero address. You may `approve` any number of times while
|
||||
/// the deed is assigned to you, only the most recent approval matters. Emits
|
||||
/// an Approval event.
|
||||
/// @dev Throws if `msg.sender` does not own deed `_deedId` or if `_to` ==
|
||||
/// `msg.sender` or if `_deedId` is not a valid deed.
|
||||
/// @param _deedId The deed for which you are granting approval
|
||||
function approve(address _to, uint256 _deedId) external payable;
|
||||
|
||||
/// @notice Become owner of a deed for which you are currently approved
|
||||
/// @dev Throws if `msg.sender` is not approved to become the owner of
|
||||
/// `deedId` or if `msg.sender` currently owns `_deedId` or if `_deedId is not a
|
||||
/// valid deed.
|
||||
/// @param _deedId The deed that is being transferred
|
||||
function takeOwnership(uint256 _deedId) external payable;
|
||||
|
||||
/// @notice Set a new owner for your deed
|
||||
/// @dev Throws if `msg.sender` does not own deed `_deedId` or if `_to` ==
|
||||
/// `msg.sender` or if `_deedId` is not a valid deed.
|
||||
/// @param _deedId The deed that is being transferred
|
||||
/// @notice Set a new owner for a deed
|
||||
/// @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 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;
|
||||
|
||||
/// @notice Set or reaffirm the "delegate operator" for this account
|
||||
/// @dev The zero address indicates there is no delegate operator.
|
||||
/// @param _delegate The new delegate operator
|
||||
function delegate(address _delegate) external;
|
||||
|
||||
// CONFORMANCE TO ERC-165 (DRAFT) //////////////////////////////////////////
|
||||
|
||||
/// @notice Query if this 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);
|
||||
}
|
||||
```
|
||||
|
||||
Implementations MAY also choose to implement the **ERC-721 Metadata extension**. This is RECOMMENDED and will allow third party wallets to show your deeds in the best way. It will be a lot more flattering than
|
||||
|
||||
> You just received deed ID `0xb9d6a94f1ab16f461b2af06aebab7e1cfe10ada985da001f274f370fbb848a40` from contract `0x0dcd2f752394c41875e259e00bb44fd505297caf`!
|
||||
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*.
|
||||
|
||||
```solidity
|
||||
/// @title Metadata extension to ERC-721 interface
|
||||
/// @title Optional metadata extension to ERC-721 Deed Standard
|
||||
/// @author William Entriken (https://phor.net)
|
||||
/// @dev Specification at https://github.com/ethereum/eips/issues/XXXX
|
||||
interface ERC721Metadata {
|
||||
|
||||
/// @dev ERC-165 (draft) interface signature for ERC721
|
||||
// bytes4 internal constant INTERFACE_SIGNATURE_ERC721Metadata = // 0x2a786f11
|
||||
// this.name.selector ^
|
||||
// this.symbol.selector ^
|
||||
// this.deedUri.selector;
|
||||
|
||||
/// @notice A descriptive name for a collection of deeds managed by this
|
||||
/// contract
|
||||
/// @dev Wallets and exchanges MAY display this to the end user.
|
||||
/// @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
|
||||
function name() external pure returns (string _name);
|
||||
|
||||
/// @notice An abbreviated name for deeds managed by this contract
|
||||
/// @dev Wallets and exchanges MAY display this to the end user.
|
||||
/// @notice An abbreviated name for deeds in this contract
|
||||
function symbol() external pure returns (string _symbol);
|
||||
|
||||
/// @notice A distinct URI (RFC 3986) for a given deed.
|
||||
/// @dev If:
|
||||
/// * The URI is a URL
|
||||
/// * The URL is accessible
|
||||
/// * The URL points to a valid JSON file format (ECMA-404 2nd ed.)
|
||||
/// * The JSON base element is an object
|
||||
/// then these names of the base element SHALL have special meaning:
|
||||
/// * "name": A string identifying the item to which `_deedId` grants
|
||||
/// ownership
|
||||
/// * "description": A string detailing the item to which `_deedId` grants
|
||||
/// ownership
|
||||
/// * "image": A URI pointing to a file of image/* mime type representing
|
||||
/// the item to which `_deedId` grants ownership
|
||||
/// Wallets and exchanges MAY display this to the end user.
|
||||
/// Consider making any images at a width between 320 and 1080 pixels and
|
||||
/// aspect ratio between 1.91:1 and 4:5 inclusive.
|
||||
/// Throws if `_deedId` is not a valid deed.
|
||||
/// @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
|
||||
/// 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);
|
||||
}
|
||||
```
|
||||
|
||||
A second extension, the **ERC-721 Accountability Extension**, is OPTIONAL and allows your contract to make all deeds discoverable.
|
||||
This is the "ERC721 Metadata JSON Schema" referenced above. Learn more about [JSON schemas](http://json-schema.org/).
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Deed Metadata",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
"description": "Identifies the thing to which the deed grants ownership",
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
"description": "Describes the thing to which the deed 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.",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```solidity
|
||||
/// @title Enumeration extension to ERC-721 interface
|
||||
/// @title Optional enumeration extension to ERC-721 Deed Standard
|
||||
/// @author William Entriken (https://phor.net)
|
||||
/// @dev Specification at https://github.com/ethereum/eips/issues/XXXX
|
||||
interface ERC721Enumerable {
|
||||
|
||||
/// @dev ERC-165 (draft) interface signature for ERC721
|
||||
// bytes4 internal constant INTERFACE_SIGNATURE_ERC721Enumerable = // 0xa5e86824
|
||||
// this.deedByIndex.selector ^
|
||||
// this.countOfOwners.selector ^
|
||||
// this.ownerByIndex.selector;
|
||||
/// @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
|
||||
/// 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()`.
|
||||
@ -214,45 +195,33 @@ interface ERC721Enumerable {
|
||||
/// @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`,
|
||||
/// (sort order not specified)
|
||||
function deedOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256 _deedId);
|
||||
}
|
||||
```
|
||||
|
||||
Here is a also an optional **ERC-721 Operators Extension** for delegating access to your deeds. This is copied from the ERC-777 efforts. At this time DO NOT recommend this extension because of [a security considuration](https://github.com/ethereum/EIPs/issues/777#issuecomment-366182824) but we are putting it forth as a starting point for discussion.
|
||||
|
||||
```solidity
|
||||
/// WARNING: THIS INTERFACE IS TO PROMOTE DISCUSSION, WE DO NOT RECOMMEND YOU
|
||||
/// IMPLEMENT IT YET AS THERE ARE SECURITY CONSIDERATIONS
|
||||
/// @title Delegated operator extension to ERC-721 interface
|
||||
/// @author William Entriken (https://phor.net)
|
||||
/// @dev Specification at https://github.com/ethereum/eips/issues/XXXX
|
||||
interface ERC721Operators {
|
||||
|
||||
/// @dev ERC-165 (draft) interface signature for ERC721 ...
|
||||
|
||||
function authorizeOperator(address operator) external;
|
||||
function revokeOperator(address operator) external;
|
||||
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
|
||||
function operatorSend(address from, address to, uint256 _deedId) external;
|
||||
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
|
||||
event RevokedOperator(address indexed operator, address indexed tokenHolder);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Caveats
|
||||
|
||||
The 0.4.19 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 specification. A contract which complies with ERC-721 MUST also abide by the following:
|
||||
|
||||
- [Solidity issue #3412](https://github.com/ethereum/solidity/issues/3412): This interface includes 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.19 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 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): If a contract is not compliant with `ERC721` then it is not compliant with `ERC721Metadata` or `ERC721Enumerable`. 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 #3494](https://github.com/ethereum/solidity/issues/3494): Use of `this.*.selector` is marked as a warning by Solidity, a future version of Solidity will not mark this as an error.
|
||||
- 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.*
|
||||
|
||||
## 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 token must have its ownership individually and atomically tracked. Regardless of the nature of these items, 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 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.
|
||||
|
||||
**"Deed" word choice**
|
||||
|
||||
@ -260,27 +229,43 @@ 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 thing that the ERC-721 contract tracks is the *deed*, the place where you live is the *asset*. This standard is not concerned with the *collectable*, *item*, *thing*, or *asset* that you own -- we use these words interchangeably.
|
||||
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*.
|
||||
|
||||
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*
|
||||
|
||||
**Deed identifiers**
|
||||
|
||||
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 MUST 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 NFT, callers MUST NOT assume that ID numbers have any specific pattern to them, and should 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.
|
||||
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 MUST 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 MUST 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.
|
||||
|
||||
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 two-step process for transferring deeds inspired from [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md). This requires the sender to approve sending and the receiver to accept approval. In the original ERC-20, this caused a problem when `allowance` 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.
|
||||
ERC-721 standardizes a single transfer function `transfer` and two levels of indirection:
|
||||
|
||||
A careful reading of this standard's `approve`, `takeOwnership` and `transfer` functions also shows that *any* business reason may be used to deny transactions. 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). Use of the `transfer` function is controversial and is 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.
|
||||
- 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".
|
||||
|
||||
Creating of new deeds and destruction of deeds is not included in the specification. Your implementing contract may implement these by other means. Please see the `event` documentation for your responsibilities when creating or destroying deeds.
|
||||
This provides a powerful set of tools for broker or auction applications to quickly use a *large* number of deeds.
|
||||
|
||||
**Function mutability**
|
||||
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 functions `approve`, `takeOwnership` and `transfer` are payable. Yes, really. The standard contemplates a real estate ownership application where transfer of property will require paying tax and/or various fees (including, but not limited to mining fees and pre-set transference fees), which may be paid by the old and/or new owner in any pre-arranged amount. Such an application would be compliant under this specification. Note that your contract may be nonpayable (syntactic sugar for `require(msg.value == 0)`) and be compliant, see caveats.
|
||||
- **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).
|
||||
- **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`
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
*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.*
|
||||
|
||||
**ERC-165 interface**
|
||||
|
||||
@ -288,44 +273,54 @@ This specification includes a function `supportsInterface` which is standardized
|
||||
|
||||
We chose ERC-165 over competing standard ERC-820 because of maturity. We consider ERC-165 low risk. But ERC-820, as recently as two weeks ago, [discovered a complete show-stopping flaw](https://github.com/ethereum/EIPs/issues/820#issuecomment-362049573). ERC-820 may be a great option, but we deem dependency on that standard an unacceptable risk at this time.
|
||||
|
||||
**Gas and complexity**
|
||||
**Gas and complexity** (regarding the enumeration extension)
|
||||
|
||||
This specification contemplates contracts managing few and *many* deeds. Specifically, we note that a large contract, initially with `N` deeds owned by the contract owner, can be deployed with `O(1)` gas. All functions in the baseline and extension specifications can be implemented with `O(1)` gas and `O(Deed Count)` storage. We have an implementation with 1 billion deployed deeds on testnet.
|
||||
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.
|
||||
|
||||
**Accountability**
|
||||
[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/).
|
||||
|
||||
Minimally, only the functions `ownerOf`, `approve`, `takeOwnership` are necessary for a usable deed standard. But wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which deeds an owner owns.
|
||||
This illustration makes clear: the Deed Standard scales.
|
||||
|
||||
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`. This is why we include just the most useful accounting in the baseline standard and the full accounting in the extension.
|
||||
*Alternatives considered: remove the deed enumeration function if it requries a for-loop, return a Soldity array type from enumeration functions.*
|
||||
|
||||
**Metadata**
|
||||
**Privacy**
|
||||
|
||||
We have chosen to make `name` and `symbol` both optional. These are interesting properties but are of limited utility. We remind wallet application authors that `name` and `symbol` cannot be trusted, a contract can easily return a value which overlaps with an existing well-known contract — or they may return an empty string / unusable value.
|
||||
Wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which deeds 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`.
|
||||
|
||||
**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 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
**Operators**
|
||||
*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)*
|
||||
|
||||
[ERC-777 discusses "operators"](https://github.com/ethereum/EIPs/issues/777), entities that you may assign to control property for you. We have considered adding such a feature to this specification. We have considered against this because it should not be required — many contracts will not need this feature and such contracts have merit enough to be considered compliant with ERC-721. At this time, the operator model does not have much real-world experience. We expect that a future extension to this deed standard may introduce such functionality if warranted.
|
||||
|
||||
**Discussions**
|
||||
**Community consensus**
|
||||
|
||||
A significant amount of discussion occurred on [the original ERC-721](ERC-721 issue), additionally we held a live meeting [on Gitter](https://gitter.im/ethereum/ERCs?at=5a62259b5ade18be3998eec4) that had good representation and [was](https://www.reddit.com/r/ethereum/comments/7r2ena/friday_119_live_discussion_on_erc_nonfungible/) [well](https://gitter.im/ethereum/EIPs?at=5a5f823fb48e8c3566f0a5e7) [advertised](https://github.com/ethereum/eips/issues/721#issuecomment-358369377) in relevant communities. Thank you to the participants:
|
||||
|
||||
- [@ImAllInNow](https://github.com/imallinnow) Rob from DEC Gaming / Presenting Michigan Ethereum Meetup Feb 7
|
||||
- [@Arachnid](https://github.com/arachnid) Nick Johnson
|
||||
- [@jadhavajay](https://github.com/jadhavajay) Ajay Jadhav from AyanWorks
|
||||
- [@superphly](https://github.com/superphly) Cody Marx Bailey - XRAM Capital / Sharing at hackathon Jan 20
|
||||
- [@superphly](https://github.com/superphly) Cody Marx Bailey - XRAM Capital / Sharing at hackathon Jan 20 / UN Future of Finance Hackathon.
|
||||
|
||||
Cody, is presenting ERC-721 at the UN Future of Finance Hackathon.
|
||||
A second event was held at ETHDenver 2018 to discuss deed 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.
|
||||
|
||||
## Backwards Compatibility
|
||||
|
||||
This standard is inspired by the semantics of ERC-20, but can't be compatible with it due to the fundamental differences between tokens and deeds. However, we do note that a deed implementation is able to satisfy the read-only ERC-20 functionality by implementing the functions `totalSupply` and `balanceOf` as aliases of `countOfDeeds` and `countOfDeedsByOwner` respectively and by implementing `decimals` to return 0.
|
||||
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 January 2018:
|
||||
Example deeds 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".
|
||||
|
Loading…
x
Reference in New Issue
Block a user