Skip to content

Commit

Permalink
Automatically merged updates to draft EIP(s) 1155 (#2083)
Browse files Browse the repository at this point in the history
Hi, I'm a bot! This change was automatically merged because:

 - It only modifies existing Draft or Last Call EIP(s)
 - The PR was approved or written by at least one author of each modified EIP
 - The build is passing
  • Loading branch information
AC0DEM0NK3Y authored and eip-automerger committed May 25, 2019
1 parent 9af2a9b commit 4b676ff
Showing 1 changed file with 51 additions and 27 deletions.
78 changes: 51 additions & 27 deletions EIPS/eip-1155.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,30 +160,30 @@ 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 MUST return `bytes4(keccak256("accept_erc1155_tokens()"))` (i.e. 0x4dc21a2f) if it accepts the transfer.
This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer.
This function MUST revert if it rejects the transfer.
Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
@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("accept_erc1155_tokens()"))`
@return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
*/
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 MUST return `bytes4(keccak256("accept_batch_erc1155_tokens()"))` (i.e. 0xac007889) if it accepts the transfer(s).
This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s).
This function MUST revert if it rejects the transfer(s).
Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
@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 (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("accept_batch_erc1155_tokens()"))`
@return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
*/
function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);
Expand All @@ -199,7 +199,7 @@ interface ERC1155TokenReceiver {

### Safe Transfer Rules

To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST operate with respect to the `ERC1155TokenReceiver` hook functions, a list of scenarios and rules follows.
To be more explicit about how the standard `safeTransferFrom` and `safeBatchTransferFrom` functions MUST operate with respect to the `ERC1155TokenReceiver` hook functions, a list of scenarios and rules follows.

#### Scenarios

Expand All @@ -219,18 +219,18 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
**_Scenario#5 :_** The receiver implements the necessary `ERC1155TokenReceiver` interface function(s) but throws an error.
* The transfer MUST be reverted.

**_Scenario#6 :_** The receiver implements the `ERC1155TokenReceiver` interface and is the recipient of one and only one balance change (e.g. safeTransferFrom called).
* All the balances in the transfer MUST have been updated to match the senders intent before any hook is called on a recipient.
* All the transfer events for the transfer MUST have been emitted to reflect the balance changes before any hook is called on a recipient.
* One of `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient.
**_Scenario#6 :_** The receiver implements the `ERC1155TokenReceiver` interface and is the recipient of one and only one balance change (e.g. `safeTransferFrom` called).
* The balances for the transfer MUST have been updated before the `ERC1155TokenReceiver` hook is called on a recipient contract.
* The transfer event MUST have been emitted to reflect the balance changes before the `ERC1155TokenReceiver` hook is called on the recipient contract.
* One of `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient contract.
* The `onERC1155Received` hook SHOULD be called on the recipient contract and its rules followed.
- See "onERC1155Received rules" for further rules that MUST be followed.
* The `onERC1155BatchReceived` hook MAY be called on the recipient contract and its rules followed.
- See "onERC1155BatchReceived rules" for further rules that MUST be followed.

**_Scenario#7 :_** The receiver implements the `ERC1155TokenReceiver` interface and is the recipient of more than one balance change (e.g. safeBatchTransferFrom called).
* All the balances in the transfer MUST have been updated to match the senders intent before any hook is called on a recipient.
* All the transfer events for the transfer MUST have been emitted to reflect the balance changes before any hook is called on a recipient.
**_Scenario#7 :_** The receiver implements the `ERC1155TokenReceiver` interface and is the recipient of more than one balance change (e.g. `safeBatchTransferFrom` called).
* All the balances in the transfer related to the next hook call MUST have been updated before that `ERC1155TokenReceiver` hook is called on a recipient contract.
* All transfer events MUST have been emitted to reflect current balance changes before an `ERC1155TokenReceiver` hook is called on the recipient contract.
* `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient as many times as necessary such that every balance change for the recipient in the scenario is accounted for.
- The return magic value for every hook call MUST be checked and acted upon as per "onERC1155Received rules" and "onERC1155BatchReceived rules".
* The `onERC1155BatchReceived` hook SHOULD be called on the recipient contract and its rules followed.
Expand All @@ -244,6 +244,14 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
* The `_data` argument MAY be re-purposed for the new context.
* If forwarding fails the transaction MAY be reverted.
- If the contract logic wishes to keep the ownership of the token(s) itself in this case it MAY do so.

**_Scenario#9 :_** You are transferring tokens via a non-standard api call i.e. an implementation specific api and NOT `safeTransferFrom` or `safeBatchTransferFrom`.
* In this scenario all balance updates and events outputs rules are the same as if a standard function had been called.
- i.e. an external viewer should still be able to query a balance via a function and it be identical to the balance as determined by `TransferSingle` and '`TransferBatch` events alone.
* If the receiver is a contract the `ERC1155TokenReceiver` hooks still need to be called on it and the return values respected the same as if a standard function had been called.
- However while the `safeTransferFrom` or `safeBatchTransferFrom` functions MUST revert if a receiving contract does not implement the `ERC1155TokenReceiver` interface, a non-standard function MAY proceed with the transfer.
- See "Implementation specific transfer api rules".


#### Rules

Expand Down Expand Up @@ -300,11 +308,11 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
* The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
* The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered.
- i.e. it MUST pass on the unaltered `_data` argument sent via the `safeTransferFrom` or `safeBatchTransferFrom` call for this transfer.
* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("accept_erc1155_tokens()"))`
- If the return value is `bytes4(keccak256("accept_erc1155_tokens()"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success.
* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
- If the return value is `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success.
* The recipient contract MAY reject an increase of its balance by calling revert.
- If recipient contract throws/reverts the transaction MUST be reverted.
* If the return value is anything other than `bytes4(keccak256("accept_erc1155_tokens()"))` the transaction MUST be reverted.
* If the return value is anything other than `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` the transaction MUST be reverted.
* `onERC1155Received` (and/or `onERC1155BatchReceived`) MAY be called multiple times in a single transaction and the following requirements must be met:
- All callbacks represent mutually exclusive balance changes.
- The set of all calls to `onERC1155Received` and `onERC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted.
Expand All @@ -317,11 +325,11 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
* The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in `_ids`) the holder balance is decreased by and match what the recipient balance is increased by.
* The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered.
- i.e. it MUST pass on the unaltered `_data` argument sent via the `safeBatchTransferFrom` call for this transfer.
* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("accept_batch_erc1155_tokens()"))`
- If the return value is `bytes4(keccak256("accept_batch_erc1155_tokens()"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success.
* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
- If the return value is `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success.
* The recipient contract MAY reject an increase of its balance by calling revert.
- If recipient contract throws/reverts the transaction MUST be reverted.
* If the return value is anything other than `bytes4(keccak256("accept_batch_erc1155_tokens()"))` the transaction MUST be reverted.
* If the return value is anything other than `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transaction MUST be reverted.
* `onERC1155BatchReceived` (and/or `onERC1155Received`) MAY be called multiple times in a single transaction and the following requirements must be met:
- All callbacks represent mutually exclusive balance changes.
- The set of all calls to `onERC1155Received` and `onERC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted.
Expand All @@ -338,26 +346,42 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
- It MUST NOT consume more than 5,000 gas.

**_Implementation specific transfer api rules:_**
* If implementation specific api functions are used to transfer 1155 tokens to a contract, the safeTransferFrom, or safeBatchTransferFrom (as appropriate) rules MUST be followed.
* If implementation specific api functions are used to transfer ERC-1155 tokens to a contract, the `safeTransferFrom`, or `safeBatchTransferFrom` (as appropriate) rules MUST still be followed if the receiver implements the `ERC1155TokenReceiver` interface. If not it is up to the implementation to revert or proceed.
* An example:
1. A approved user calls a function such as `function myTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values);`.
2. `myTransferFrom` updates the balances for `_from` and `_to` addresses for all `_ids` and `_values`.
3. `myTransferFrom` emits `TransferBatch` with the details of what was transferred from address `_from` to address `_to`.
4. `myTransferFrom` checks if `_to` is a contract address and determines that it is (if not, then the transfer can be considered successful).
5. `myTransferFrom` calls `onERC1155BatchReceived` on `_to` and it reverts or returns an unknown value (if it had returned `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transfer can be considered successful).
6. At this point `myTransferFrom` MAY decide to revert the transaction immediately as receipt of the tokens was not explicitly accepted by the `onERC1155BatchReceived` function.
7. If `myTransferFrom` wishes to continue it MUST call `isERC1155TokenReceiver()` on `_to` and if it returns `bytes4(keccak256("isERC1155TokenReceiver()"))` the transaction MUST be reverted, as it is a valid receiver and the previous step failed.
8. If the above call to `isERC1155TokenReceiver()` on `_to` reverts or returns an unknown value the `myTransferFrom` function MAY consider this transfer successful (__NOTE__: this MAY result in unrecoverable tokens if sent to an address that does not expect to receive ERC-1155 tokens).
* The above example is not exhaustive but illustrates the major points (and shows that most are shared with `safeTransferFrom` and `safeBatchTransferFrom`):
- Balances that are updated MUST have equivalent transfer events emitted.
- A receiver address has to be checked if it is a contract and if so relevant `ERC1155TokenReceiver` hook function(s) have to be called on it.
- Balances (and events associated) that are referenced in a call to a receiver hook MUST be updated (and emitted) before the `ERC1155TokenReceiver` hook is called.
- The return values of the `ERC1155TokenReceiver` hook functions that are called MUST be respected if they are implemented.
- Only in the case that a recipient contract does NOT implement the necessary `ERC1155TokenReceiver` hook functions can a non-standard implementation allow tokens to be sent to it. A standard function MUST always revert regardless (unless it is a hybrid implementation see "Compatibility with other standards").


###### A solidity example of the keccak256 generated constants for the return magic is:
- bytes4 constant public ERC1155_ACCEPTED = 0x4dc21a2f; // bytes4(keccak256("accept_erc1155_tokens()"))
- bytes4 constant public ERC1155_BATCH_ACCEPTED = 0xac007889; // bytes4(keccak256("accept_batch_erc1155_tokens()"))
- bytes4 constant public ERC1155_ACCEPTED = 0xf23a6e61; // bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))
- bytes4 constant public ERC1155_BATCH_ACCEPTED = 0xbc197c81; // bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))

#### Compatibility with other standards

There have been requirements during the design discussions to have this standard be compatible with existing standards when sending to contract addresses, specifically ERC-721 at time of writing.
To cater for this scenario, there is some leeway with the rejection logic should a contract not implement the `ERC1155TokenReceiver` as per "Safe Transfer Rules" section above, specifically "Scenario#3 : The receiver does not implement the necessary `ERC1155TokenReceiver` interface function(s)".

Hence in a hybrid 1155 contract implementation an extra call MUST be made on the recipient contract and checked before any hook calls to `onERC1155Received` or `onERC1155BatchReceived` are made.
Hence in a hybrid ERC-1155 contract implementation an extra call MUST be made on the recipient contract and checked before any hook calls to `onERC1155Received` or `onERC1155BatchReceived` are made.
Order of operation MUST therefore be:
1. The implementation MUST call the function `isERC1155TokenReceiver` on the recipient, providing at least 5,000 gas.
2. If the function call succeeds and the return value is `bytes4(keccak256("isERC1155TokenReceiver()"))` the implementation proceeds as a regular 1155 implementation, with the call(s) to the `onERC1155Received` or `onERC1155BatchReceived` hooks and rules associated.
3. If the function call fails or the return value is NOT `bytes4(keccak256("isERC1155TokenReceiver()"))` the implementation can assume the contract recipient is not an `ERC1155TokenReceiver` and follow its other standard's rules for transfers.
1. The implementation MUST call the function `isERC1155TokenReceiver` on the recipient contract, providing at least 5,000 gas.
2. If the function call succeeds and the return value is `bytes4(keccak256("isERC1155TokenReceiver()"))` the implementation proceeds as a regular ERC-1155 implementation, with the call(s) to the `onERC1155Received` or `onERC1155BatchReceived` hooks and rules associated.
3. If the function call fails or the return value is NOT `bytes4(keccak256("isERC1155TokenReceiver()"))` the implementation can assume the recipient contract is not an `ERC1155TokenReceiver` and follow its other standard's rules for transfers.

*__Note that a pure implementation of a single standard is recommended__* rather than a hybrid solution, but an example of a hybrid 1155+721 contract is linked in the references section under implementations.
*__Note that a pure implementation of a single standard is recommended__* rather than a hybrid solution, but an example of a hybrid ERC-1155/ERC-721 contract is linked in the references section under implementations.

An important consideration is that even if the tokens are sent with another standard's rules the *__1155 transfer events MUST still be emitted.__* This is so the balances can still be determined via events alone as per 1155 standard rules.
An important consideration is that even if the tokens are sent with another standard's rules the *__1155 transfer events MUST still be emitted.__* This is so the balances can still be determined via events alone as per ERC-1155 standard rules.


### Metadata
Expand Down

0 comments on commit 4b676ff

Please sign in to comment.