Merge branch 'patch-2' into reconsider-deed-name

This commit is contained in:
William Entriken 2018-02-26 00:49:50 -05:00 committed by GitHub
commit 707cb55898
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 103 additions and 66 deletions

View File

@ -39,33 +39,30 @@ 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 (see "caveats", below).
**Every ERC-721 compliant contract must implement the `ERC721` and [`ERC165`](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md) interfaces** (subject to "caveats" below):
```solidity
pragma solidity ^0.4.20;
import "./ERC165.sol";
/// @title Required part of ERC-721 Distinguishable Assets Registry
/// @title Required part of ERC-721 Deed Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
/// Note: the ERC-165 identifier for this interface is 0xb3a99827
/// 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
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that assetis implicitly reset
/// to the zero address.
/// any transfer, the approved address for that asset (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 _assetId);
/// @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.
/// @dev This emits when the approved address for a single 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);
/// @dev This emits when a third party ("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 disable for an owner.
/// The operator may manage all assets of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all assets assigned to an owner
@ -82,47 +79,75 @@ interface ERC721 /* is ERC165 */ {
/// @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.
/// @notice Transfer ownership of a asset -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING DEEDS 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 asset to transfer
function transfer(address _to, uint256 _assetId) external payable;
/// @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 -- 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.
/// 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
/// @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;
/// @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 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
/// @notice Set or reaffirm the approved address for a asset
/// @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 authorized address
/// @param _assetId The asset to approve
/// @param _approved The new approved deed controller
/// @param _assetId The deed to approve
function approve(address _approved, uint256 _assetId) external payable;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all your asset.
/// @dev Emits the ApprovalForAll event
/// @param _operator Address to add to the set of authorized operators.
/// @param _approved True to appove an operators, false to revoke approval
function setApprovalForAll(address _operateor, boolean _approved) payable;
/// @param _approved True if the operators is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
// CONFORMANCE TO ERC-165 //////////////////////////////////////////////////
/// @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 Query if this implements an interface
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the deeds
/// @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);
}
interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
@ -132,7 +157,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 assets.
A wallet/broker/auction application MUST implement the **wallet interface** if it will accept safe transfers.
```solidity
interface ERC721TokenReceiver {
/// @notice Handle the receipt of a token
/// @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 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*.
```solidity
/// @title Optional metadata extension to ERC-721 Distinguishable Assets Registry
@ -176,7 +219,7 @@ This is the "ERC721 Metadata JSON Schema" referenced above. Learn more about [JS
}
```
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.
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.
```solidity
/// @title Optional enumeration extension to ERC-721 Distinguishable Assets Registry
@ -252,47 +295,39 @@ The choice of uint256 allows a wide variety of applications because UUIDs and sh
**Transfer mechanism**
ERC-721 standardizes a single transfer function `transferFrom`, one convenience function, and two mechanisms for indirection:
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 `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 of an asset
- The approved address of an assec
- An authorized operator of the current owner of an asset
- 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.
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.
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`, `transfer `, `approve` and `setApproveForAll` in other situations. This allows implementations to achieve interesting results:
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:
- **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 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 authorized address
- **Read only DAR** — always throw from `transfer`, `approve` and `delegate`
- **Blacklist certain address from receiving deeds** — prior art, [Crypto Kitties, (lines 565, 566)](https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code).
- **Disallow unsafe transfers** — `unsafeTransfer` throws unless `_to` equals `msg.sender` or `countOf(_to)` is non-zero (because such cases are safe)
- **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 address for the deed
- **Read only deed registry** — always throw from `unsafeTransfer`, `transferFrom`, `approve` and `setApprovalForAll`
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.
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.
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 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.
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.
*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**
This specification includes a function `supportsInterface` which is standardized in [ERC-165](https://github.com/ethereum/EIPs/pull/881). So any contract MAY query your contract to see if it complies with ERC-721 and the extensions.
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 contract to 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.
[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/).
[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/).
This illustration makes clear: the Distinguishable Assets Registry standard scales.
@ -306,13 +341,13 @@ It may be interesting to consider a use case where assets are not enumerable or
**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. Assets **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 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-777](https://github.com/ethereum/EIPs/issues/777), [ERC-827](https://github.com/ethereum/EIPs/issues/827)) included these functions.
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.
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.
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.
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.
@ -333,7 +368,7 @@ We have been very inclusive in this process and invite anyone with questions or
## Backwards Compatibility
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.
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 goal 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 DAR implementations as of February 2018:
@ -343,6 +378,8 @@ Example DAR implementations as of February 2018:
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).
The `onNFTReceived` function specifically works around old deployed contracts which may inadvertently return 1 (`true`) in certain circumstances even if they don't implement a function. By returning, and checking for, a magic value we are able to distinguish actual affirmative responses versus these `true`s.
## Test Cases
**TO DO**