-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
ERC 1410: Partially Fungible Token Standard #1410
Comments
Operators may not be required when using this ERC. I would suggest the functionality relating to operators be separated in the interface and described as optional to support use cases where it is not required for the functionality of an SFT. The implication of this is that the e.g. |
If that is the case, I would use some other terminology because that is confusing. Also, I think that the "multiple send" variants of the functions specified by the standard ( |
re. My thinking was that you may want to be able to transfer from multiple tranches when using the For example, you could define a users default tranches as This does however imply that the ordering of Suppose Alice calls
This opens up another question which is whether the These "multiple send" functions are then a bit different to the more common Interested in your thoughts on the above! Will think about the best way to clarify the operators within this EIP. |
Yeah, the ordering thing I think is very important if you are attempting to define complex transfers within the standard. If you left it as one default, that would be equally as useful if the transfers were more straightforward. When I was originally thinking of my own PFT use case, I was considering the "tranches" to be an enumeration, such that I could specify a global order within the standard by just numbering them in that way. It makes sense that different users may want to specify different orders though, it just becomes much more difficult to deal with. Removing the ability to send multiple tranches is an option that may be useful. It makes the underlying implementation a bit simpler to deal with, although clients would then need to submit multiple requests if moving from different tranches. But, in my opinion, clearer is better. Lastly, the event |
Another potential use case: Coin voting A token could have a voting flow that would taint coins already used for voting, "locking" them against the possibility of future voting by disallowing re-use of tainted tokens until the voting was over. This would be an excellent tool against concerns arising from non-locking token votes, where users can simply transfer their tokens to a new address to get around vote sanctions on token holders. |
@ALL We have scheduled a second community call for 11am EST, Tuesday, October 2nd. We will try using Zoom this time as we hit the 25 attendee limit on Google Hangouts on the last call. Ethereum Magicians thread for the call is at: Please use the HackMD document linked here to add any agenda items and your details if you're able to attend. |
@fubuloubu - re. tranches as an enumeration - I’ve been thinking of them more like primary keys for metadata. So they could be something like I wrote a medium article explaining a bit more along these lines: I cleaned up the:
to take multiple tranche values but a single destination address. I split out the change of tranche from the I moved the |
For simplicity's sake, can the base standard have For example, you might want to send amounts from tranches A, B, and C, but they have complicated inter-dependancies leading you to only send one at a time. The basic "singular send" would let you do that independantly if the multiple send was failing due to those dependencies. I believe strongly the basic sending mechanism be as atomic as possible without involving too much complexity in the priority scheme of handling multiple tranches. |
Interesting dialog. I'm not technical, so I can't follow the technical discussion fully, but happy to contribute knowledge for my experience in finance and in law. |
@adamdossa Would #1410 be helpful for distinguishing between affiliate and non-affiliate tokens? Also, are there mechanisms to expand or contract a tranche, or convert one tranche into another tranche? Apologies, if this should be clear from the code; you know I'm not technically inclined. |
@jllaw - Yep - you could distinguish between affiliate / non-affiliate tokens using tranches (i.e. having a tranche called It is possible to move tokens between tranches (e.g. from non-affiliate to affiliate) and this logic can be determined on-chain (in the |
What’s the point of having the standards so specific they are practically designed to work for specific platforms? You might want to have a look at ERC-1462 |
Well, I think that's unfair. This team has taken advice to segregate different parts of their proposal that had alternative use cases (like this PFT spec) with the goal of advancing other possible use cases. I know I have been interested in a partially fungible token spec exploring ideas I've had with reputation tokens, which may be held in different contexts. |
@morpheus499 we don't have a hidden agenda beyond trying to push forward the conversation on standards for security tokens (and other more sophisticated token types). I have looked at ERC 1462 and there is certainly an argument for a simpler standard which we've discussed openly on our open community calls. My view is that it is better to try and design a future proofed standard that acknowledges that tokens can be more complex than what is easily represented with ERC20 - some of the potential use-cases for more sophisticated tokens (outside of security tokens) are in the article: Being able to check the validity of transactions before executing them (which is covered by #1462) is certainly one part of the puzzle, but IMO we also need the other parts (differentiated ownership, ability to inject off-chain data into transfer operations and so on). |
Hey all, I had a great chat with @thegostep today. I think ERC20 compatibility is really important for many reasons, so I have a proposal for an implementation that preserves this past the idea of “default tranches”. contract ERC1410v2Tranche extends ERC20 {
function getTrancheId() public view returns (bytes32 trancheId);
}
contract ERC1410v2 extends ERC1410 {
mapping (bytes32 => ERC1410v2Tranche) tranches;
constructor() public {
tranches[keccak256("gold")] = new ERC1410v2Tranche();
tranches[keccak256("silver")] = new ERC1410v2Tranche();
}
function balanceOfByTranche(bytes32 _tranche, address _tokenHolder) external view returns (uint256) {
require(tranches[_tranche] != address(0));
return tranches[_tranche].balanceOf(_tokenHolder);
}
// ...
} Downsides:
Upsides:
|
As part of the Ethereum Magicians hosted Council of Prague before DevCon some of us will be attending the Token Ring (Monday 29th October) to discuss ERC 1400 / security token standards. Some links to sign up for this and mark your attendance are: |
I added a reference implementation for ERC 1410 - still some improvement to be done to factor out common base classes or make a robust implementation, but hopefully good reference for anyone looking at these standards. |
Any progress on this, or further thoughts? I am trying to see the utility of the proposed The securities that I'm aware of using a tranche system (CDOs, corporate bonds) do so because each "tranche" has different properties; really, they are different securities and trade as such (i.e., there is a market for senior bonds and a separate market for junior bonds, etc). Therefore my gut instinct is that the most basic security token is actually a single tranche, which is probably closer to ERC1462 (#1462). |
In a word, convertibility. With multiple tokens, you would have to design a complicated atomic swap, mint/burn or locking system to trade vesting to unlimited shares, and accurately represent the restrictions on tradability on multiple tokens that essentially represent the same thing. If they are all subcategories of the same token, that logic is much simpler to represent. I am not a "security tokens" guy, but that much makes sense to me. This proposal also has wider utility. Allowing subcategories for a general token (in a non-security use case) would allow different ideas like locking voting tokens temporarily or making allocations to something like staking without delegating control to a smart contract you don't necessarily trust. That little bit of state helps have additional logic for transferability be much easier to implement. I do still wish |
That's not it at all. Tranche is probably a bad word, because it implies that the securities in the different tranches are different or have different rights -like the tranches of an MBS. However, that is NOT the purpose of this particular data structure. This data structure describes securities that are the SAME security, but issued on different days, or in different jurisdictions. The most important use case is to implement the rules of US Regulation D and Regulation S. For example, I can issue the SAME common stock in many different events. The stock that I sell to US investors has a one year lockup. So, stock issued on each date can go into a separate tranche that tracks the origination date. There are many ways to implement lockup, so this tranche data structure is not required. However, it IS required for the stock that I sell to non-US investors under Reg S. This is covered by a rule that says the stock cannot be sold back to US investors until one year has passed. So, we have to track each share of stock that was issued on that date (using a tranche) to see if those shares have been outstanding for a year. It also happens that securities jump from Reg D into Reg S, and the date of issue follows them, through purchase by a large fund that is exempt from Reg D under rule 144a. You might end up with 20 different issuance dates and jurisdictions. For most purposes, we don't care about the different tranches. We only care about the full set of outstanding securities for voting, and distribution, and trade volume restrictions. And, in most cases these tranches would collapse to the same security after a year or so. Keeping track of 20 different tokens in that case, and adding them up, and recombining them, would be silly. |
This type of tranche would describe different issuances of the same security. As a corollary, if you have several securities from one issuer, but with different rights, I think you should make different tokens. |
@fubuloubu @zingleton Thanks for the detailed responses. The examples given make a lot of sense - especially the point about lock-up times/issuance dates/recombining being a nightmare with separate tokens. I can definitely see advantages to this approach. Since it seems to be a potential source of confusion (at least in my case), perhaps the name should be changed from |
@adamdossa What do you think about changing |
Also, one point. Again I am not an expert on securities at all, but focusing on US requirements is too narrowly focused to define a standard on. ERC standards are all about giving developers tools to build different use cases with, one of the main reasons I pushed back against the original monolithic proposal. As noted, the fact is you really have multiple options for implementing a "security token" using this ERC, other ERCs (like using multiple ERC20s), or any combination of the above. It depends on what makes sense for how you're implementing it. A lot of people think ERC proposals are somehow a "stamp of legitimacy", when in reality it's just an idea. Unlike core protocol EIPs, ERCs just serve as a rallying point for people to adopt them. By focusing on something more broadly useful, that means faster adoption of the ERC, which means it's "legitimacy" increases with each adoption. |
@ParkinsonJ I want to point you to this WIP update to the 1400 proposals. It includes renaming 'tranches' to 'partitions' in order to remove the association with MBS and CDOs. |
@thegostep Thank you, I shall take a look at it. Sounds like a positive step! @fubuloubu I think that's a fair point, although as you've pointed out the partitioning is relevant to a berth of applications outside of US securities. Regarding "legitimacy", I'm not sure I agree in this particular context if only because a securities platform is not very useful if it only works with a small fraction of security tokens. It's utility that is driving the push for a standard - utility for platform builders, token designers and ultimately end users (who then won't need to use different tokens on different platforms, etc). |
Oh yeah, don't get me wrong, the securities use case is a really big driver of adoption, in the same way it has also driven the push for defining multiple, "competing" standards, basically all at the same time. I'm saying that non-security use cases can be a way to help engender adoption of a particular standard, as you have defined something more generally useful than if it were focused more towards one niche of the community. |
Following feedback from the token ring, community calls, GitHub & the Telegram group, I have made the following changes: Motivation
Updates
The changes in this update along with some related conversation are collected in a GitHub PR at: |
@adamdossa Spec says |
Hi @adamdossa, Kindly help me on below points.
This function MUST throw if the transfer of tokens is not successful for any reason. The function MUST return the bytes32 _partition of the receiver. Does that mean, when we sends tokens from a perticular _partition to receiver, it generates a new _partition key? If thats true then what if the receiver partition is already holding the partition tokens (simliar attribute of tokens)
|
hiya @adamdossa: "Modify ERC-1400 to be an umbrella of ERC-1410, ERC-1594, ERC-1643 and ERC-1644 with a few additional constraints to make these standards interoperate." Could you please elaborate on this or drop some #umbrella #constraint markers into the specification itself. I'm interested to know what are the sticking points for these standards to interoperate. It sounds like good work, kudos! |
Agreed - just updated to fix this - thanks for spotting. |
Partition keys can be anything - for example, in our current implementation we are using LOCKED and UNLOCKED as the only partitions we support. One concern with this approach though that came up during implementation is what happens if transferred tokens end up in two (or more) partitions. We took the view that this returned partition key is an "indicative" value. A more precise thing to do might be to return a list of [amount, partition] pairs which fully describe the destination partitions of the transferred tokens. |
There weren't a lot of constraints - mainly just making sure that operators etc. were treated consistently across the different standards. Ideally every standard in the ERC1400 umbrella would be able to be combined with every other standard which does mean that they need to be consistent if they are using concepts which are defined in other standards (for example operators). |
"Operators can be authorised at by individual token holders for either all partitions, or a specific partition." ^typo Re: Operator Aggregation. It seems to be something important where it concerns trust networks —for example, the Circles design (as well as others, BrightId, etc.) In any case, any old list of operators would get the job done, I suppose. With an aggregate model, however, we would have more abstraction of details away from the organisations that will ostensibly be using these things — or away from the user— which is a basic side-effect of employing such standards per se. |
Fixed typo - thanks. Do you have a link to some more details on the aggregate operator model? |
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. |
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. |
eip: ERC-1410
title: Partially Fungible Token Standard (part of the ERC-1400 Security Token Standards)
author: Adam Dossa (@adamdossa), Pablo Ruiz (@pabloruiz55), Fabian Vogelsteller (@frozeman), Stephane Gosselin (@thegostep)
discussions-to: #1411
status: Draft
type: Standards Track
category: ERC
created: 2018-09-13
require: ERC-1066 (#1066)
Simple Summary
A standard interface for organising an owners tokens into a set of partitions.
Abstract
This standard sits under the ERC-1400 (#1411) umbrella set of standards related to security tokens.
Describes an interface to support an owners tokens being grouped into partitions, with each partition being represented by an identifying key and a balance.
Tokens are operated upon at a partition granularity, but data about the overall supply of tokens and overall balances of owners is also tracked.
This standard can be combined with ERC-20 (#20) or ERC-777 (#777) to provide an additional layer of granular transparency as to the behaviour of a token contract on different partitions of a token holders balance.
Motivation
Being able to associate metadata with individual fungible tokens is useful when building functionality associated with those tokens.
For example, knowing when an individual token was minted allows vesting or lockup logic to be implemented for a portion of a token holders balance.
Tokens that represent securities often require metadata to be attached to individual tokens, such as restrictions associated with the share.
Being able to associate arbitrary metadata with groups of tokens held by users is useful in a variety of use-cases. It can be used for token provenance (i.e. recording the previous owner(s) of tokens) or to attach data to a token which is then used to determine any transfer restrictions of that token.
In general it may be that whilst tokens are fungible under some circumstances, they are not under others (for example in-game credits and deposited balances). Being able to define such groupings and operate on them whilst maintaining data about the overall distribution of a token irrespective of this is useful in modelling these types of assets.
Having a standard way to identify groupings of tokens within an overall balance helps provides token holders transparency over their balances.
Rationale
A Partially-Fungible Token allows for attaching metadata to a partial balance of a token holder. These partial balances are called partitions and are indexed by a
bytes32 _partition
key which can be associated with metadata on-chain or off-chain.The specification for this metadata, beyond the existence of the
_partition
key to identify it, does not form part of this standard. The token holders address can be paired with the partition to use as a metadata key if data varies across token holders with the same partition (e.g. a "restricted" partition may be associated with different lock up dates for each token holder).For an individual owner, each token in a partition therefore shares common metadata.
Token fungibility includes metadata so we have:
Note - partitions with the same
bytes32
key across different users may be associated with different metadata depending on the implementation.Backwards Compatibility
This standard is un-opinionated on ERC-20 vs. ERC-777. It can be easily combined with either standard, and we expect this to usually be the case. We don't define the standard token view functions (
name
,symbol
,decimals
) as a consequence.In order to remain backwards compatible with ERC-20 / ERC-777 (and other fungible token standards) it is necessary to define what partition or partitions are used when a
transfer
/send
operation is executed (i.e. when not explicitly specifying the partition). However this is seen as an implementation detail (could be via a fixed list, or programatically determined). One option is to simple iterate over allpartitionsOf
for the token holder, although this approach needs to be cognisant of block gas limits.Specification
Token Information
balanceOf
Aggregates a token holders balances across all partitions. Equivalent to
balanceOf
in the ERC-20/777 specification.MUST count the sum of all partition balances assigned to a token holder.
balanceOfByPartition
As well as querying total balances across all partitions through
balanceOf
there may be a need to determine the balance of a specific partition.For a given token holder, the sum of
balanceOfByPartition
acrosspartitionsOf
MUST be equal tobalanceOf
.partitionsOf
A token holder may have their balance split into several partitions (partitions) - this function will return all of the partitions that could be associated with a particular token holder address. This can include empty partitions, but MUST include any partitions which have a non-zero balance.
totalSupply
Returns the total amount of tokens issued across all token holders and partitions.
MUST count all tokens tracked by this contract.
Tokens Transfers
Token transfers always have an associated source and destination partition, as well as the usual amounts and sender / receiver addresses.
As an example, a permissioned token may use partition metadata to enforce transfer restrictions based on:
_partition
value_partition
value (e.g. a lockup timestamp that may be associated with_partition
)_data
parameter allows the caller to supply any additional authorisation or details associated with the transfer (e.g. signed data from an authorised entity who is permissioned to authorise the transfer)Other use-cases include tracking provenance of tokens by associating previous holders with destination partitions.
transferByPartition
This function MUST throw if the transfer of tokens is not successful for any reason.
When transferring tokens from a particular partition, it is useful to know on-chain (i.e. not just via an event being fired) the destination partition of those tokens. The destination partition will be determined by the implementation of this function and will vary depending on use-case.
The function MUST return the
bytes32 _partition
of the receiver.The
bytes _data
allows arbitrary data to be submitted alongside the transfer, for the token contract to interpret or record. This could be signed data authorising the transfer (e.g. a dynamic whitelist), or provide some input for the token contract to determine the receivers partition.This function MUST emit a
TransferByPartition
event for successful transfers.operatorTransferByPartition
Allows an operator to transfer security tokens on behalf of a token holder, within a specified partition.
This function MUST revert if called by an address lacking the appropriate approval as defined by
isOperatorForPartition
orisOperator
.This function MUST emit a
TransferByPartition
event for successful token transfers, and include the operator address.The return data is interpreted consistently with
transferByPartition
.canTransferByPartition
Transfers of partially fungible tokens may fail for a number of reasons, relating either to the token holders partial balance, or rules associated with the partition being transferred.
The standard provides an on-chain function to determine whether a transfer will succeed, and return details indicating the reason if the transfer is not valid.
These rules can either be defined using smart contracts and on-chain data, or rely on
_data
passed as part of thetransferByPartition
function which could represent authorisation for the transfer (e.g. a signed message by a transfer agent attesting to the validity of this specific transfer).The function will return both a ESC (Ethereum Status Code) following the EIP-1066 standard, and an additional
bytes32
parameter that can be used to define application specific reason codes with additional details (for example the transfer restriction rule responsible for making the transfer operation invalid).It also returns the destination partition of the tokens being transferred in an analogous way to
transferByPartition
.Operators
Operators can be authorised by individual token holders for either all partitions, or a specific partition.
authorizeOperator
,revokeOperator
,isOperator
)authorizeOperatorByPartition
,revokeOperatorByPartition
,isOperatorForPartition
)authorizeOperator
Allows a token holder to set an operator for their tokens across all partitions.
MUST authorise an operator for all partitions of
msg.sender
This function MUST emit the event
AuthorizedOperator
every time it is called.revokeOperator
Allows a token holder to revoke an operator for their tokens across all partitions.
NB - it is possible the operator will retain authorisation over this token holder and some partitions through
authorizeOperatorByPartition
.MUST revoke authorisation of an operator previously given for all partitions of
msg.sender
This function MUST emit the event
RevokedOperator
every time it is called.isOperator
Returns whether a specified address is an operator for the given token holder and all partitions.
This should return TRUE if the address is an operator under any of the above categories.
MUST query whether
_operator
is an operator for all partitions of_tokenHolder
.authorizeOperatorByPartition
Allows a token holder to set an operator for their tokens on a specific partition.
This function MUST emit the event
AuthorizedOperatorByPartition
every time it is called.revokeOperatorByPartition
Allows a token holder to revoke an operator for their tokens on a specific partition.
NB - it is possible the operator will retain authorisation over this token holder and partition through either
defaultOperatorsByPartition
ordefaultOperators
.This function MUST emit the event
RevokedOperatorByPartition
every time it is called.isOperatorForPartition
Returns whether a specified address is an operator for the given token holder and partition.
This should return TRUE if the address is an operator under any of the above categories.
Interface
The text was updated successfully, but these errors were encountered: