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
The existing metadata 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 will be enabled.
Specification
An extension of SRC-721 interface with the following functions and events is suggested:
pragmasolidity^0.5.2;/**
* @dev Interface of the SRC1948 contract.
*/interfaceISRC1948{/**
* @dev Emitted when `oldData` is replaced with `newData` in storage of `tokenId`.
*
* Note that `oldData` or `newData` may be empty bytes.
*/eventDataUpdated(uint256indexedtokenId,bytes32oldData,bytes32newData);/**
* @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.
*/functionreadData(uint256tokenId)externalviewreturns(bytes32);/**
* @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.
*/functionwriteData(uint256tokenId,bytes32newData)external;}
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 SRC stems from the distributed breeding proposal to allow better integration of NFTs across side-chains. ost.com, Skale, POA, and LeapDAO have been part of the discussion.
Backwards Compatibility
🤷♂️ No related proposals are known to the author, hence no backwards compatibility to consider.
Test Cases
Simple happy test:
constSRC1948=artifacts.require('./SRC1948.sol');contract('SRC1948',(accounts)=>{constfirstTokenId=100;constempty='0x0000000000000000000000000000000000000000000000000000000000000000';constdata='0x0101010101010101010101010101010101010101010101010101010101010101';letdataToken;beforeEach(async()=>{dataToken=awaitSRC1948.new();awaitdataToken.mint(accounts[0],firstTokenId);});it('should allow to write and read',async()=>{letrsp=awaitdataToken.readData(firstTokenId);assert.equal(rsp,empty);awaitdataToken.writeData(firstTokenId,data);rsp=awaitdataToken.readData(firstTokenId);assert.equal(rsp,data);});});
Implementation
An example implementation of the interface in solidity would look like this:
/**
* @dev Implementation of SRC721 token and the `ISRC1948` interface.
*
* SRC1948 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.
*/contractSRC1948isISRC1948,SRC721{mapping(uint256=>bytes32)data;/**
* @dev See `ISRC1948.readData`.
*
* Requirements:
*
* - `tokenId` needs to exist.
*/functionreadData(uint256tokenId)externalviewreturns(bytes32){require(_exists(tokenId));returndata[tokenId];}/**
* @dev See `ISRC1948.writeData`.
*
* Requirements:
*
* - `msg.sender` needs to be owner of `tokenId`.
*/functionwriteData(uint256tokenId,bytes32newData)external{require(msg.sender==ownerOf(tokenId));emitDataUpdated(tokenId,data[tokenId],newData);data[tokenId]=newData;}}
Johann Barbie (@johannbarbie), Ben Bollen <ben@ost.com>, pinkiebell (@pinkiebell), "SRC-1948: Non-fungible Data Token [STAGNANT]," Sila Improvement Proposals, no. 1948, April 2019. Available: https://sips.sila.org/SIPS/sip-1948.