Appearance
Abstract
This EIP standardizes an interface for non-fungible value-holding shareable tokens. Shareability is accomplished by minting copies of existing tokens for new recipients. Sharing and associated events allow the construction of a graph describing who has shared what to which party.
Motivation
NFT standards such as EIP-721 and EIP-1155 have been developed to standardize scarce digital resources. However, many non-fungible digital resources need not be scarce.
We have attempted to capture positive externalities in ecosystems with new types of incentive mechanisms that exhibit anti-rival logic, serve as an unit of accounting and function as medium of sharing. We envision that shareable tokens can work both as incentives but also as representations of items that are typically digital in their nature and gain more value as they are shared.
These requirements have set us to define shareable NFTs and more specifically a variation of shareable NFTs called non-transferable shareable NFTs. These shareable NFTs can be “shared” in the same way digital goods can be shared, at an almost zero technical transaction cost. We have utilized them to capture anti-rival value in terms of accounting positive externalities in an economic system.
Typical NFT standards such as EIP-721 and EIP-1155 do not define a sharing modality. Instead ERC standards define interfaces for typical rival use cases such as token minting and token transactions that the NFT contract implementations should fulfil. The ‘standard contract implementations' may extend the functionalities of these standards beyond the definition of interfaces. The shareable tokens that we have designed and developed in our experiments are designed to be token standard compatible at the interface level. However the implementation of token contracts may contain extended functionalities to match the requirements of the experiments such as the requirement of 'shareability'. In reflection to standard token definitions, shareability of a token could be thought of as re-mintability of an existing token to another party while retaining the original version of it.
Sharing is an interesting concept as it can be thought and perceived in different ways. For example, when we talk about sharing we can think about it is as digital copying, giving a copy of a digital resource while retaining a version by ourselves. Sharing can also be fractional or sharing could be about giving rights to use a certain resource. The concept of shareability and the context of shareability can take different forms and one might use different types of implementatins for instances of shareable tokens. Hence we haven't restricted that the interface should require any specific token type.
Shareable tokens can be made non-transferable at the contract implementaiton level. Doing so, makes them shareable non-transferable tokens. In the reference implementation we have distilled a general case from our use cases that defines a shareable non-transferable NFTs using the shareable NFT interface.
We believe that the wider audience should benefit from an abstraction level higher definition for shareability, such as this interface implementation, that defines minimum amount of functions that would be implemented to satisfy the concept of shareability.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
solidity
/// Note: the ERC-165 identifier for this interface is 0xded6338b
interface IERC5023 is IERC165 {
/// @dev This emits when a token is shared, reminted and given to another wallet that isn't function caller
event Share(address indexed from, address indexed to, uint256 indexed tokenId, uint256 derivedFromtokenId);
/// @dev Shares, remints an existing token, gives a newly minted token a fresh token id, keeps original token at function callers possession and transfers newly minted token to receiver which should be another address than function caller.
function share(address to, uint256 tokenIdToBeShared) external returns(uint256 newTokenId);
}
The Share event is expected to be emitted when function method share is succesfully called and a new token on basis of a given token id is minted and transferred to a recipient.
Rationale
Current NFT standards define transferable non-fungible tokens, but not shareable non-fungible tokens. To be able to create shareable NFTs we see that existing NFT contracts could be extended with an interface which defines the basic principles of sharing, namely the Event of sharing and the function method of sharing. Definition of how transferability of tokens should be handled is left to the contract implementor. In case transfering is left enable shareable tokens behave similarily to the existing tokens, except when they are shared, a version of token is retained. In case transfering is disabled, shareable tokens become shareable non-transferable tokens, where they can be minted and given or shared to other people, but they cannot be transferred away.
Imagine that Bob works together with Alice on a project. Bob earns an unique NFT indicating that he has made effort to the project, but Bob feels that his accomplishments are not only out of his own accord. Bob wants to share his token with Alice to indicate that also Alice deserves recognition of having put effort on their project. Bob initiates token sharing by calling Share
method on the contract which has his token and indicates which one of his tokens he wishes to share and to whom by passing address and token id parameters. A new token is minted for Alice and a Share
event is initiated to communicate that it was Bob whom shared his token to Alice by logging addresses who shared a token id to whose address and which token id was this new token derived from.
Over time, a tree-like structures can be formed from the Share event information. If Bob shared to Alice, and Alice shared further to Charlie and Alice also shared to David a rudimentary tree structure forms out from sharing activity. This share event data can be later on utilized to gain more information of share activities that the tokens represent.
text
B -> A -> C
\
> D
These tree structures can be further aggregated and collapsed to network representations e.g. social graphs on basis of whom has shared to whom over a span of time. E.g. if Bob shared a token to Alice, and Alice has shared a different token to Charlie and Bob has shared a token to Charlie, connections form between all these parties through sharing activities.
text
B----A----C
\_______/
Backwards Compatibility
This proposal is backwards compatible with EIP-721 and EIP-1155.
Reference Implementation
Following reference implementation demonstrates a general use case of one of our pilots. In this case a shareable non-transferable token represents a contribution done to a community that the contract owner has decided to merit with a token. Contract owner can mint a merit token and give it to a person. This token can be further shared by the receiver to other parties for example to share the received merit to others that have participated or influenced his contribution.
solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "./IERC5023.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract ShareableERC721 is ERC721URIStorage, Ownable, IERC5023 /* EIP165 */ {
string baseURI;
uint256 internal _currentIndex;
constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {}
function mint(
address account,
uint256 tokenId
) external onlyOwner {
_mint(account, tokenId);
}
function setTokenURI(
uint256 tokenId,
string memory tokenURI
) external {
_setTokenURI(tokenId, tokenURI);
}
function setBaseURI(string memory baseURI_) external {
baseURI = baseURI_;
}
function _baseURI() internal view override returns (string memory) {
return baseURI;
}
function share(address to, uint256 tokenIdToBeShared) external returns(uint256 newTokenId) {
require(to != address(0), "ERC721: mint to the zero address");
require(_exists(tokenIdToBeShared), "ShareableERC721: token to be shared must exist");
require(msg.sender == ownerOf(tokenIdToBeShared), "Method caller must be the owner of token");
string memory _tokenURI = tokenURI(tokenIdToBeShared);
_mint(to, _currentIndex);
_setTokenURI(_currentIndex, _tokenURI);
emit Share(msg.sender, to, _currentIndex, tokenIdToBeShared);
return _currentIndex;
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
revert('In this reference implementation tokens are not transferrable');
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
revert('In this reference implementation tokens are not transferrable');
}
}
Security Considerations
Reference implementation should not be used as is in production. There are no other security considerations related directly to implementation of this standard.
Copyright
Copyright and related rights waived via CC0.