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

ERC: Separating Token Balance from Voting Rights #804

Closed
pabloruiz55 opened this issue Dec 21, 2017 · 6 comments
Closed

ERC: Separating Token Balance from Voting Rights #804

pabloruiz55 opened this issue Dec 21, 2017 · 6 comments
Labels

Comments

@pabloruiz55
Copy link

pabloruiz55 commented Dec 21, 2017

Preamble

EIP: <to be assigned>
Title: Token Voting Rights Separation
Author: Pablo Ruiz <me@pabloruiz.co>
Type: Standard Track
Category: ERC
Status: Draft
Created: 2017-12-21

Simple Summary

The proposed ERC20 add-on allows the decoupling of voting rights and token balance.

Abstract

The following discusses the implementation of a backwards-compatible ERC20 add-on to separate voting rights from token balance. The idea behind this implementation is to allow tokens to have their voting rights separated from the token balance, in order to prevent -mainly- exchanges from voting using someone else's tokens while they are "deposited" on their platform.

This implementation would be backwards-compatible so that accounts can still transfer their balance normally, but for an application using this token, voting rights won't be moved around unless explicitly done by the owner of the token balance.
The applications / smart contracts basing their voting process on the token, instead of using balanceOf(address _owner) to calculate the voting weight, they would use another function votingRightsOf(address _owner) that considers not only the token balance but also the voting rights held by the accounts.

Motivation

It is a common practice to use tokens for on-chain voting as a decision mechanism. Most applications / smart contracts using tokens as a means of governance base the decision-making process on having token holders vote according to the amount of tokens they own.

Even though each application implements voting as they see fit, they base the voting process on how many tokens an account owns - or if an account has any tokens- by checking the balanceOf(address _owner).

This creates at least one big problem related to exchanges. When people transact with a token exchange platform, they are transferring their tokens to said platform, and with them, they also transfer whatever voting rights they had on an application using those tokens. For example, this can lead to exchanges holding massive amounts of a particular token, to attack a platform by voting on key decision-making processes, if they wanted to impact on a particular token's value.

Specification

TokenWithVotingRights

NOTE: The TokenWithVotingRights contract extends the ERC20 token specification, so it MUST inherit from an ERC20 contract.

Methods

votingRightsOf

Returns the voting rights a given account currently holds. An application wanting to base their voting process on tokens held, MUST use this method instead of balanceOf().
votingRightsOf(address _owner) returns the least between balances[_owner] and votingRights[_owner], which determines the account's voting weight.

function votingRightsOf(address _owner) public view returns (uint256)

transferVotingRights

Transfers _value amount of voting rights to address _to, and MUST fire the TransferVotingRights event.

function transferVotingRights(address _to, uint256 _value) public returns (bool)

transferTokensAndVotingRights

It MUST call transferVotingRights() to transfer the specified voting rights.
It MUST also call the ERC20 transfer(_to, _value) method to transfer the tokens as well.
If the _from account does not have enough voting rights, all specified tokens are still transferred as well as whatever voting rights _from had.
For example, if _from had 1000 voting rights and 2000 tokens, transferring 1500 tokens will leave him with 0 voting rights and 500 tokens and _to will have 1000 voting rights and 1500 tokens

function transferTokensAndVotingRights(address _to, uint256 _value) public returns (bool)

Events

TransferVotingRights

MUST trigger when voting rights are transferred, including zero value transfers.

A token contract which creates new tokens SHOULD trigger a TransferVotingRights event with the _from address set to 0x0 when tokens and voting rights are created.

event TransferVotingRights(address indexed from, address indexed to, uint256 value);

Rationale

This token keeps track of not only the balance held by each account, but also of the voting rights held by each of them. Here's a common scenario:

  1. Company deploys Crowdsale and Token With Voting Rights contracts (TWVR for short) and assigns initial/total supply to the contract, as well as all the voting rights to it. These TWVR can be used for on-chain voting within Company's ecosystem. Company wants to prevent an exchange, which at some point might hold many of their tokens, from being able to participate in a voting process with those tokens.

  2. During the crowdsale period, Alice contributes enough funds to buy 1000 TWVR. These tokens are transferred to her by executing transferTokensAndVotingRights(address _to, uint256 _value) instead of transfer(address _to, uint256 _value).
    Alice now has a balance and voting rights of 1000.

  3. Alice wants to transfer Bob 500 of her tokens. She has two options: a) Using transfer(address _to, uint256 _value) to send Bob the tokens without the voting rights; b) Using transferTokensAndVotingRights(address _to, uint256 _value) to transfer the tokens AND an equal number of voting rights to Bob.
    Bobs wants to be also part of the Company's decision process, so he asks Alice to transfer the tokens and voting rights.
    Both Alice and Bob now have 500 tokens and 500 voting rights.

  4. Alice wants to sell 400 of her remaining tokens on a token exchange platform (EtherD). To do so, she would just do what she always does. She enters the platform and transfers the tokens to EtherD (which SHOULD be calling the token's transfer(address _to, uint256 _value) function as usual. This would transfer 400 tokens out of Alice's balance, but she would still hold her 500 voting rights.
    Alice now has a balance of 100 and 500 voting rights.

  5. John wants to buy some tokens but he doesn't know anyone that wants to sell them to him, so he goes to EtherD exchange and buys 300 tokens.
    John now has a balance of 300 and 0 voting rights, since these tokens were sold and transferred by an account that didn't have any voting rights.

  6. Company needs to hold a voting process. Alice, Bob and John cast their votes. Company needs to check what each account's voting weight is. Instead of relying on balanceOf(address _owner) the company MUST use TWVR's votingRightsOf(address _owner) which returns whichever is the least between the token balance and the voting rights.
    Bob's vote weight is 500 since he has 500 voting rights and he owns the 500 tokens to cast such voting weight.
    Alice vote weight is 100 since, even if she has 500 voting rights, she currently only holds 100 tokens.
    John's vote weight is 0 since, even if he has 300 tokens, he has 0 voting rights.

As a side note, in the example above the Company could also extend the TWVR to only allow voting rights to be transferred from the crowdsale to the original contributors (By allowing the usage of transferTokensAndVotingRights(address _to, uint256 _value) only during the crowdsale period). Then, the only accounts able to vote would be the people that participated in the crowdsale (as long as they still hold the tokens) and prevent voting rights to be distributed among new accounts.

Implementation

Example implementation:
https://github.com/pabloruiz55/TokenWithVotingRights

Copyright

Copyright and related rights waived via CC0.

@clesaege
Copy link
Contributor

I think it's easier to make a voting right token separated from the main one for the cases you described.
About token with voting rights, you can look at the Minime https://github.com/Giveth/minime/blob/master/contracts/MiniMeToken.sol where you could fork the token to have a vote token.

@stevenh512
Copy link

stevenh512 commented Jan 15, 2018

The problem with this is, if I buy a large amount of your tokens at ICO and sell them immediately, I would retain my voting rights as if I still owned those tokens. Meanwhile, the person or people who just bought a million tokens from me get no voting rights at all unless I voluntarily transfer rights to them. If all of Ethereum's users were as tech savvy as the people here on GitHub writing code this might not be a problem, but I see questions every day on Reddit about the simplest of things (like "How do I transfer tokens from MetaMask to MyEtherWallet?"). I can't imagine something like this not being used by a malicious actor to scam a lot of people. I think using something like MiniMe, where you can either fork the token into a voting token or just query balanceOfAt at a certain block number is probably a better solution to the voting rights problem.

@AnthonyAkentiev
Copy link

@pabloruiz55 Your proposal is a compilcation. Instead, imho, it's better to use separate token with 'governance' rights only. This token will be ERC20-compatible and will not require any changes to wallets and software.

@chaitanyapotti
Copy link
Contributor

We are currently developing a membership token which is used as a proof of membership. It decouples voting rights from token balance.
The owner of an organization is free to assign/revoke memberships.
Users can request for membership to govern. They can only participate in polls if they have a min amt of tokens along with membership token.
During Governance, several types of polls can be used which check for the existence of voting rights for a particular poll using a single/combination of membership tokens.
There is game-theoretic security at stake (If he acts irrationally, people wouldn't invest in his token).
You can find more information here
Further discussion/comments are welcome.

@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 19, 2021
@github-actions
Copy link

github-actions bot commented Jan 2, 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 2, 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

5 participants