Skip to content

Commit

Permalink
Update transfer naming and interface IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
fulldecent committed Feb 28, 2018
1 parent 89d0162 commit 2bddd12
Showing 1 changed file with 35 additions and 43 deletions.
78 changes: 35 additions & 43 deletions EIPS/eip-721.md
Expand Up @@ -46,8 +46,8 @@ pragma solidity ^0.4.20;
/// @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 */ {
/// Note: the ERC-165 identifier for this interface is 0x6466353c
interface ERC721 /* is ERC165 */ {
/// @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
Expand Down Expand Up @@ -79,38 +79,38 @@ interface ERC721 /* is ERC165 */ {
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId) external view returns (address);
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function 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
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onNFTReceived` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onNFTReceived(address,uint256,bytes)"))`.
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function transferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to []
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Set or reaffirm the approved address for an NFT
Expand All @@ -132,13 +132,13 @@ interface ERC721 /* is ERC165 */ {
/// @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);
function getApproved(uint256 _tokenId) external returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) returns (bool);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
interface ERC165 {
Expand All @@ -155,6 +155,7 @@ interface ERC165 {
A wallet/broker/auction application MUST implement the **wallet interface** if it will accept safe transfers.

```solidity
/// @dev Note: the ERC-165 identifier for this interface is 0xf0b9e5ba
interface ERC721TokenReceiver {
/// @notice Handle the receipt of an NFT
/// @dev The ERC721 smart contract calls this function on the recipient
Expand All @@ -165,8 +166,9 @@ interface ERC721TokenReceiver {
/// @param _from The sending address
/// @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);
/// @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(address _from, uint256 _tokenId, bytes data) external returns(bytes4);
}
```

Expand All @@ -175,7 +177,7 @@ The **metadata extension** is OPTIONAL for ERC-721 smart contracts (see "caveats
```solidity
/// @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 0xTODO_ADD_THIS
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f
interface ERC721Metadata /* is ERC721 */ {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external pure returns (string _name);
Expand Down Expand Up @@ -219,7 +221,7 @@ The **enumeration extension** is OPTIONAL for ERC-721 smart contracts (see "cave
```solidity
/// @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 0xTODO_ADD_THIS
/// Note: the ERC-165 identifier for this interface is 0x780e9d63
interface ERC721Enumerable /* is ERC721 */ {
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
Expand All @@ -233,16 +235,6 @@ interface ERC721Enumerable /* is ERC721 */ {
/// (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);
/// @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 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);
/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
/// `_owner` is the zero address, representing invalid NFTs.
Expand Down Expand Up @@ -283,27 +275,27 @@ The choice of `uint256` allows a wide variety of applications because UUIDs and

**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:
ERC-721 standardizes a safe transfer function `safeTransferFrom` (overloaded with and without a `bytes` parameter) and an unsafe function `transferFrom`. Transfers may be initiated by:

- 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 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:
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 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`
- **Blacklist certain address from receiving NFTs** — prior art, [Crypto Kitties, (lines 565, 566)](https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code).
- **Disallow unsafe transfers**`transferFrom` throws unless `_to` equals `msg.sender` or `countOf(_to)` is non-zero or was non-zero previously (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 any transfer function, require transfer parameter `_to` to equal `msg.sender`, require transfer parameter `_to` to be the approved address for the NFT
- **Read only NFT 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 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.
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 NFT 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 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.*
*Alternatives considered: only allow two-step ERC-20 style transaction, require that transfer functions never throw, require all functions to return a boolean indicating the success of the operation.*

**ERC-165 interface**

Expand Down Expand Up @@ -358,15 +350,15 @@ 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 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:
Example NFT 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 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* 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`)](http://solidity.readthedocs.io/en/develop/bugs.html#DelegateCallReturnValue) 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.
The `onERC721Received` function specifically works around old deployed contracts [which may inadvertently return 1 (`true`)](http://solidity.readthedocs.io/en/develop/bugs.html#DelegateCallReturnValue) 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

Expand Down

0 comments on commit 2bddd12

Please sign in to comment.