diff --git a/EIPS/eip-1155.md b/EIPS/eip-1155.md index 322738e3..f7c1de48 100644 --- a/EIPS/eip-1155.md +++ b/EIPS/eip-1155.md @@ -1,7 +1,7 @@ --- eip: 1155 title: ERC-1155 Multi Token Standard -author: Witek Radomski , Andrew Cooke , Philippe Castonguay , James Therien , Eric Binet +author: Witek Radomski , Andrew Cooke , Philippe Castonguay , James Therien , Eric Binet , Ronan Sandford type: Standards Track category: ERC status: Draft @@ -13,7 +13,7 @@ requires: 165 ## Simple Summary -A standard interface for contracts that manage multiple token types. A single deployed contract may include any combination of fungible tokens, non-fungible tokens, or other configurations (for example, semi-fungible tokens). +A standard interface for contracts that manage multiple token types. A single deployed contract may include any combination of fungible tokens, non-fungible tokens, or other configurations (e.g. semi-fungible tokens). ## Abstract @@ -84,13 +84,13 @@ interface ERC1155 /* is ERC165 */ { MUST revert if `_to` is the zero address. MUST revert if balance of holder for token `_id` is lower than the `_value` sent. MUST revert on any other error. - After the above conditions are met, 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). - MUST emit `TransferSingle` event on transfer success (see "Safe Transfer Rules" section of the standard). + MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard). + After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. 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, MUST be sent in call to `onERC1155Received` on `_to` + @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to` */ function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; @@ -100,15 +100,15 @@ interface ERC1155 /* is ERC165 */ { 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(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. - MUST revert on any other error. - Transfers and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc). - After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (eg. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). - MUST emit `TransferSingle` or `TransferBatch` event(s) on transfer success (see "Safe Transfer Rules" section of the standard). + MUST revert on any other error. + MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard). + Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc). + After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). @param _from Source address @param _to Target address @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, MUST be sent in call to the `ERC1155TokenReceiver` hook(s) on `_to` + @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to` */ function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external; @@ -193,13 +193,13 @@ interface ERC1155TokenReceiver { This function MUST NOT consume more than 5,000 gas. @return `bytes4(keccak256("isERC1155TokenReceiver()"))` */ - function isERC1155TokenReceiver() external pure returns (bytes4); + function isERC1155TokenReceiver() external view returns (bytes4); } ``` ### Safe Transfer Rules -To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST operate with respect to the `ERC1155TokenReceiver`, a list of scenarios and rules follows. +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. #### Scenarios @@ -219,22 +219,30 @@ 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 (eg. safeTransferFrom called). +**_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. * 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 (eg. safeBatchTransferFrom called). +**_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. * `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. - See "onERC1155BatchReceived rules" for further rules that MUST be followed. * The `onERC1155Received` hook MAY be called on the recipient contract and its rules followed. - See "onERC1155Received rules" for further rules that MUST be followed. + +**_Scenario#8 :_** You are the creator of a contract that implements the `ERC1155TokenReceiver` interface and you forward the token(s) onto another address in one or both of `onERC1155Received` and `onERC1155BatchReceived`. +* Forwarding should be considered acceptance and then initiating a new `safeTransferFrom` or `safeBatchTransferFrom` in a new context. + - The prescribed keccak256 acceptance value magic for the receiver hook being called MUST be returned after forwarding is successful. +* The `_data` argument MAY be re-purposed for the new context. +* If forwarding unexpectedly fails the transaction MUST be reverted. #### Rules @@ -243,8 +251,8 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op * MUST revert if `_to` is the zero address. * MUST revert if balance of holder for token `_id` is lower than the `_value` sent to the recipient. * MUST revert on any other error. -* After the above conditions are met, 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 "onERC1155Received rules" section). -* MUST emit `TransferSingle` event on transfer success (see "TransferSingle and TransferBatch event rules" section). +* MUST emit the `TransferSingle` event to reflect the balance change (see "TransferSingle and TransferBatch event rules" section). +* After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "onERC1155Received rules" section). **_safeBatchTransferFrom rules:_** * Caller must be approved to manage all the tokens being transferred out of the `_from` account (see "Approval" section). @@ -252,9 +260,9 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op * MUST revert if length of `_ids` is not the same as length of `_values`. * MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. * MUST revert on any other error. -* After the above conditions are met, this function MUST check if `_to` is a smart contract (eg. code size > 0). If so, it MUST call `onERC1155Received` or `onERC1155BatchReceived` on `_to` and act appropriately (see "`onERC1155Received` and onERC1155BatchReceived rules" section). -* MUST emit `TransferSingle` or `TransferBatch` event(s) on transfer success (see "TransferSingle and TransferBatch event rules" section). -* Transfers and events MUST occur in the array order they were submitted (_ids[0]/_values[0] before _ids[1]/_values[1], etc). +* MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "TransferSingle and TransferBatch event rules" section). +* The balance changes and events MUST occur in the array order they were submitted (_ids[0]/_values[0] before _ids[1]/_values[1], etc). +* After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` or `onERC1155BatchReceived` on `_to` and act appropriately (see "`onERC1155Received` and onERC1155BatchReceived rules" section). **_TransferSingle and TransferBatch event rules:_** * `TransferSingle` SHOULD be used to indicate a single balance transfer has occurred between a `_from` and `_to` pair. @@ -278,6 +286,8 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). * The total value transferred from address 0x0 minus the total value transferred to 0x0 MAY be used by clients and exchanges to be added to the "circulating supply" for a given token ID. * To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_value` of 0. +* All `TransferSingle` and `TransferBatch` events MUST be emitted to reflect all the balance changes that have occurred before any call(s) to `onERC1155Received` or `onERC1155BatchReceived`. + - To make sure event order is correct in the case of valid re-entry (e.g. if a receiver contract forwards tokens on receipt) state balance and events balance MUST match before calling an external contract. **_onERC1155Received rules:_** * The `_operator` argument MUST be the address of the account/contract that initiated the transfer (i.e. msg.sender). @@ -285,7 +295,8 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op - `_from` MUST be 0x0 for a mint. * The `_id` argument MUST be the token type being transferred. * 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 extra information provided by the sender for the transfer. +* The `_data` argument MUST contain the unaltered information provided by the sender for the transfer. + - 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 reject an increase of its balance by calling revert. @@ -301,7 +312,8 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op - `_from` MUST be 0x0 for a mint. * The `_ids` argument MUST be the list of tokens being transferred. * 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 a transfer. +* The `_data` argument MUST contain the unaltered information provided by the sender for the transfer. + - 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_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 reject an increase of its balance by calling revert. @@ -314,7 +326,7 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op **_isERC1155TokenReceiver rules:_** * The implementation of `isERC1155TokenReceiver` function SHOULD be as follows: ``` - function isERC1155TokenReceiver() external pure returns (bytes4) { + function isERC1155TokenReceiver() external view returns (bytes4) { return 0x0d912442; // bytes4(keccak256("isERC1155TokenReceiver()")) } ```