Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ERC981: Partial Ownership Standard #981

Closed
jdkanani opened this issue Apr 7, 2018 · 19 comments
Closed

ERC981: Partial Ownership Standard #981

jdkanani opened this issue Apr 7, 2018 · 19 comments
Labels

Comments

@jdkanani
Copy link

jdkanani commented Apr 7, 2018

Preamble

EIP: <to be assigned>
Title: Partial Ownership Standard
Author: Brij Bhasin <brij@akodo.in>, Jaynti Kanani <jdkanani@matic.network>
Type: Standard
Category: ERC
Status: Draft
Created: 2018-04-07

Simple Summary

An interface for tokens that are unique, fungible and allow partial ownership and trade of digital and real assets.

Motivation

The current ERC Tokens while useful leave out key aspects of information needed to enable tokenized trade of real-world assets on distributed ledger technology. ERC20 fungible interface is great in its simplicity for issuing tokens that are fungible, but due to its open design and extensibility, can lead to manipulation by bad actors. ERC721 non-fungible interface is great in allowing uniqueness of token to represent an asset, and track its ownership, it restricts fungibility.

Rationale

This proposal serves to describe a new Ethereum Interface for issuing tokens for asset owners who come to a marketplace with a finite quantity of their asset to trade, representing 100% ownership of that particular issue. They then choose to divide the asset into divisible units that increase fungibility of that asset.

The initial price and type of the value of the entire asset or it divisible unit is determined by the owner. Once the owner puts it up for trade and proposes the value and mechanism for exchange. Once an agreement is reached on the type of value, the seller and buyer agree on the price of the exchange on the original asset with the value being offered by the buyer.

In theory, such a system would allow the owner of a fungible ERC 20 to exchange specific units with non-fungible ERC 721 tokens as long as the seller and buyer agree on a value assignment mechanism for each asset.

Part-Owner Interface

Part-Owner is a free, open standard that describes how to build fungible yet uniquely representative tokens of the underlying asset on the Ethereum blockchain. While most tokens are fungible (every token is the same as every other token), and some tokens are non-fungible (every token is unique), Part-Owner Tokens (POTs) uniquely represent a divisible unit ownership of an asset, which is tradeable for divisible unit ownership of another asset as long as there is agreement on the value mechanism.

For eg. A Sports League can issue POTs for each Team in the League. Fans of that team can put bids to buy POTs and collectively own their team.

Specification

This ERC defines a minimum part-owner interface that a smart contract must implement to allow tokens to be managed, owned, and traded. It does not mandate a standard for token metadata or restrict adding supplemental functions.

Design concepts to incorporate –

  • Mother asset ownership to be capped to 100%
  • Mother Asset to be divisible up to 18 decimal points of Child Assets
  • Collective ownership of Child Assets defines ownership of Mother Asset
  • Mother Asset have Types [Physical, Digital] (Physical – Land) (Digital – Sports Leagues, Private Partnerships, Venture Funds)
  • Child Assets have Units (Weight, Area, Number, Decimals)
  • Mother Asset has an Issuer
  • Child Assets have Owners
  • Mother Asset has Issue Value
  • Child Assets have Price

Token

Methods

name

Returns the name of the token - e.g. "CryptoPremierLeague", "VirtualSmartFund".

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.

function name() public view returns (string _name)

symbol

Returns the symbol of the token. E.g. "CPL", "VSF".

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.

function symbol() public view returns (string _symbol)

decimals

Returns the number of decimals the token uses - e.g. 8, means to divide the token amount by 100000000 to get its user representation.

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.

function decimals() public view returns (uint8 _decimals)

balanceOf

Returns the number of tokens (percentage of underlying mother asset) assigned to _owner.

function balanceOf(address _owner) public view returns (uint256 _balance)

divide

Divides token (underlying asset) into multiple tokens with specified amount, type and metadata - if and only if identity of msg.sender == ownerOf(_tokenId). A successful divide MUST fire the Transfer event for each new POT (defined below).

Owners are array of owner addresses. sigs are array of signatures which required to divide the token (underlying asset). Signature type can be approval or acknowledgement.

This method MUST divide token into multiple child tokens or throw, no other outcomes can be possible. Reasons for failure include (but are not limited to):

  • msg.sender is not the owner of _tokenId
  • invalid or insufficient signatures
function divide(uint256 _tokenId, address[] _owners, uint256[] amounts, string[] types, string[] metadata, bytes[] sigs) public payable

merge

Merges tokens (underlying assets) into single token with metadata - and msg.sender the owner of new token. A successful merge MUST fire the Transfer event for new POT (defined below).

This method MUST merge tokens into single token or throw, no other outcomes can be possible. Reasons for failure include (but are not limited to):

  • invalid token id in _tokenIds
  • invalid or insufficient signatures
function merge(uint256[] _tokenIds, uint256 amount, string type, string metadata, bytes[] sigs) public payable

transfer

Assigns the ownership of the POT with ID _tokenId to _to if and only if msg.sender == ownerOf(_tokenId). A successful transfer MUST fire the Transfer event (defined below).

This method MUST transfer ownership to _to or throw, no other outcomes can be possible. Reasons for failure include (but are not limited to):

  • msg.sender is not the owner of _tokenId
  • invalid or insufficient signatures

_tokenId does not represent a POT currently tracked by this contract
_to is 0 (Conforming contracts MAY have other methods to destroy or burn POTs, which are conceptually "transfers to 0" and will emit Transfer events reflecting this. However, transfer(0, tokenId, sigs) MUST be treated as an error.)

A conforming contract MUST allow the current owner to "transfer" a token to themselves, as a way of affirming ownership in the event stream. (i.e. it is valid for _to == ownerOf(_tokenID).) This "no-op transfer" MUST be considered a successful transfer, and therefore MUST fire a Transfer event (with the same address for _from and _to).

Triggers Event: Transfer

function transfer(address _to, uint256 _tokenId, bytes[] sigs) public payable

Basic Ownership

owners

Returns addresses of all owners currently holding POTs.

function owners() public view returns (address[] _owners)

ownerOf

Returns the address currently marked as the owner of _tokenId. This method MUST throw if _tokenId does not represent a POT currently tracked by this contract. This method MUST NOT return 0 (POTs assigned to the zero identity are considered destroyed, and queries about them should throw).

function ownerOf(uint256 _tokenId) public view returns (address owner)

getToken

Returns token data of _tokenId, if _tokenId represents POT currently tracked by this contract.

function getToken(uint256 _tokenId) public view returns (address _owner, uint256 _amount, string _type, string _metadata)

totalTokens

Returns number of tokens owned by _owner

function totalTokens(address _owner) public view returns (uint256 _totalTokens)

tokenOfOwnerByIndex

OPTIONAL - It is recommended that this method is implemented for enhanced usability, but interfaces and other contracts MUST NOT depend on the existence of this method.

Returns the nth POT assigned to the _owner, with n specified by the _index argument. This method MUST throw if _index >= totalTokens(_owner).

function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256 _tokenId)

Recommended usage is as follows:

uint256 numberOfTokens = partOwnerTokenContract.totalTokens(owner);

uint256[] memory ownerTokens = new uint256[](numberOfTokens);

for (uint256 i = 0; i < numberOfTokens; i++) {
    ownerTokens[i] = partOwnerTokenContract.tokenOfOwnerByIndex(owner, i);
}

Identity

getIdentity

Returns identity hash if _owner is valid owner of any child assets.

function getIdentity(address _owner) public view returns (bytes32 _key);

POT metadata

tokenMetadata

OPTIONAL - It is recommended that this method is implemented for enhanced usability, but interfaces and other contracts MUST NOT depend on the existence of this method.

Returns a multiaddress string referencing an external resource bundle that contains (optionally localized) metadata about the POT associated with _tokenId. The string MUST be an IPFS or HTTP(S) base path (without a trailing slash) to which specific subpaths are obtained through concatenation. (IPFS is the preferred format due to better scalability, persistence, and immutability.) See ERC721

function tokenMetadata(uint256 _tokenId) public view returns (string infoUrl)

Events

Transfer

This event MUST trigger when POT ownership is transferred via any mechanism.

Additionally, the creation of new asset MUST trigger a Transfer event for each newly created assets, with a _from to 0x0 and a _to key matching identity the owner of the new asset (possibly the smart contract itself). The deletion (or burn) of any asset MUST trigger a Transfer event with a _to as 0x0 and a _from address of the owner of the asset (now former owner).

event Transfer(address indexed _from, address indexed _to, uint256 _tokenId)

Solidity interface

pragma solidity ^0.4.19;

contract ERC {
    // Events
    event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);

    // Methods
    function divide(uint256 _tokenId, address[] _owners, uint256[] amounts, string[] types, string[] metadata, bytes[] sigs) public payable;
    function merge(uint256[] _tokenIds, uint256 amount, string type, string metadata, bytes[] sigs) public payable;
    function transfer(address _to, uint256 _tokenId, bytes[] sigs) public payable;
    
    function balanceOf(address _owner) public view returns (uint256 _balance);
    function owners() public view returns (address[] _owners);
    function ownerOf(uint256 _tokenId) public view returns (address owner);
    function getToken(uint256 _tokenId) public view returns (address _owner, uint256 _amount, string _type, string _metadata);
    function totalTokens(address _owner) public view returns (uint256 _totalTokens);
    function tokenOfOwnerByIndex(address _owner, uint256 _index) public view returns (uint256 _tokenId);
    
    // identity hash
    function getIdentity(address _owner) public view returns (bytes32 _key);
    
    // OPTIONAL
    function name() public view returns (string _name);
    function symbol() public view returns (string _symbol);
    function decimals() public view returns (uint8 _decimals);
    
    function tokenMetadata(uint256 _tokenId) public view returns (string infoUrl);
}

Copyright

Copyright and related rights waived via CC0.

@jdkanani jdkanani changed the title Barter Token Standard ERC981: Barter Token Standard Apr 7, 2018
@MicahZoltu
Copy link
Contributor

most primitive form human trade – barter system

This isn't considered a "fact" in the scientific (anthropology) community. Here is a good summary article: https://www.theatlantic.com/business/archive/2016/02/barter-society-myth/471051/

@brijakodo
Copy link

brijakodo commented Apr 7, 2018

@MicahZoltu I see your point on barter, and perhaps we should rephrase it to not apply as such. My intention was to use the word "barter" to signify concept of fungibility to unique items through a mechanism of exchange of value. I read the first chapter on the origin of money and Barter in Graeber's Debt: The First 5,000 Years. While he refutes Barter as a precursor to the origin of monetary systems, he clearly states that Barter systems have been used in trade amongst strangers with low trust in each other. Moreover, the system of credit or tracking of asset exchanges actually predates both money and barter.

In fact, I completely agree with the premise made in the article of "Gift Economy" and I observe this quite frequently in close-knit socially homogeneous communities in India. The intent of this proposal is, however, to capture the process humans do offline - loose trust networks that lead to the exchange of assets based on mutual consent of value.

We will change the language in the rationale and open to specific suggestions from the community.

@jaycenhorton
Copy link

jaycenhorton commented Apr 9, 2018

This is definitely something I can get behind. I have been working on something similar that adapts 721+777 and allows for these kinds of per-token qualities. Some of that is defined here, although this is out of date, and I have furthered the implementation to allow for a similar type of divide method (which effectively takes one token of x value, and can mint a new token x2 where x1 + x2 = x and x2 is linked to the I'd of x as a parent).

I strongly suggest implementing #820 as part of this standard.

@jdkanani
Copy link
Author

jdkanani commented Apr 9, 2018

This ERC uses ERC725 key hashes for owners and sigs to prove/acknowledge while transferring or dividing. If implementation demands to bolt regulators/authorities/partners in, this allows them to keep track of assets.

Question - should we use addresses and add getIdentity? @eordano

@jaycenhorton Interesting. I will definitely look at #820 and see how we can use it. I understand this is very much necessary when receiver is contact.

@Physes
Copy link

Physes commented Apr 12, 2018

I think there might be some overlap here with the ERC I've proposed called Delegated Non-Fungible Tokens: #994

The use case I was targeting was property registry, which requires a kind of "base ownership" at the level of zones, and the ability to delegate physical property rights at a higher level. Perhaps there might be a possibility of merging?

@jdkanani
Copy link
Author

@Physes Is there any way I can connect with you directly - chat/call? Would love to talk more on #994 and our goals on #981.

@Physes
Copy link

Physes commented Apr 13, 2018

Sure thing- email philip@nuhanse.network.

@brijakodo
Copy link

@Physes #994 looks really interesting, in fact we were just working on extending #981 based on suggestions made in this blog post:
https://medium.com/@apompliano/digital-securities-unlocking-the-cheapest-capital-27d913e0633d

@ricburton
Copy link

I love this concept but keep thinking about a better way to phrase it. Things that spring to mind:

Division into pieces
Partial ownership
Tokenization of a single asset
Part-ownership
Split tokens
Token splits

Just ideas to play with :)

@Physes
Copy link

Physes commented Apr 13, 2018

For the record, I would also suggest renaming this to "Delegated Non-Fungible Tokens" (DNFTs) and including a "delegate" and "revoke" function. This would make a tree-like structure with a base DNFT being able to delegate children, with the ability to revoke (that can be superseded by a conditional owning contract between both parties).

If this was implemented in 981 I would be happy to merge 994 because this would meet the needs of the Zone Protocol property registry system.

With this structure of DNFTs you can have all of those features described in the quobands white paper: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3107645.

@brijakodo
Copy link

@Physes it was great talking to you yesterday, JD and I had a discussion post our call.

For now, we would keep the name "Barter Token" but use ERC981 more to publicize the ERC standard rather than our proposed name. This way as people extend the parent Interface to whatever their use case, their own Token Name is used in their project. At a philosophical level, we are "Bartering" this code with the community in exchange for their feedback, knowledge and support :)

Regarding "delegate" and "revoke" functions, we feel that same can be accomplished through Sigs, but you are welcome to write optional methods to extend as you feel. But we would like to keep the original proposal simple and elegant.

@brijakodo
Copy link

@ricburton thanks for the feedback and suggestions! We have already started using "Partial/Part Ownership" in our discussions and people are starting to get it.

However, when we use Token related nomenclature you suggested most non-crypto founders and asset owners glaze over it. We are trying to figure out the balance of language that is representative of the concept and easy to understand for all types of folks.

@PhABC
Copy link
Contributor

PhABC commented Apr 25, 2018

Any implementation of ERC-981 @brijakodo @jdkanani? Want to check efficiency for batch child transfers.

@jdkanani
Copy link
Author

jdkanani commented May 1, 2018

@PhABC sorry was caught up with work. I will update here once it's done.

@jdkanani jdkanani changed the title ERC981: Barter Token Standard ERC981: Partial Ownership Standard May 15, 2018
@Jason-splyt
Copy link

Have there been any on-chain implementations of this yet? We are shopping around for a sensible "fractional ownership" framework. This one could make sense.

Has there been any thought into consensus-forming/decision-making by owners of one collective asset? I.e. how this allows fractional owners for art to decide who to leave the art with (and pay them) or how this allows fractional real estate owners to collectively sign/pay a housekeeper.

@jjalan
Copy link

jjalan commented Oct 17, 2018

I attempted to implement ERC981 (attached). I appreciate your feedback @jdkanani @brijakodo

PRTToken.txt

Few questions / comments:

  1. I created a constructor that would allow setting the maximum amount associated with mother asset. This would create first token which can later be used to divide into child tokens.

  2. For merge, I am not clear the purpose of amount argument. Right now, I am simply transferring amount associated with each token present in tokenId array. How this values should be used?

uint256 totalAmountToBeTransferred = 0;
uint256 i = 0;
for(i = 0; i < _tokenIds.length; i = SafeMath.add(i, 1)) {
  totalAmountToBeTransferred = SafeMath.add(totalAmountToBeTransferred, tokensMapById[_tokenIds[i]].tokenAmount);
  tokensMapById[_tokenIds[i]].tokenAmount = 0; // Set token amount to 0 after we transfer the token amount
}
uint256 newTokenId = _createToken(msg.sender, totalAmountToBeTransferred, tokenType, metadata);

@Bluechip23
Copy link

Currently building a project that could utilize this type of work. Has there been any updates from 3 years ago?

@github-actions
Copy link

There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Dec 18, 2021
@github-actions
Copy link

github-actions bot commented Jan 1, 2022

This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment.

@github-actions github-actions bot closed this as completed Jan 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants