Voting v1 single delegation

voting

#1

Started this post as a Github issue on aragon-apps, but decided to post it here first because there are some open questions we should discuss before jumping into implementation.

Given that our naïve liquid democracy implementation has been tabled due to scalability and technical concerns we should start considering implementing one-level delegation directly into the Voting app. Adding this feature should be possible through an on-chain upgrade of the current Voting implementation.

Requirements

  • A token holder (A) should be able to express to the Voting app that they wish to allow another account (B) to vote on their behalf.
  • B can have more than one token holder delegating to them.
  • If B delegates their voting power to someone else (C), C will only be delegated whatever voting power B has on their own (their own tokens) and not everything that has been delegated to B.
  • At any time A can overrule whatever B voted. If B changes their vote later, they wouldn’t be overruling A's overrule.
  • If A votes on a proposal before their delegate B does, whenever B votes they won’t be casting a vote on behalf of A since they already voted.
  • B shouldn’t have to incur on an extra gas cost because they are voting on behalf of someone else (voting cost should be constant regardless of delegate count).
  • B should be able to signal that they are an active delegate for passive token holders to discover and delegate to them.
  • A should be able to see what B voted (or whether B abstained) on their behalf for on-going votes and all past votes since the delegation happened.
  • A should be able to revoke their delegation at any time, removing any on-going casted votes by B on A's behalf.

Open questions

Constant voting cost for delegates

In order to achieve constant cost for the delegate, we need to have precomputed the total amount of delegated tokens a delegate has at any given time. In order to do this, we would need to execute a hook from the Token Manager’s onTransfer function (or potentially implement an intermediate Delegation management app that all the Voting apps in the DAO can use) to the Voting app to notify on every balance change so we can update the delegate’s total voting power.

In the context of using simple delegation for AGP voting we would need to transfer the controller of ANT to a contract that provides the Voting apps with this balance change notification (this will come at the expense of making ANT transfers even more expensive, ~3-7k gas more).

If used just for AN voting, the Staking app would need to implement the discussed stake change hook functionality that we decided to not implement for now.

Late delegate vote

There is a potential attack in which a delegate waits until the very last moment to vote on their delegate’s behalf for something that they think their delegates won’t agree with, but making it impossible for their delegates to notice and overrule.

This could be solved by adding a deadline for delegates to vote on behalf of others. After this only direct votes and overrules can be submitted, the deadline should be enough time for people to notice what their delegate voted and overrule it if needed. This should be a parameter configurable in every DAO, but I consider that a minimum of 24 hours are needed for this attack to be neutralized.

Checkpointing account delegates

In order to prevent an array of interesting problems I believe we should checkpoint an account’s delegates by block number. We can use the checkpointing library that both Staking and the LD implementations use.


#2

Hey Jorge, really interesting post – I have a few questions:

  1. Does token holder always equal an individual’s account or could it also equal an organization or a group of organization’s members?

  2. How is voting power delegated and revoked? Will it be metadata associated to the voting account/token holder? Will this be stored on-chain? How do you imagine this process happening? Within the voting app itself? Through permissions or another authorized app?

  3. In the current voting app voting power is non transferable, so what are the implications for deployed orgs (and other applications) of adding this new functionality to it VS creating another vote delegation app?

  4. Should be there a max or min nº of votes that can be delegated to a single token holder that acts as delegate?

  5. What’s the difference between delegating voting power (in general) and delegating the action of casting 1 vote for a specific domain? For example, could you delegate your voting power for all the votes on ‘x’ domain?

  6. My assumption is that we’ll need a way to show the history of voting power delegation from specific (verified?) accounts. And possibly, a history of past votes per account too. We cannot rely on people remembering all their past actions. Where and how will this data be stored and fetched?

  7. We’ll also need a way to signal power delegation changes, for example, make B aware that A has overruled their vote. How do you think this info should be communicated?

  1. Does the above mean that delegation of voting power is not effective until the delegate casts the vote? And shouldn’t we try to prevent this in the first place? Meaning, communicating to B that can’t casts the vote on behalf of A before B spends time deciding on the issue and trying to do it.

  2. Discovery of active delegates – I feels this has a lot of unfolded complexity and also might be related to contextual identity. Must a delegate belong to the same org that the token holder that acts on behalf of? Could this delegates belong to the same pool of domain expertise?

  1. Does the above imply having some sort of filtering functionality added to the voting app, for example, to view only votes (ongoing, past and abstained) by B? It’d be great to explore this further and understand what additional filters/faceted search would be useful here.

#3

I will read it again and think more about it. The requirements make a lot of sense to me, I want to think a little bit about the implementation details.

About this:

I also think we should consider the idea of creating a new app. For already deployed orgs, but also because some DAOs might want not to allow delegation. And well, you know, for that Unix principle too… :wink:


#4

It would be an address, which may be an individuals externally owned account (key) or a contract address controlled by one or more users (e.g. the actor app).

I think it is reasonable for delegative voting to be a separate app, but could see an argument for including it in the existing voting app under the pre-text that it is important for users to be able to participate in voting from cold storage.

There are interesting things that could be done here (you could apply a quadratic dampening function to vote balances from a single source for example, this is what Status is currently doing with their signaling app). However, I would consider that a completely different voting process with different objectives and outside the scope of delegation.

I believe that this proposes that delegation happens in the voting app, and therefore you could have a different delegate for each voting app instance installed in the organization. So if you have a voting app instance for finance and a voting app instance for contract upgrades you could have different delegates for each.

This feels like a nice to have, as opposed to a strict requirement initially in my opinion. I think we just need to be able to display in the voting interface how your voting authority was used on a given vote.

In the long run offering more visibility into delegation history would be good: how delegates have voted, how many people, how much voting authority is delegated to specific addresses is helpful information.

I’m not sure this is necessary, as I don’t think that information has any bearing on A or B’s actions. I think the more important notification is if A delegates to B, and then B delegates to C, A needs to know that unless they re-delegate to someone else who plans to vote directly their vote will not get cast.

Again I think this is important in the long run, but less so in the short-run. In the short-run we can probably assume that most people will delegate to active/public members of the community or to organizations like the community cooperative and likely those members will publicly state their positions and commitments to vote.

I don’t think this is something to prevent it is the expected behavior, If A votes it should always take precedence over what B does.

Also don’t think it is necessary for B to be aware or care about who is delegating to them, they vote as they normally would, and if someone has delegated to them then their vote just carries additional weight.

Agree lots of complexity here, but I think addressing that complexity can be out of scope for this. We can assume that people will communicate out of band whether they plan on voting directly, users may campaign based on their overall positions on the forum etc to attract delegates, we do not need to facilitate discovery imo.

Agree, I think this along with the comments above having additional information about voting behaviors available and well organized should be explored (though I think it can largely be deferred for now).


#5

Good point!
(I’m adding void here because: “Post must be at least 20 characters”??)


#6

Thinking more about it, I also think that some use cases could rather use a simple voting app, like a Multisig for instance.


#7

I think this is a compelling reason to have two versions of the Voting app.

It also brings attention to a relatively small but important issue–when an organization installs apps they probably need a way to rename them in the sidebar. EG the name of the voting app might be “Delegative Voting” or “Simple Voting” but the side bar should say “Member Voting” or “Board Voting”. The technical application name is not necessarily important to the users of a DAO, but the context in which it is used is important!

edit: cc @stellarmagnet @Paty


#8

If minting new tokens is allowed, a hook from the mint function (or whatever calls token.generateTokens) should be added too, right?