变更记录

  • 2020/10/28:初稿

状态

已接受

摘要

本 ADR 定义了对治理模块的一项修改,允许质押者将其投票拆分为多个投票选项。例如,它可以将其 70% 的投票权用于投赞成票,将 30% 的投票权用于投反对票。

背景

目前,一个地址只能使用一个选项(Yes/No/Abstain/NoWithVeto)进行投票,并将其全部投票权用于该选择。 但是,很多情况下,拥有该地址的实体并不一定是单个个人。例如,一家公司可能有希望采取不同投票立场的不同利益相关方,因此允许其拆分投票权是合理的。另一个示例场景是交易所。许多中心化交易所通常会质押一部分其托管的用户代币。目前,它们无法进行“透传投票”,也无法将用户代币对应的投票权交给用户本人。不过,借助这一系统,交易所可以先向用户征集投票偏好,再按投票结果的比例在链上进行投票。

决策

我们将投票结构体修改为
type WeightedVoteOption struct {
    Option string
  Weight sdk.Dec
}

type Vote struct {
    ProposalID int64
  Voter      sdk.Address
  Options    []WeightedVoteOption
}
为了向后兼容,我们引入 MsgVoteWeighted,同时保留 MsgVote。
type MsgVote struct {
    ProposalID int64
  Voter      sdk.Address
  Option     Option
}

type MsgVoteWeighted struct {
    ProposalID int64
  Voter      sdk.Address
  Options    []WeightedVoteOption
}
MsgVoteWeighted 结构体的 ValidateBasic 需要满足:
  1. 所有权重之和等于 1.0
  2. 不允许重复出现同一个 Option
治理计票函数会遍历一次投票中的所有选项,并将“投票人的投票权乘以该选项权重”的结果累加到计票结果中。
tally() {
    results := map[types.VoteOption]sdk.Dec
    for _, vote := range votes {
    for i, weightedOption := range vote.Options {
    results[weightedOption.Option] += getVotingPower(vote.voter) * weightedOption.Weight
}
 
}
}
用于创建多选项投票的 CLI 命令如下:
simd tx gov vote 1 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05" --from mykey
如果要创建单选项投票,用户既可以执行
simd tx gov vote 1 "yes=1" --from mykey
也可以执行
simd tx gov vote 1 yes --from mykey
以保持向后兼容。

影响

向后兼容性

  • 现有的 VoteMsg 类型将保持不变,因此除非客户端希望支持 WeightedVoteMsg 功能,否则无需更新其处理流程。
  • 当从状态中查询 Vote 结构体时,其结构将发生变化,因此希望展示所有投票人及其各自投票情况的客户端需要处理这一新格式,以及单个投票人可能拆分投票的事实。
  • 查询计票函数结果时,对客户端暴露的 API 应保持不变。

正面影响

  • 对于代表多个利益相关方的地址(其中通常包含一些规模最大的地址),可以让投票过程更加准确。

负面影响

  • 相比简单投票,这种方式更复杂,因此可能更难向用户解释。不过,这一点在很大程度上可通过该功能为可选启用来缓解。

中性影响

  • 对治理计票函数而言,这是一项相对较小的改动。

Changelog

  • 2020/10/28: Intial draft

Status

Accepted

Abstract

This ADR defines a modification to the governance module that would allow a staker to split their votes into several voting options. For example, it could use 70% of its voting power to vote Yes and 30% of its voting power to vote No.

Context

Currently, an address can cast a vote with only one options (Yes/No/Abstain/NoWithVeto) and use their full voting power behind that choice. However, often times the entity owning that address might not be a single individual. For example, a company might have different stakeholders who want to vote differently, and so it makes sense to allow them to split their voting power. Another example use case is exchanges. Many centralized exchanges often stake a portion of their users’ tokens in their custody. Currently, it is not possible for them to do “passthrough voting” and giving their users voting rights over their tokens. However, with this system, exchanges can poll their users for voting preferences, and then vote on-chain proportionally to the results of the poll.

Decision

We modify the vote structs to be
type WeightedVoteOption struct {
    Option string
  Weight sdk.Dec
}

type Vote struct {
    ProposalID int64
  Voter      sdk.Address
  Options    []WeightedVoteOption
}
And for backwards compatibility, we introduce MsgVoteWeighted while keeping MsgVote.
type MsgVote struct {
    ProposalID int64
  Voter      sdk.Address
  Option     Option
}

type MsgVoteWeighted struct {
    ProposalID int64
  Voter      sdk.Address
  Options    []WeightedVoteOption
}
The ValidateBasic of a MsgVoteWeighted struct would require that
  1. The sum of all the Rates is equal to 1.0
  2. No Option is repeated
The governance tally function will iterate over all the options in a vote and add to the tally the result of the voter’s voting power * the rate for that option.
tally() {
    results := map[types.VoteOption]sdk.Dec
    for _, vote := range votes {
    for i, weightedOption := range vote.Options {
    results[weightedOption.Option] += getVotingPower(vote.voter) * weightedOption.Weight
}
 
}
}
The CLI command for creating a multi-option vote would be as such:
simd tx gov vote 1 "yes=0.6,no=0.3,abstain=0.05,no_with_veto=0.05" --from mykey
To create a single-option vote a user can do either
simd tx gov vote 1 "yes=1" --from mykey
or
simd tx gov vote 1 yes --from mykey
to maintain backwards compatibility.

Consequences

Backwards Compatibility

  • Previous VoteMsg types will remain the same and so clients will not have to update their procedure unless they want to support the WeightedVoteMsg feature.
  • When querying a Vote struct from state, its structure will be different, and so clients wanting to display all voters and their respective votes will have to handle the new format and the fact that a single voter can have split votes.
  • The result of querying the tally function should have the same API for clients.

Positive

  • Can make the voting process more accurate for addresses representing multiple stakeholders, often some of the largest addresses.

Negative

  • Is more complex than simple voting, and so may be harder to explain to users. However, this is mostly mitigated because the feature is opt-in.

Neutral

  • Relatively minor change to governance tally function.