Snapshot Analytics of recent proposal strategies

Snapshot allows projects to use voting systems (how votes are aggregated) and voting strategies (how much weight a user’s votes have) to govern their DAO. We made an analysis of the last 2.000 proposals on Snapshot (going back from 19. Sep. 2022) to see what was being used!

In those last 2,000 proposals the following voting systems have been used:

  • Single-choice: 1,596
  • Basic: 147
  • Weighted: 129
  • Quadratic: 79
  • Approval: 32
  • Ranked-choice: 17

For an explanation of how the different voting systems work, see: https://docs.snapshot.org/proposals/voting-types

Most of these voting systems are used in conjunction with a simply voting “strategy”: users can vote based on how many tokens they have. The most basic and widely used strategy to assess voting power is by counting the user’s ERC20 balance for a particular token.

However, token-based or coin-based governance has its downsides, as has been extensively discussed by i.a. Vitalik. We can easily see that ~80% of the projects prefer the “single choice” voting algorithm, often setting vote weight according to how many tokens someone has. In such a scenario, it is in theory possible that all the votes are happening by a small group of elites. In contrast, you might want to involve a larger crowd, but then you have to protect yourself against sybil attacks. For example, 79 recent proposals used Quadratic Voting, which explicitly needs some form of sybil resistance in order to implement it. If those projects still rely on how many tokens users have, the mechanism can easily be exploited by shifting those tokens across multiple accounts. To summarise, this will never give you fair and democratic voting, which is a problem because DAOs are not supposed to be like corporations but rather credibly neutral coordination mechanisms. Sometimes “1-person-1-vote” (1p1v) is the only way to go.

The problem however with this approach is that 1p1v until now relied on fraud-prone solutions to get to uniqueness or heavy, privacy invading KYC procedures. We at zkPortal have not only made a convenient way to get uniqueness of personhood for your voting needs, we also are providing it free of charge to the community!

There are projects which take an interesting approach to achieve some form of sybil resistance. For example, Hakka Finance has a proposal using Quadratic Voting, whereby if you stake for a longer period of time, your voting power increases, so any potential sybil attack effectively becomes more expensive. However, staking also increases the costs for honest users to participate in governance, so it is unclear what the net impact of staking will be on sybil-resistance.

Below we explain how we collected the above data. Go ahead and check it out! We are curious about your feedback, also regarding our analytics approach :)

Code Used

Snapshot has a wonderful API documentation here as well as the possibility to query their database (awesome snapshot, thank you for that).

The query we used was:

query {
  proposals (
    first: 2000,
    skip: 1000,
    where: {
      state: "closed"
    },
    orderBy: "created",
    orderDirection: desc
  ) {
    id
    space {
      id
    }
    title
    type
    created
  }
}

You can iterate through the proposals by setting skip and first to get more. We did not want to abuse the database but you probably can increase those limits but be nice ;)

All we did from there is to make some rudimentary analytics with some JavaScript magic and done.

Paste the above results into a variable:

const result = {
  "data": {
    "proposals": [
      {
        "id": "0x000000000000000000",
        "space": {
          "id": "xyz111.eth"
        },
        "title": "title",
        "type": "quadratic"
      },
      {
        "id": "0x000000000000000000",
        "space": {
          "id": "xyz111.eth"
        },
        "title": "titel test",
        "type": "single-choice"
      },
…..] 
  }
}

Make some vars to store the results:

let [approval, singlechoice, basic, weighted, quadratic, rankedchoice] = [0,0,0,0,0,0]

And get to the counting:

function count(item) {
 if (item["type"] == 'approval') {
     approval = approval + 1
 } else if (item["type"] == 'single-choice') {
     singlechoice = singlechoice + 1
 } else if (item["type"] == 'basic') {
     basic = basic + 1
 } else if (item["type"] == 'weighted') {
     weighted = weighted + 1
 } else if (item["type"] == 'quadratic') {
     quadratic = quadratic + 1
 } else if (item["type"] == 'ranked-choice') {
     rankedchoice = rankedchoice + 1
 } else {
     console.log("missing strategy")
 }
}

result["data"].proposals.forEach(count)

And finally:

console.log(approval, singlechoice, basic, weighted, quadratic, rankedchoice)

Will give you the results, happy analysing.