From d4984c53eecb11cefa5af8fedab25565f11c1c05 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 18 Apr 2019 11:34:03 +0200 Subject: [PATCH 01/12] first draft --- EIPS/eip-nonfungible_data_token.md | 134 +++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 EIPS/eip-nonfungible_data_token.md diff --git a/EIPS/eip-nonfungible_data_token.md b/EIPS/eip-nonfungible_data_token.md new file mode 100644 index 0000000000000..701aa45a3eba7 --- /dev/null +++ b/EIPS/eip-nonfungible_data_token.md @@ -0,0 +1,134 @@ +--- +eip: +title: Non-fungible Data Token +author: Johann Barbie <@johannbarbie>, Ben Bollen , pinkiebell <@pinkiebell> +discussions-to: https://ethereum-magicians.org/t/erc-non-fungible-data-token/3139 +status: Draft +type: Standards Track +category: ERC +created: 2019-04-18 +requires: ERC721 +--- + + +This is the suggested template for new EIPs. + +Note that an EIP number will be assigned by an editor. When opening a pull request to submit your EIP, please use an abbreviated title in the filename, `eip-draft_title_abbrev.md`. + +The title should be 44 characters or less. + +## Simple Summary + +Some NFT use-cases require to have dynamic data associated with a non-fungible token that can change during its live-time. Examples for dynamic data: +- cryptokitties that can change color +- intellectual property tokens that encode rights holders +- tokens that store data to transport them across chains + +The existing meta-data standard does not suffice as data can only be set at minting time and not modified later. + +## Abstract + +Non-fungible tokens (NFTs) are extended with the ability to store dynamic data. A 32 bytes data field is added and a read function allows to access it. The write function allows to update it, if the caller is the owner of the token. An event is emitted every time the data updates and the previous and new value is emitted in it. + +## Motivation + +The proposal is made to standardize on tokens with dynamic data. Interactions with bridges for side-chains like xDAI or Plasma chains will profit from the ability to use such tokens. Protocols that build on data tokens like [distributed breeding](https://ethresear.ch/t/a-distributed-breeding-function/5264) will be enabled. + +## Specification + +An extension of ERC721 interface with the following functions and events is suggested: + +``` +pragma solidity ^0.5.2; + +contract IDataToken { + + event DataUpdated(uint256 indexed tokenId, bytes32 oldData, bytes32 newData); + + function readData(uint256 _tokenId) public view returns (bytes32); + + function writeData(uint256 _tokenId, bytes32 _newData) public; + +} +``` + +## Rationale + +The suggested data field in the NFT is used either for storing data directly, like a counter or address. If more data is required the implementer should fall back to authenticated data structures, like merkle- or patricia-trees. + +The proposal for this ERC stems from the [distributed breeding proposal](https://ethresear.ch/t/a-distributed-breeding-function/5264) to allow better integration of NFTs accross sidechains. [ost.com](https://ost.com/), [Skale](https://skalelabs.com/), [POA](https://poa.network/), and [LeapDAO](https://leapdao.org/) have been part of the discussion. + +## Backwards Compatibility + +🤷‍♂️ No related proposals are known to the author, hence no backwards compatability to consider. + +## Test Cases + +Simple happy test: + +``` +const DataToken = artifacts.require('./DataToken.sol'); + +contract('DataToken', (accounts) => { + const firstTokenId = 100; + const empty = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const data = '0x0101010101010101010101010101010101010101010101010101010101010101'; + let dataToken; + + beforeEach(async () => { + dataToken = await DataToken.new(); + await dataToken.mint(accounts[0], firstTokenId); + }); + + it('should allow to write and read', async () => { + let rsp = await dataToken.readData(firstTokenId); + assert.equal(rsp, empty); + await dataToken.writeData(firstTokenId, data); + rsp = await dataToken.readData(firstTokenId); + assert.equal(rsp, data); + }); + +}); +``` + + +## Implementation + +An example implementation of the interface in solidity would look like this: + +``` +pragma solidity ^0.5.2; + +import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol"; +import "./IDataToken.sol"; + +contract DataToken is IDataToken, ERC721 { + + mapping(uint256 => bytes32) data; + + /** + * @dev Reads the data of a specified token. + * @param _tokenId The token to read the data off. + * @return A bytes32 representing the current data stored in the token. + */ + function readData(uint256 _tokenId) public view returns (bytes32) { + require(_exists(_tokenId)); + return data[_tokenId]; + } + + /** + * @dev Updates the data of a specified token. + * @param _tokenId The token to write data to. + * @param _newData The data to be written to the token. + */ + function writeData(uint256 _tokenId, bytes32 _newData) public { + require(msg.sender == ownerOf(_tokenId)); + emit DataUpdated(_tokenId, data[_tokenId], _newData); + data[_tokenId] = _newData; + } + +} +``` + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From eda395619ed9bf76d5a0d60c77f7b69fbd2bbdcf Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 18 Apr 2019 11:41:29 +0200 Subject: [PATCH 02/12] remove boilerplate --- EIPS/eip-nonfungible_data_token.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/EIPS/eip-nonfungible_data_token.md b/EIPS/eip-nonfungible_data_token.md index 701aa45a3eba7..8c1be5e971726 100644 --- a/EIPS/eip-nonfungible_data_token.md +++ b/EIPS/eip-nonfungible_data_token.md @@ -10,13 +10,6 @@ created: 2019-04-18 requires: ERC721 --- - -This is the suggested template for new EIPs. - -Note that an EIP number will be assigned by an editor. When opening a pull request to submit your EIP, please use an abbreviated title in the filename, `eip-draft_title_abbrev.md`. - -The title should be 44 characters or less. - ## Simple Summary Some NFT use-cases require to have dynamic data associated with a non-fungible token that can change during its live-time. Examples for dynamic data: From b4f69645b4176bb9c949108654f9d0aaec8ee818 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 18 Apr 2019 11:42:42 +0200 Subject: [PATCH 03/12] fix indentations --- EIPS/eip-nonfungible_data_token.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/EIPS/eip-nonfungible_data_token.md b/EIPS/eip-nonfungible_data_token.md index 8c1be5e971726..182bd7ee39a45 100644 --- a/EIPS/eip-nonfungible_data_token.md +++ b/EIPS/eip-nonfungible_data_token.md @@ -99,21 +99,21 @@ contract DataToken is IDataToken, ERC721 { mapping(uint256 => bytes32) data; - /** - * @dev Reads the data of a specified token. - * @param _tokenId The token to read the data off. - * @return A bytes32 representing the current data stored in the token. - */ + /** + * @dev Reads the data of a specified token. + * @param _tokenId The token to read the data off. + * @return A bytes32 representing the current data stored in the token. + */ function readData(uint256 _tokenId) public view returns (bytes32) { require(_exists(_tokenId)); return data[_tokenId]; } - /** - * @dev Updates the data of a specified token. - * @param _tokenId The token to write data to. - * @param _newData The data to be written to the token. - */ + /** + * @dev Updates the data of a specified token. + * @param _tokenId The token to write data to. + * @param _newData The data to be written to the token. + */ function writeData(uint256 _tokenId, bytes32 _newData) public { require(msg.sender == ownerOf(_tokenId)); emit DataUpdated(_tokenId, data[_tokenId], _newData); From 5e4928a12854cae1fde4a2f4ed5eedb38fec79b4 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 18 Apr 2019 13:35:58 +0200 Subject: [PATCH 04/12] typos --- EIPS/eip-nonfungible_data_token.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-nonfungible_data_token.md b/EIPS/eip-nonfungible_data_token.md index 182bd7ee39a45..1a4c74bcbf6cb 100644 --- a/EIPS/eip-nonfungible_data_token.md +++ b/EIPS/eip-nonfungible_data_token.md @@ -17,7 +17,7 @@ Some NFT use-cases require to have dynamic data associated with a non-fungible t - intellectual property tokens that encode rights holders - tokens that store data to transport them across chains -The existing meta-data standard does not suffice as data can only be set at minting time and not modified later. +The existing metadata standard does not suffice as data can only be set at minting time and not modified later. ## Abstract @@ -49,11 +49,11 @@ contract IDataToken { The suggested data field in the NFT is used either for storing data directly, like a counter or address. If more data is required the implementer should fall back to authenticated data structures, like merkle- or patricia-trees. -The proposal for this ERC stems from the [distributed breeding proposal](https://ethresear.ch/t/a-distributed-breeding-function/5264) to allow better integration of NFTs accross sidechains. [ost.com](https://ost.com/), [Skale](https://skalelabs.com/), [POA](https://poa.network/), and [LeapDAO](https://leapdao.org/) have been part of the discussion. +The proposal for this ERC stems from the [distributed breeding proposal](https://ethresear.ch/t/a-distributed-breeding-function/5264) to allow better integration of NFTs across side-chains. [ost.com](https://ost.com/), [Skale](https://skalelabs.com/), [POA](https://poa.network/), and [LeapDAO](https://leapdao.org/) have been part of the discussion. ## Backwards Compatibility -🤷‍♂️ No related proposals are known to the author, hence no backwards compatability to consider. +🤷‍♂️ No related proposals are known to the author, hence no backwards compatibility to consider. ## Test Cases From 58476c7fa38ea36cd4a892bb1c67a36a80b2b6b0 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 18 Apr 2019 13:42:43 +0200 Subject: [PATCH 05/12] ethereum code highlights --- EIPS/eip-nonfungible_data_token.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-nonfungible_data_token.md b/EIPS/eip-nonfungible_data_token.md index 1a4c74bcbf6cb..d422a286d53af 100644 --- a/EIPS/eip-nonfungible_data_token.md +++ b/EIPS/eip-nonfungible_data_token.md @@ -59,7 +59,7 @@ The proposal for this ERC stems from the [distributed breeding proposal](https:/ Simple happy test: -``` +``` solidity const DataToken = artifacts.require('./DataToken.sol'); contract('DataToken', (accounts) => { @@ -89,7 +89,7 @@ contract('DataToken', (accounts) => { An example implementation of the interface in solidity would look like this: -``` +``` solidity pragma solidity ^0.5.2; import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol"; From e6ab2973b33b9359460e3974dd466da77b63081c Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 18 Apr 2019 16:54:52 +0200 Subject: [PATCH 06/12] moved file to id --- EIPS/{eip-nonfungible_data_token.md => eip-1948.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename EIPS/{eip-nonfungible_data_token.md => eip-1948.md} (99%) diff --git a/EIPS/eip-nonfungible_data_token.md b/EIPS/eip-1948.md similarity index 99% rename from EIPS/eip-nonfungible_data_token.md rename to EIPS/eip-1948.md index d422a286d53af..9be931030c64f 100644 --- a/EIPS/eip-nonfungible_data_token.md +++ b/EIPS/eip-1948.md @@ -1,5 +1,5 @@ --- -eip: +eip: 1948 title: Non-fungible Data Token author: Johann Barbie <@johannbarbie>, Ben Bollen , pinkiebell <@pinkiebell> discussions-to: https://ethereum-magicians.org/t/erc-non-fungible-data-token/3139 From d59e38ec787369a3afe3b79a460c86851209fe22 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Tue, 30 Apr 2019 18:09:44 +0200 Subject: [PATCH 07/12] syntax --- EIPS/eip-1948.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md index 9be931030c64f..33b346c2263f8 100644 --- a/EIPS/eip-1948.md +++ b/EIPS/eip-1948.md @@ -1,13 +1,13 @@ --- eip: 1948 title: Non-fungible Data Token -author: Johann Barbie <@johannbarbie>, Ben Bollen , pinkiebell <@pinkiebell> +author: Johann Barbie (@johannbarbie), Ben Bollen , pinkiebell (@pinkiebell) discussions-to: https://ethereum-magicians.org/t/erc-non-fungible-data-token/3139 status: Draft type: Standards Track category: ERC created: 2019-04-18 -requires: ERC721 +requires: 721 --- ## Simple Summary From a422fc7144e0ffa57d6d6952c19af998a9884a15 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Wed, 1 May 2019 08:43:31 +0200 Subject: [PATCH 08/12] naming --- EIPS/eip-1948.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md index 33b346c2263f8..c890cfae19339 100644 --- a/EIPS/eip-1948.md +++ b/EIPS/eip-1948.md @@ -34,7 +34,7 @@ An extension of ERC721 interface with the following functions and events is sugg ``` pragma solidity ^0.5.2; -contract IDataToken { +contract IERC1948 { event DataUpdated(uint256 indexed tokenId, bytes32 oldData, bytes32 newData); @@ -60,16 +60,16 @@ The proposal for this ERC stems from the [distributed breeding proposal](https:/ Simple happy test: ``` solidity -const DataToken = artifacts.require('./DataToken.sol'); +const ERC1948 = artifacts.require('./ERC1948.sol'); -contract('DataToken', (accounts) => { +contract('ERC1948', (accounts) => { const firstTokenId = 100; const empty = '0x0000000000000000000000000000000000000000000000000000000000000000'; const data = '0x0101010101010101010101010101010101010101010101010101010101010101'; let dataToken; beforeEach(async () => { - dataToken = await DataToken.new(); + dataToken = await ERC1948.new(); await dataToken.mint(accounts[0], firstTokenId); }); @@ -93,9 +93,9 @@ An example implementation of the interface in solidity would look like this: pragma solidity ^0.5.2; import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol"; -import "./IDataToken.sol"; +import "./IERC1948.sol"; -contract DataToken is IDataToken, ERC721 { +contract ERC1948 is IERC1948, ERC721 { mapping(uint256 => bytes32) data; From 2ffd7ac81fa91d9a87664447ee51ad863723fc73 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Wed, 1 May 2019 21:41:04 +0200 Subject: [PATCH 09/12] link to erc721 --- EIPS/eip-1948.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md index c890cfae19339..cfc339dc42baf 100644 --- a/EIPS/eip-1948.md +++ b/EIPS/eip-1948.md @@ -29,7 +29,7 @@ The proposal is made to standardize on tokens with dynamic data. Interactions wi ## Specification -An extension of ERC721 interface with the following functions and events is suggested: +An extension of [ERC721](https://eips.ethereum.org/EIPS/eip-721) interface with the following functions and events is suggested: ``` pragma solidity ^0.5.2; From 931d9d780a69e20078b6be36152340aafd795795 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Fri, 10 May 2019 19:22:41 +0200 Subject: [PATCH 10/12] make interface an interface --- EIPS/eip-1948.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md index cfc339dc42baf..750ecb74daa58 100644 --- a/EIPS/eip-1948.md +++ b/EIPS/eip-1948.md @@ -34,7 +34,7 @@ An extension of [ERC721](https://eips.ethereum.org/EIPS/eip-721) interface with ``` pragma solidity ^0.5.2; -contract IERC1948 { +interface IERC1948 { event DataUpdated(uint256 indexed tokenId, bytes32 oldData, bytes32 newData); From 42672f8d1e0fd85e421b6263f01414ecc9ae7634 Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Wed, 22 May 2019 18:49:04 +0200 Subject: [PATCH 11/12] typo --- EIPS/eip-1948.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md index 750ecb74daa58..8d5deb16d180f 100644 --- a/EIPS/eip-1948.md +++ b/EIPS/eip-1948.md @@ -12,7 +12,7 @@ requires: 721 ## Simple Summary -Some NFT use-cases require to have dynamic data associated with a non-fungible token that can change during its live-time. Examples for dynamic data: +Some NFT use-cases require to have dynamic data associated with a non-fungible token that can change during its lifetime. Examples for dynamic data: - cryptokitties that can change color - intellectual property tokens that encode rights holders - tokens that store data to transport them across chains From e6e1b84a7b7c996d63345dd4cbb0aba963e0d8ef Mon Sep 17 00:00:00 2001 From: Johann Barbie Date: Thu, 27 Jun 2019 09:19:23 +0200 Subject: [PATCH 12/12] better comments --- EIPS/eip-1948.md | 78 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md index 8d5deb16d180f..a944d69e164c3 100644 --- a/EIPS/eip-1948.md +++ b/EIPS/eip-1948.md @@ -31,16 +31,41 @@ The proposal is made to standardize on tokens with dynamic data. Interactions wi An extension of [ERC721](https://eips.ethereum.org/EIPS/eip-721) interface with the following functions and events is suggested: -``` +``` solidity pragma solidity ^0.5.2; +/** + * @dev Interface of the ERC1948 contract. + */ interface IERC1948 { - + + /** + * @dev Emitted when `oldData` is replaced with `newData` in storage of `tokenId`. + * + * Note that `oldData` or `newData` may be empty bytes. + */ event DataUpdated(uint256 indexed tokenId, bytes32 oldData, bytes32 newData); - function readData(uint256 _tokenId) public view returns (bytes32); + /** + * @dev Reads the data of a specified token. Returns the current data in + * storage of `tokenId`. + * + * @param tokenId The token to read the data off. + * + * @return A bytes32 representing the current data stored in the token. + */ + function readData(uint256 tokenId) external view returns (bytes32); - function writeData(uint256 _tokenId, bytes32 _newData) public; + /** + * @dev Updates the data of a specified token. Writes `newData` into storage + * of `tokenId`. + * + * @param tokenId The token to write data to. + * @param newData The data to be written to the token. + * + * Emits a `DataUpdated` event. + */ + function writeData(uint256 tokenId, bytes32 newData) external; } ``` @@ -59,7 +84,7 @@ The proposal for this ERC stems from the [distributed breeding proposal](https:/ Simple happy test: -``` solidity +``` javascript const ERC1948 = artifacts.require('./ERC1948.sol'); contract('ERC1948', (accounts) => { @@ -90,34 +115,41 @@ contract('ERC1948', (accounts) => { An example implementation of the interface in solidity would look like this: ``` solidity -pragma solidity ^0.5.2; - -import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol"; -import "./IERC1948.sol"; - +/** + * @dev Implementation of ERC721 token and the `IERC1948` interface. + * + * ERC1948 is a non-fungible token (NFT) extended with the ability to store + * dynamic data. The data is a bytes32 field for each tokenId. If 32 bytes + * do not suffice to store the data, an authenticated data structure (hash or + * merkle tree) shall be used. + */ contract ERC1948 is IERC1948, ERC721 { mapping(uint256 => bytes32) data; /** - * @dev Reads the data of a specified token. - * @param _tokenId The token to read the data off. - * @return A bytes32 representing the current data stored in the token. + * @dev See `IERC1948.readData`. + * + * Requirements: + * + * - `tokenId` needs to exist. */ - function readData(uint256 _tokenId) public view returns (bytes32) { - require(_exists(_tokenId)); - return data[_tokenId]; + function readData(uint256 tokenId) external view returns (bytes32) { + require(_exists(tokenId)); + return data[tokenId]; } /** - * @dev Updates the data of a specified token. - * @param _tokenId The token to write data to. - * @param _newData The data to be written to the token. + * @dev See `IERC1948.writeData`. + * + * Requirements: + * + * - `msg.sender` needs to be owner of `tokenId`. */ - function writeData(uint256 _tokenId, bytes32 _newData) public { - require(msg.sender == ownerOf(_tokenId)); - emit DataUpdated(_tokenId, data[_tokenId], _newData); - data[_tokenId] = _newData; + function writeData(uint256 tokenId, bytes32 newData) external { + require(msg.sender == ownerOf(tokenId)); + emit DataUpdated(tokenId, data[tokenId], newData); + data[tokenId] = newData; } }