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

v2.7.0 - Vote Delegation, Initial Actions, Deep Instantiate2 Support #888

Open
wants to merge 5 commits into
base: development
Choose a base branch
from

Conversation

NoahSaso
Copy link
Member

@NoahSaso NoahSaso commented Oct 10, 2024

DAO Vote Delegation

The dao-vote-delegation contract allows members of a DAO to delegate their
voting power to other members of the DAO who have registered as delegates. It
works in conjunction with voting and proposal modules to offer a comprehensive
delegation system for DAOs that supports the following features:

  • Fractional delegation of voting power on a per-proposal-module basis.
  • Delegate votes that can be overridden on a per-proposal basis by each
    delegator.

Instantiation and Setup

This contract must be instantiated by the DAO. It is queried by proposal modules when users cast votes to determine how much voting power to tally on a proposal, in addition to a user's normal voting power derived from the voting module.

Hooks

After instantiating the contract, it is VITAL to set up the required hooks for
it to work. To compute delegate voting power correctly, this contract needs to
know about both voting power changes and votes cast on proposals as soon as they
happen.

This can be achieved using the add_hook method on voting/staking contracts
that support voting power changes, such as:

  • cw4-group
  • dao-voting-cw721-staked
  • dao-voting-token-staked
  • cw20-stake

For proposal modules, the corresponding hook is add_vote_hook:

  • dao-proposal-single
  • dao-proposal-multiple
  • dao-proposal-condorcet

Design Decisions

Fractional Delegation via Percentages

In order to support fractional delegation, users assign a percentage of voting
power to each delegate. Percentages are used instead of choosing an absolute
amount of voting power (e.g. staked tokens) since voting power can change
independently of delegation. If an absolute amount were used, and a user who had
delegated all of their voting power to a few different delegates then unstaked
half of their tokens, there is no clear way to resolve what their new
delegations are. Using percentages instead allows voting power and delegation to
be decided independently.

Implementation Notes

The trickiest piece of this implementation is navigating the snapshot maps,
which are the data structures used to store historical state.

Essentially, snapshot maps (and the other historical data structures based on
snapshot maps) take 1 block to reflect updates made, but only when querying
state at a specific height (typically in the past). When using the query
functions that do not accept a height, they read the updates immediately,
including those from the same block. For example, snapshot_map.may_load
returns the latest map values, including those changed in the same block by an
earlier transaction; on the other hand, snapshot_map.may_load_at_height
returns the map values as they were at the end of the previous block (due to an
implementation detail of snapshot maps that I'm not sure was intentional).

Ideally, we would just fix this discrepancy and move on. However, many other
modules have been built using SnapshotMaps, and it is important that all modules
behave consistently with respect to this issue. For example, voting power
queries in voting modules operate in this way, with updates delayed 1
block—because of this, it is crucial that we compute and store delegated voting
power in the same way. Otherwise we risk introducing off-by-one inconsistencies
in voting power calculations. Thus, for now, we will accept this behavior and
continue.

What this means for the implementation is that we must be very careful whenever
we do pretty much anything. When performing updates at the latest block, such as
when delegating or undelegating voting power, or when handling a change in
someone's voting power (in order to propagate that change to their delegates),
we need to be sure to interact with the latest delegation and voting power
state. However, when querying information from the past, we need to match the
delayed update behavior of voting power queries.

More concretely:

  • when registering/unregistering a delegate, delegating/undelegating, or
    handling voting power change hooks, we need to access the account's latest
    voting power (by querying latest_height + 1), even if it was updated in the
    same block. this ensures that changes to voting power right before a
    registration/delegation occurs, or voting power changes right after a
    delegation occurs, are taken into account. e.g. an account should not be able
    to get rid of all their voting power (i.e. stop being a member) and then
    become a delegate within the same block.
  • when delegating/undelegating or handling voting power change hooks, in order
    to update a delegate's total delegated VP, we need to query the latest
    delegated VP, even if it was updated earlier in the same block, and then
    effectively "re-prepare" the total that will be reflected in historical
    queries starting from the next block. snapshot_map.update takes care of this
    automatically by loading the latest value from the same block.
  • when querying information from the past, such as when querying a delegate's
    total unvoted delegated VP when they cast a vote, or when a vote cast hook is
    triggered for a delegator, we need to use historical queries that match the
    behavior of the voting module's voting power queries, i.e. delayed by 1 block.

Initial Actions

The creator of the DAO can specify actions to be executed on behalf of the DAO during instantiation. A query is added so that the initial actions can be queried, which the UI should clearly display in order to address security concerns by allowing the DAO creator to arbitrarily execute messages on behalf of the DAO.

Other refactors

ModuleInstantiateInfo, which is used to add modules to the DAO, now contains an optional salt field, which will use instantiate2 to instantiate contracts instead of the normal instantiate so that the caller can predict the addresses of any module it is adding. This also replaces many hardcoded WasmMsg::Instantiate definitions with ModuleInstantiateInfo to allow other modules to take advantage of this mechanism and let the caller decide whether or not to use instantiate2. For example, the dao-voting-cw20-staked contract can optionally instantiate the cw20-stake contract with instantiate2 or not—same for dao-voting-cw4's cw4-group contract, among other contracts.

This change is necessary to make the initial actions useful, as the caller can create initial actions that operate on the voting and proposal modules (and they'd need to know the addresses before they're instantiated to do so).

Also, a while ago, the funds field was added to ModuleInstantiateInfo, making some versions of modules incompatible with other versions. This refactor makes that optional, so that old versions should work fine with newer modules, provided they still use the same interfaces (which we have never changed).

@NoahSaso NoahSaso force-pushed the noah/delegations branch 6 times, most recently from f35c887 to 92fed36 Compare October 11, 2024 20:17
Copy link

codecov bot commented Oct 12, 2024

Codecov Report

Attention: Patch coverage is 98.43023% with 81 lines in your changes missing coverage. Please review.

Project coverage is 96.73%. Comparing base (011ee2b) to head (dadd461).

Files with missing lines Patch % Lines
...tracts/delegation/dao-vote-delegation/src/hooks.rs 85.41% 21 Missing ⚠️
...cts/delegation/dao-vote-delegation/src/contract.rs 96.66% 18 Missing ⚠️
...l/dao-proposal-multiple/src/testing/instantiate.rs 74.46% 12 Missing ⚠️
packages/dao-interface/src/helpers.rs 58.82% 7 Missing ⚠️
...cts/voting/dao-voting-token-staked/src/contract.rs 0.00% 6 Missing ⚠️
...tracts/delegation/dao-vote-delegation/src/error.rs 0.00% 4 Missing ⚠️
packages/dao-voting/src/delegation.rs 95.18% 4 Missing ⚠️
packages/cw-snapshot-vector-map/src/lib.rs 98.24% 3 Missing ⚠️
...acts/delegation/dao-vote-delegation/src/helpers.rs 98.85% 1 Missing ⚠️
...ontracts/delegation/dao-vote-delegation/src/msg.rs 66.66% 1 Missing ⚠️
... and 4 more
Additional details and impacted files
@@               Coverage Diff               @@
##           development     #888      +/-   ##
===============================================
+ Coverage        96.64%   96.73%   +0.09%     
===============================================
  Files              245      258      +13     
  Lines            66833    70922    +4089     
===============================================
+ Hits             64592    68609    +4017     
- Misses            2241     2313      +72     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@NoahSaso NoahSaso mentioned this pull request Oct 17, 2024
@NoahSaso NoahSaso force-pushed the noah/delegations branch 5 times, most recently from d46af09 to ae17307 Compare October 18, 2024 14:00
@NoahSaso NoahSaso force-pushed the noah/delegations branch 2 times, most recently from 2e4cc61 to 92efcc1 Compare November 1, 2024 18:10
@NoahSaso NoahSaso force-pushed the noah/delegations branch 4 times, most recently from 5cb1d01 to 7b8d966 Compare December 24, 2024 20:36
@NoahSaso NoahSaso force-pushed the noah/delegations branch 2 times, most recently from c73d7f3 to f80833e Compare January 22, 2025 06:17
@NoahSaso NoahSaso marked this pull request as ready for review February 4, 2025 18:34
@NoahSaso NoahSaso changed the title delegations Vote Delegation Feb 4, 2025
@NoahSaso NoahSaso requested a review from bekauz February 25, 2025 00:57
@NoahSaso NoahSaso changed the title Vote Delegation Vote Delegation and Initial Actions Feb 25, 2025
@NoahSaso NoahSaso changed the title Vote Delegation and Initial Actions v2.7.0 - Vote Delegation, Initial Actions, Deep Instantiate2 Support Feb 26, 2025
@NoahSaso NoahSaso force-pushed the noah/delegations branch 7 times, most recently from d4de204 to cdc8a91 Compare February 26, 2025 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants