Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding more clarity in transfer rules and other things, altered accept/reject logic to one mentioned in github discussion thread and compatibility section mentioning 1155+721 hybrid, plus we are now at Solidity 0.5.8 and my email should match github. #1993

Merged
merged 2 commits into from
May 3, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 98 additions & 37 deletions EIPS/eip-1155.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
eip: 1155
title: ERC-1155 Multi Token Standard
author: Witek Radomski <witek@enjin.com>, Andrew Cooke <andrew@enjin.com>, Philippe Castonguay <pc@horizongames.net>, James Therien <james@enjin.com>, Eric Binet <eric@enjin.com>
author: Witek Radomski <witek@enjin.com>, Andrew Cooke <ac0dem0nk3y@gmail.com>, Philippe Castonguay <pc@horizongames.net>, James Therien <james@enjin.com>, Eric Binet <eric@enjin.com>
type: Standards Track
category: ERC
status: Draft
Expand Down Expand Up @@ -34,7 +34,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
**Smart contracts implementing the ERC-1155 standard MUST implement the `ERC1155` and `ERC165` interfaces.**

```solidity
pragma solidity ^0.5.7;
pragma solidity ^0.5.8;

/**
@title ERC-1155 Multi Token Standard
Expand Down Expand Up @@ -63,7 +63,7 @@ interface ERC1155 /* is ERC165 */ {
event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);

/**
@dev MUST emit when an approval is updated.
@dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absense of an event assumes disabled).
*/
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

Expand All @@ -77,34 +77,34 @@ interface ERC1155 /* is ERC165 */ {
/**
@notice Transfers value amount of an _id from the _from address to the _to address specified.
@dev MUST emit TransferSingle event on success.
Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
MUST throw if `_to` is the zero address.
MUST throw if balance of sender for token `_id` is lower than the `_value` sent.
MUST throw on any other error.
When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). If so, it MUST call `onERC1155Received` on `_to` and revert if the return value is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`.
Caller must be approved to manage the _from account's tokens (see "Approval" section of the standard).
MUST revert if `_to` is the zero address.
MUST revert if balance of sender for token `_id` is lower than the `_value` sent.
MUST revert on any other error.
After the transfer succeeds, this function MUST check if `_to` is a smart contract (eg. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
@param _from Source address
@param _to Target address
@param _id ID of the token type
@param _value Transfer amount
@param _data Additional data with no specified format, sent in call to `_to`
@param _data Additional data with no specified format, sent in call to `onERC1155Received` on `_to`
*/
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;

/**
@notice Send multiple types of Tokens from a 3rd party in one transfer (with safety call).
@dev MUST emit TransferBatch event on success.
Caller must be approved to manage the _from account's tokens (see isApprovedForAll).
MUST throw if `_to` is the zero address.
MUST throw if length of `_ids` is not the same as length of `_values`.
MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
MUST throw on any other error.
When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return value is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`.
Caller must be approved to manage the _from account's tokens (see "Approval" section of the standard).
MUST revert if `_to` is the zero address.
MUST revert if length of `_ids` is not the same as length of `_values`.
MUST revert if any of the balance of sender for token `_ids` is lower than the respective `_values` sent.
MUST revert on any other error.
Transfers and events MUST occur in the array order they were submitted (_ids[0] before _ids[1], etc).
After all the transfer(s) in the batch succeed, this function MUST check if `_to` is a smart contract (eg. code size > 0). If so, it MUST call `onERC1155BatchReceived` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
@param _from Source addresses
@param _to Target addresses
@param _ids IDs of each token type
@param _values Transfer amounts per token type
@param _data Additional data with no specified format, sent in call to `_to`
@param _ids IDs of each token type (order and length must match _values array)
@param _values Transfer amounts per token type (order and length must match _ids array)
@param _data Additional data with no specified format, sent in call to `onERC1155BatchReceived` on `_to`
*/
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;

Expand Down Expand Up @@ -144,58 +144,116 @@ interface ERC1155 /* is ERC165 */ {

### ERC-1155 Token Receiver

Smart contracts **MUST** implement this interface to accept transfers.
Smart contracts **MUST** implement this interface to accept transfers. See "Safe Transfer Rules" for further detail.

```solidity
pragma solidity ^0.5.7;
pragma solidity ^0.5.8;

interface ERC1155TokenReceiver {
/**
@notice Handle the receipt of a single ERC1155 token type.
@dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
This function MAY throw to revert and reject the transfer.
Return of other than the magic value MUST result in the transaction being reverted.
@dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
This function MUST return whether it accepts or rejects the transfer via the prescribed keccak256 generated values.
Return of any other value than the prescribed keccak256 generated values WILL result in the transaction being reverted.
Note: The contract address is always the message sender.
@param _operator The address which called the `safeTransferFrom` function
@param _operator The address which initiated the transfer (i.e. msg.sender)
@param _from The address which previously owned the token
@param _id The id of the token being transferred
@param _value The amount of tokens being transferred
@param _data Additional data with no specified format
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
@return `bytes4(keccak256("accept_erc1155_tokens()"))`==0x4dc21a2f or `bytes4(keccak256("reject_erc1155_tokens()"))`==0xafed434d
*/
function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);

/**
@notice Handle the receipt of multiple ERC1155 token types.
@dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
This function MAY throw to revert and reject the transfer.
Return of other than the magic value WILL result in the transaction being reverted.
@dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
This function MUST return whether it accepts or rejects the transfer via the prescribed keccak256 generated values.
Return of any other value than the prescribed keccak256 generated values WILL result in the transaction being reverted.
Note: The contract address is always the message sender.
@param _operator The address which called the `safeBatchTransferFrom` function
@param _operator The address which initiated the batch transfer (i.e. msg.sender)
@param _from The address which previously owned the token
@param _ids An array containing ids of each token being transferred
@param _values An array containing amounts of each token being transferred
@param _ids An array containing ids of each token being transferred (order and length must match _values array)
@param _values An array containing amounts of each token being transferred (order and length must match _ids array)
@param _data Additional data with no specified format
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
@return `bytes4(keccak256("accept_batch_erc1155_tokens()"))`==0xac007889 or `bytes4(keccak256("reject_erc1155_tokens()"))`==0xafed434d
*/
function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);
}
```

### Safe Transfer Rules

To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST operate, a list of rules follows:

* onERC1155Received and onERC1155BatchReceived MUST NOT be called on an EOA account.
* onERC1155Received and onERC1155BatchReceived MUST NOT be called outside of a mint or transfer process.

##### When the recipient is a contract:

* The onERC1155Received hook MUST be called every time one and only one token type is transferred to an address in the transaction.
* The onERC1155Received hook MUST NOT be called when more than one token type is transferred to an address in the transaction.
* The onERC1155BatchReceived hook MUST be called when more than one token type is transferred to an address in the transaction with the entire list of what was transferred to it.
* The onERC1155BatchReceived hook MUST NOT be called when only one token type is transferred to an address in the transaction.

* If implementation specific functions are used to transfer 1155 tokens to a contract the appropriate hook MUST still be called with the same rules as if safeTransferFrom/safeBatchTransferFrom was used.
* If the destination/to contract does not implement the appropriate hook the transfer MUST be reverted with the one caveat below.
- If the tokens being sent are part of a hybrid implementation of another standard, that particular standard's rules on sending to a contract MAY now be followed instead. See "Compatibility with other standards" section.

* When calling either onERC1155Received or onERC1155BatchReceived:
- operator MUST be the address of the account/contract that initiated the transfer (i.e. msg.sender).
- from MUST be the address of the holder whose balance is decreased.
- to MUST be the address of the recipient whose balance is increased.
- from MUST be 0x0 for a mint.
- data MUST contain the extra information provided by the sender (if any) for a transfer.
- the hook MUST be called after all the balances in the transaction have been updated to match the senders intent.

* When calling onERC1155Received
- id MUST be the token type being transferred.
- value MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
- If the return value is anything other than `bytes4(keccak256("accept_erc1155_tokens()"))` or `bytes4(keccack256("reject_erc1155_tokens()"))` the transaction MUST be reverted.

* When calling onERC1155BatchReceived
- ids MUST be the list of tokens being transferred.
- values MUST be the list of number of tokens (specified in ids) the holder balance is decreased by and match what the recipient balance is increased by.
- If the return value is anything other than `bytes4(keccak256("accept_batch_erc1155_tokens()"))` or `bytes4(keccack256("reject_erc1155_tokens()"))` the transaction MUST be reverted.

* The destination/to contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("accept_erc1155_tokens()"))` for onERC1155Received or `bytes4(keccak256("accept_batch_erc1155_tokens()"))` for onERC1155BatchReceived.
- If such explicit acceptance happens the transfer MUST be completed, unless other conditions apply.
* The destination/to contract MAY reject an increase of its balance by returning the rejection magic value `bytes4(keccack256("reject_erc1155_tokens()"))`.
- If such explicit rejection happens, the transfer MUST be reverted with the one caveat below.
- If the tokens being sent are part of a hybrid implementation of another standard, that particular standard's rules on sending to a contract MAY now be followed instead. See "Compatibility with other standards" section.

* A solidity example of the keccak256 generated constants for the return magic is:
- bytes4 constant public ERC1155_REJECTED = 0xafed434d; // keccak256("reject_erc1155_tokens()")
- bytes4 constant public ERC1155_ACCEPTED = 0x4dc21a2f; // keccak256("accept_erc1155_tokens()")
- bytes4 constant public ERC1155_BATCH_ACCEPTED = 0xac007889; // keccak256("accept_batch_erc1155_tokens()")

##### Compatibility with other standards

There have been requirements during the design discussions to have this standard be compatible with older standards when sending to contract addresses, specifically ERC721 at time of writing.
To cater for this there is some leeway with the rejection logic should a contract return `bytes4(keccack256("reject_erc1155_tokens()"))` from the call to onERC1155Received/onERC1155BatchReceived as detailed in the main "Safe Transfer Rules" section above.
In this case the hybrid implementation MAY now follow the secondary standard's rules when transferring token(s) to a contract address.

Note however it is recommended that a hybrid solution NOT be followed and a pure implementation of a single standard is followed instead, as a hybrid solution is an unproven method to date.

An example of a hybrid 1155+721 contract is linked in the references section under implementations.

### Metadata

The URI value allows for ID substitution by clients. If the string `{id}` exists in any URI, clients MUST replace this with the actual token ID in hexadecimal form. This allows for large number of tokens to use the same on-chain string by defining a URI once, for a large collection of tokens. Example of such a URI: `https://token-cdn-domain/{id}.json` would be replaced with `https://token-cdn-domain/780000000000001e000000000000000000000000000000000000000000000000.json` if the client is referring to token ID `780000000000001e000000000000000000000000000000000000000000000000`.
The URI value allows for ID substitution by clients. If the string `{id}` exists in any URI, clients MUST replace this with the actual token ID in hexadecimal form. This allows for large number of tokens to use the same on-chain string by defining a URI once, for a large collection of tokens. Example of such a URI: `https://token-cdn-domain/{id}.json` would be replaced with `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004CCE0.json` if the client is referring to token ID 314592/0x4CCE0.

The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix.
The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary.

#### Metadata Extensions

The following optional extensions can be identified with the (ERC-165 Standard Interface Detection)[https://eips.ethereum.org/EIPS/eip-165].

Changes to the URI MUST emit the `URI` event if the change can be expressed with an event. If the optional ERC1155Metadata_URI extension is included, the value returned by this function SHOULD be used to retrieve values for which no event was emitted. The function MUST return the same value as the event if it was emitted.
Changes to the URI MUST emit the `URI` event if the change can be expressed with an event (i.e. it isn't dynamic). If the optional ERC1155Metadata_URI extension is included, the 'uri' function SHOULD be used to retrieve values for which no event was emitted. The function MUST return the same value as the event if it was emitted.

```solidity
pragma solidity ^0.5.7;
pragma solidity ^0.5.8;

/**
Note: The ERC-165 identifier for this interface is 0x0e89341c.
Expand Down Expand Up @@ -277,7 +335,7 @@ An example of an ERC-1155 Metadata JSON file follows. The properties array propo

##### Localization

Metadata localization should be standardized to increase presentation uniformity accross all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content MAY be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software.
Metadata localization should be standardized to increase presentation uniformity across all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content MAY be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software.

##### JSON Schema

Expand Down Expand Up @@ -362,6 +420,9 @@ fr.json:
### Approval

The function `setApprovalForAll` allows an operator to manage one's entire set of tokens on behalf of the approver. To permit approval of a subset of token IDs, an interface such as [ERC-1761 Scoped Approval Interface (DRAFT)](https://eips.ethereum.org/EIPS/eip-1761) is suggested.
The counterpart `isAprrovedForAll` provides introspection into status set by `setApprovalForAll`.

An owner SHOULD be assumed to always be able to operate on their own tokens regardless of approval status, so should SHOULD NOT have to call `setApprovalForAll` to approve themselves as an operator before they can operate on them.

## Rationale

Expand Down