From 193fdeb56f8f7721651efa8dfcfc1e7bf50b2dec Mon Sep 17 00:00:00 2001 From: Andrew Cooke Date: Fri, 7 Jun 2019 06:07:51 -0400 Subject: [PATCH] Automatically merged updates to draft EIP(s) 1155 (#2108) 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 --- EIPS/eip-1155.md | 59 +++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/EIPS/eip-1155.md b/EIPS/eip-1155.md index 977c9f48c5c5bc..24c76699e7101d 100644 --- a/EIPS/eip-1155.md +++ b/EIPS/eip-1155.md @@ -149,12 +149,13 @@ interface ERC1155 /* is ERC165 */ { ### ERC-1155 Token Receiver Smart contracts **MUST** implement all the functions in this interface to accept transfers. See "Safe Transfer Rules" for further detail. +Smart contracts **MUST** implement the ERC-165 `supportsInterface` function and signify support for this interface. See "ERC1155TokenReceiver ERC-165 rules" for further detail. ```solidity pragma solidity ^0.5.9; /** - Note: The ERC-165 identifier for this interface is 0x43b236a2. + Note: The ERC-165 identifier for this interface is 0x4e2312e0. */ interface ERC1155TokenReceiver { /** @@ -185,15 +186,7 @@ interface ERC1155TokenReceiver { @param _data Additional data with no specified format @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); - - /** - @notice Indicates whether a contract implements the `ERC1155TokenReceiver` functions and so can accept ERC1155 token types. - @dev This function MUST return `bytes4(keccak256("isERC1155TokenReceiver()"))` (i.e. 0x0d912442). - This function MUST NOT consume more than 5,000 gas. - @return `bytes4(keccak256("isERC1155TokenReceiver()"))` - */ - function isERC1155TokenReceiver() external view returns (bytes4); + function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4); } ``` @@ -336,16 +329,19 @@ To be more explicit about how the standard `safeTransferFrom` and `safeBatchTran - The set of all calls to `onERC1155Received` and `onERC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted. * A contract MAY skip calling the `onERC1155BatchReceived` hook function if the transfer operation is transferring the token(s) to itself. -**_isERC1155TokenReceiver rules:_** -* The implementation of `isERC1155TokenReceiver` function SHOULD be as follows: - ``` - function isERC1155TokenReceiver() external view returns (bytes4) { - return 0x0d912442; // bytes4(keccak256("isERC1155TokenReceiver()")) +**_ERC1155TokenReceiver ERC-165 rules:_** +* The implementation of the ERC-165 `supportsInterface` function SHOULD be as follows: + ``` + function supportsInterface(bytes4 interfaceID) external view returns (bool) { + return interfaceID == 0x01ffc9a7 || // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`). + interfaceID == 0x4e2312e0; // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). } - ``` + ``` * The implementation MAY differ from the above but: - - It MUST return the same value. - - It MUST NOT consume more than 5,000 gas. + - It MUST return the constant value `true` if `0x01ffc9a7` is passed through the `interfaceID` argument. This signifies ERC-165 support. + - It MUST return the constant value `true` if `0x4e2312e0` is passed through the `interfaceID` argument. This signifies ERC-1155 `ERC1155TokenReceiver` support. + - It MUST NOT consume more than 10,000 gas. + - This keeps it below the ERC-165 requirement of 30,000 gas, reduces the gas reserve needs and minimises possible side-effects of gas exhaustion during the call. **_Implementation specific transfer API rules:_** * If an implementation specific API function is used to transfer ERC-1155 token(s) to a contract, the `safeTransferFrom` or `safeBatchTransferFrom` (as appropriate) rules MUST still be followed if the receiver implements the `ERC1155TokenReceiver` interface. If it does not the non-standard implementation SHOULD revert but MAY proceed. @@ -356,9 +352,9 @@ To be more explicit about how the standard `safeTransferFrom` and `safeBatchTran 4. `myTransferFrom` checks if `_to` is a contract address and determines that it is so (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` SHOULD revert the transaction immediately as receipt of the token(s) was not explicitly accepted by the `onERC1155BatchReceived` function. - 7. If however `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 now known to be a valid receiver and the previous acceptance step failed. - - NOTE: You could have called `isERC1155TokenReceiver()` at a previous step if you wanted to gather and act upon that information earlier, such as in a hybrid standards scenario. - 8. If the above call to `isERC1155TokenReceiver()` on `_to` reverts or returns an unknown value the `myTransferFrom` function MAY consider this transfer successful. + 7. If however `myTransferFrom` wishes to continue it MUST call `supportsInterface(0x4e2312e0)` on `_to` and if it returns the constant value `true` the transaction MUST be reverted, as it is now known to be a valid receiver and the previous acceptance step failed. + - NOTE: You could have called `supportsInterface(0x4e2312e0)` at a previous step if you wanted to gather and act upon that information earlier, such as in a hybrid standards scenario. + 8. If the above call to `supportsInterface(0x4e2312e0)` on `_to` reverts or returns a value other than the constant value `true` 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. @@ -381,20 +377,25 @@ To be more explicit about how the standard `safeTransferFrom` and `safeBatchTran * A contract MAY skip calling the `ERC1155TokenReceiver` hook function(s) if the mint/create operation is transferring the token(s) to itself. In all other cases the `ERC1155TokenReceiver` rules MUST be followed as appropriate for the implementation (i.e. safe, custom and/or hybrid). -###### A solidity example of the keccak256 generated constants for the return magic is: - - 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)")) +##### A solidity example of the keccak256 generated constants for the various magic values (these MAY be used by implementation): + +``` +bytes4 constant public ERC1155_ERC165 = 0xd9b67a26; // ERC-165 identifier for the main token standard. +bytes4 constant public ERC1155_ERC165_TOKENRECEIVER = 0x4e2312e0; // ERC-165 identifier for the `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). +bytes4 constant public ERC1155_ACCEPTED = 0xf23a6e61; // Return value from `onERC1155Received` call if a contract accepts receipt (i.e `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`). +bytes4 constant public ERC1155_BATCH_ACCEPTED = 0xbc197c81; // Return value from `onERC1155BatchReceived` call if a contract accepts receipt (i.e `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)". +To cater for this scenario, there is some leeway with the revert 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 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 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. +1. The implementation MUST call the function `supportsInterface(0x4e2312e0)` on the recipient contract, providing at least 10,000 gas. +2. If the function call succeeds and the return value is the constant value `true` 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 the constant value `true` 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 ERC-1155/ERC-721 contract is linked in the references section under implementations. @@ -416,6 +417,8 @@ The following optional extensions can be identified with the (ERC-165 Standard I 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. +Note that if this extension is enabled: if the URI event is to be used for metadata uri's changes, it does not need to be emitted first at Mint time. Observer can fetch the metadata uri at mint time from the `uri` function. + ```solidity pragma solidity ^0.5.9;