A Better Treasury System

Increasing OpenGov Participation

I wanted to add a few thoughts so far on ways to increase governance participation.

Ideally the target goal should be getting as much tokens as possible in the network participating in voting on referenda. This is either through directly voting (in some places in the codebase this is differentiated as ‘Casting’), or through delegating votes to someone else.

Right now there doesn’t exist a lot of products and uis specifically geared to things like delegating, besides this page from Math Crypto, far as I’m aware.

With any of this I think we need to take account for what the average user and token holder is like: they are not active, probably not up to speed with all the technical information of Polkadot (so that means they’re not intimately familiar with all the small details of open governance, the different tracks, thresholds, curves, etc), and want to do the least amount of effort and decision making possible.

In the future it hopefully gets better to where more unique token holders in the network are individually participating, but for now imo it makes the most sense to focus on trying to get people to delegate their votes so that their passiveness is at least not a detriment to the network.

Furthermore, imo, the goal in the very short term should be small, short, wins, where the least amount of time, energy, and effort involved for everyone is taken to account.

The following sheet is a rough snapshot of all the votes so far in the network, including if they’ve been delegated or casted.

Looking at this so far, I think it makes the most sense (in the short term, to focus on small, impactful wins) to focus on getting delegations to those that participate most in the network. The group of people I see as participating the most at the moment is validators, who regularly have 40+ votes each, and often don’t miss votes. Compare this group of people to say the fellowship currently, which accounts for 58 votes out of the current 20,038 listed there (so something like a 0.289% of total engagement).

As one of many efforts to get more people to participate in governance, I think one small short effort would be to have optional (but suggested) parts of nomination workflows to also include a checkbox that says “also delegate all or some particular governance tracks to this validator as well”.

If we take into account what the average user looks like, they don’t want to make additional choices, and from a product and ux perspective, this adds but 1 small point of friction for them to optionally select. This would be compared to having say an entire governance focused workflow for users, where the sole purpose would be them making more detailed choices in selecting which people to delegate governance votes to.

I think this sort of delegation to validators can be included in both the staking dashboard, as well as the workflow that many wallets like Talisman, Nova, Fearless, or others have in selecting validators to nominate.

This is a pretty small feature set that I think can have a lot of immediate impact in total tokens voting in the network.

In the mid to long term I would like to see more governance focused UI’s and products that allow people to make more granular and informed decision making about making delegations or voting themselves. But these will take a bit more time to make and mature.

I think in general wallets will probably be able to have the most impact in getting more governance participation, and having them include features like this will go a long way.

I would be curious to hear everyone else’s thoughts on ways they think we can include more participation and engagement in open governance participation, and what sort of products or features can help move things forward with this.

4 Likes

I’m all for more governance participation, I’m also for delegating to validators, but I would be curious why should Polkadot integrate an exclusive delegation mechanism solely for validators and exclude delegating to other parties like the fellowship.

I think this sort of delegation to validators can be included in both the staking dashboard, as well as the workflow that many wallets like Talisman, Nova, Fearless, or others have in selecting validators to nominate.

And then ask all ecosystem wallets to integrate it?

Instead of integrating something inclusive that would allow DOT holders to delegate to all. This can also be a fast “win”.

This also goes against the purpose of agile/multirole delegation, which OpenGov introduces.

From the blog:

" Not everyone has the time or inclination to make a well-researched vote on every matter… Gov2, however, improves on this with a rather special feature called Multirole Delegation. This allows you to specify a different delegate for every class of referendum in the system. If you do not want to delegate for a particular class of referendum then you can also retain direct control for that class.

This means you can delegate to one individual for any referenda on dealing out small tips to ecosystem contributors, another entity for referenda on more substantial treasury spending, another entity for purely technical network upgrades and parameterisations and finally retain direct control for any other decisions!"

But if the only individuals’ DOT holders can delegate to are validators, what’s the point of agile delegation?

1 Like

Those sorts of delegation to other groups of people besides validators would be included in the product / ux workflows of things that are specifically governance focused, not in the workflow for nominating validators.

The kind of thing your suggesting from a product perspective takes a lot longer to implement.

Adding in this sort of delegation checkbox to nomination workflows can be done within a ~week optimistically.

Ideally the sort of informed delegation decisions to people in the rest of the network might be good features for the kind of things @XyloDrone / Ordum want to make, or to subsquare/polkassembly, which can be done in parallel to features like nominator delegation, which would be much quicker to implement.


And IMO, just to highlight what I think both of these things look like from a product and integation standpoint:

Governance Products

For something like encourage delegation to anyone in the network, the userflow of this would first be:

1.) A user connects their wallet with the application via something like Polkadot js extension, talisman, subwallet, etc.

2.) Upon the user connecting a wallet, there would be a handful of queries does in the backround. We would first want to determine if the user has participated in governance, or if they’re are currently delegating to anyone for all or particular governance tracks.

a.) Right now the dev ex of some of these things is not ideal, as the storage items for this exist in convictionVoting.votingFor.

You would query for all tracks via something like:

const addressVoting = await api.query.convictionVoting.entries(ADDRESS)

This will return all the tracks someone is either delegating for, or if they have voted.

Based on what this returns, if the amount of tracks, or amount of things they have voted for is not above some threshold, the app would display some kind of banner or toast saying something like: “You are not currently participating in governance as much, consider delegating by clicking here”.

b.) By clicking that they are brought to a new modal or page that starts the governance workflow

3.) The governance workflow begins with a list of say something like ~50 accounts that are reccomendations of whom they should delegate to.

This list of accounts is something that aligns with the goals of getting the most amount of tokens in the network participating in governance. That is, the suggested accounts are those that 1.) consistently vote on things in the network, and also possibley 2.) also have a lot of other delegations.

Aggregating this sort of information can be a bit tricky because you need to have a list of all accounts in the network that participate in things historically, of which querying this from the chain itself often leads to bad ux for the user as it can take a bit of time to actually put this together. So something like subsquid, subquery, or some type of indexer storing this in a relational db and served via an api might be needed.

I’ve written a query of this for the chain itself here in the process of indexing it:

  // OpenGov Conviction Voting
  getConvictionVoting = async () => {
    const allVotes: ConvictionVote[] = [];
    const allDelegations: ConvictionDelegation[] = [];

    // Create a map to more easily check the status of a referenda, is it ongoing or finished
    const referendaMap = new Map();
    const { ongoingReferenda, finishedReferenda } =
      await this.getOpenGovReferenda();
    for (const ref of ongoingReferenda) {
      referendaMap.set(ref.index, ref);
    }
    for (const ref of finishedReferenda) {
      referendaMap.set(ref.index, ref);
    }

    const tracks = this.api.consts.referenda.tracks;

    // Query the keys and storage of all the entries of `votingFor`
    // These are all the accounts voting, for which tracks, for which referenda
    // And whether they are delegating or not.
    const votingFor = await this.api.query.convictionVoting.votingFor.entries();
    for (const [key, entry] of votingFor) {
      // Each of these is the votingFor for an account for a given governance track
      // @ts-ignore
      const [address, track] = key.toHuman();

      // For each track, an account is either voting themselves, or delegating to another account

      // The account is voting themselves
      // @ts-ignore
      if (entry.isCasting) {
        // For each given track, these are the invididual votes for that track,
        //     as well as the total delegation amounts for that particular track
        // @ts-ignore
        const { votes, delegations } = entry.asCasting;

        // The total delegation amounts.
        //     delegationVotes - the _total_ amount of tokens applied in voting. This takes the conviction into account
        //     delegationCapital - the base level of tokens delegated to this address
        const { votes: delegationVotes, capital: delegationCapital } =
          delegations;

        // The list of votes for that track
        for (const referendumVote of votes) {
          // The vote for each referendum - this is the referendum index,the conviction, the vote type (aye,nay), and the balance
          const [referendumIndex, voteType] = referendumVote;
          if (voteType.isStandard) {
            const { vote: refVote, balance } = voteType.asStandard;
            const { conviction, vote: voteDirection } = refVote.toHuman();

            // The formatted vote
            const v: ConvictionVote = {
              // The particular governance track
              track: Number(track.toString()),
              // The account that is voting
              address: address.toString(),
              // The index of the referendum
              referendumIndex: Number(referendumIndex.toString()),
              // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
              conviction: conviction.toString(),
              // The balance they are voting with themselves, sans delegated balance
              balance: {
                aye:
                  voteDirection.toString() == "Aye"
                    ? Number(balance.toJSON())
                    : 0,
                nay:
                  voteDirection.toString() == "Nay"
                    ? Number(balance.toJSON())
                    : 0,
                abstain: 0,
              },
              // The total amount of tokens that were delegated to them (including conviction)
              delegatedConvictionBalance: Number(delegationVotes.toString()),
              // the total amount of tokens that were delegated to them (without conviction)
              delegatedBalance: Number(delegationCapital.toString()),
              // The vote type, either 'aye', or 'nay'
              voteDirection: voteDirection.toString(),
              // The vote direction type, either "Standard", "Split", or "SplitAbstain"
              voteDirectionType: "Standard",
              // Whether the person is voting themselves or delegating
              voteType: "Casting",
              // Who the person is delegating to
              delegatedTo: null,
            };
            allVotes.push(v);
          } else if (voteType.isSplit) {
            const { aye, nay } = voteType.asSplit;

            // The formatted vote
            const v: ConvictionVote = {
              // The particular governance track
              track: Number(track.toString()),
              // The account that is voting
              address: address.toString(),
              // The index of the referendum
              referendumIndex: Number(referendumIndex.toString()),
              // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
              conviction: "Locked1x",
              // The balance they are voting with themselves, sans delegated balance
              balance: {
                aye: Number(aye),
                nay: Number(nay),
                abstain: 0,
              },
              // The total amount of tokens that were delegated to them (including conviction)
              delegatedConvictionBalance: Number(delegationVotes.toString()),
              // the total amount of tokens that were delegated to them (without conviction)
              delegatedBalance: Number(delegationCapital.toString()),
              // The vote type, either 'aye', or 'nay'
              voteDirection: aye >= nay ? "Aye" : "Nay",
              // The vote direction type, either "Standard", "Split", or "SplitAbstain"
              voteDirectionType: "Split",
              // Whether the person is voting themselves or delegating
              voteType: "Casting",
              // Who the person is delegating to
              delegatedTo: null,
            };
            allVotes.push(v);
          } else {
            const voteJSON = voteType.toJSON();

            if (voteJSON["splitAbstain"]) {
              const { aye, nay, abstain } = voteJSON["splitAbstain"];
              // The formatted vote
              const v: ConvictionVote = {
                // The particular governance track
                track: Number(track.toString()),
                // The account that is voting
                address: address.toString(),
                // The index of the referendum
                referendumIndex: Number(referendumIndex.toString()),
                // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
                conviction: "Locked1x",
                // The balance they are voting with themselves, sans delegated balance
                balance: {
                  aye: Number(aye),
                  nay: Number(nay),
                  abstain: Number(abstain),
                },
                // The total amount of tokens that were delegated to them (including conviction)
                delegatedConvictionBalance: Number(delegationVotes.toString()),
                // the total amount of tokens that were delegated to them (without conviction)
                delegatedBalance: Number(delegationCapital.toString()),
                // The vote type, either 'aye', or 'nay'
                voteDirection:
                  abstain >= aye && abstain >= nay
                    ? "Abstain"
                    : aye > +nay
                    ? "Aye"
                    : "Nay",
                // The vote direction type, either "Standard", "Split", or "SplitAbstain"
                voteDirectionType: "SplitAbstain",
                // Whether the person is voting themselves or delegating
                voteType: "Casting",
                // Who the person is delegating to
                delegatedTo: null,
              };
              allVotes.push(v);
            }
          }
        }
        // @ts-ignore
      } else if (entry.isDelegating) {
        // The address is delegating to another address for this particular track

        const {
          balance,
          target,
          conviction,
          delegations: { votes: delegationVotes, capital: delegationCapital },
          prior,
          // @ts-ignore
        } = entry.asDelegating;
        const delegation: ConvictionDelegation = {
          track: track,
          address: address.toString(),
          target: target.toString(),
          balance: balance.toString(),
          conviction: conviction.toString(),
          // The total amount of tokens that were delegated to them (including conviction)
          delegatedConvictionBalance: delegationVotes.toString(),
          // the total amount of tokens that were delegated to them (without conviction)
          delegatedBalance: delegationCapital.toString(),
          prior: prior,
        };
        allDelegations.push(delegation);
      }
    }

    // Create a vote entry for everyone that is delegating for current ongoing referenda
    for (const delegation of allDelegations) {
      // Find the vote of the person they are delegating to
      const v = allVotes.filter((vote) => {
        const isReferendumOngoing =
          referendaMap.get(vote.referendumIndex)?.currentStatus == "Ongoing";
        return (
          isReferendumOngoing &&
          vote.address == delegation.target &&
          vote.track == delegation.track
        );
      });
      if (v.length > 0) {
        // There are votes for a given track that a person delegating will have votes for.
        for (const vote of v) {
          const voteDirectionType = vote.voteDirectionType;
          let balance;
          if (voteDirectionType == "Aye") {
            balance = {
              aye: Number(delegation.balance),
              nay: Number(0),
              abstain: Number(0),
            };
          } else if (voteDirectionType == "Nay") {
            balance = {
              aye: Number(0),
              nay: Number(delegation.balance),
              abstain: Number(0),
            };
          } else if (
            voteDirectionType == "Split" ||
            voteDirectionType == "SplitAbstain"
          ) {
            balance = {
              aye: Number(0),
              nay: Number(0),
              abstain: Number(0),
            };
          }

          const delegatedVote: ConvictionVote = {
            // The particular governance track
            track: vote.track,
            // The account that is voting
            address: delegation.address,
            // The index of the referendum
            referendumIndex: vote.referendumIndex,
            // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
            conviction: delegation.conviction,
            // The balance they are voting with themselves, sans delegated balance
            balance: balance,
            // The total amount of tokens that were delegated to them (including conviction)
            delegatedConvictionBalance: delegation.delegatedConvictionBalance,
            // the total amount of tokens that were delegated to them (without conviction)
            delegatedBalance: delegation.delegatedBalance,
            // The vote type, either 'aye', or 'nay'
            voteDirection: vote.voteDirection,
            // Whether the person is voting themselves or delegating
            voteType: "Delegating",
            voteDirectionType: voteDirectionType,
            // Who the person is delegating to
            delegatedTo: vote.address,
          };
          allVotes.push(delegatedVote);
        }
      } else if (v.length == 0) {
        // There are no direct votes from the person the delegator is delegating to,
        // but that person may also be delegating, so search for nested delegations

        let found = false;
        // The end vote of the chain of delegations
        let delegatedVote;

        delegatedVote = delegation;
        while (!found) {
          // Find the delegation of the person who is delegating to
          const d = allDelegations.filter((del) => {
            return (
              del.address == delegatedVote.target &&
              del.track == delegatedVote.track
            );
          });

          if (d.length == 0) {
            // There are no additional delegations, try to find if there are any votes

            found = true;
            const v = allVotes.filter((vote) => {
              return (
                vote.address == delegatedVote.target &&
                vote.track == delegatedVote.track
              );
            });
            if (v.length > 0) {
              // There are votes, ascribe them to the delegator
              for (const vote of v) {
                const voteDirectionType = vote.voteDirectionType;
                let balance;
                if (voteDirectionType == "Aye") {
                  balance = {
                    aye: Number(delegation.balance),
                    nay: Number(0),
                    abstain: Number(0),
                  };
                } else if (voteDirectionType == "Nay") {
                  balance = {
                    aye: Number(0),
                    nay: Number(delegation.balance),
                    abstain: Number(0),
                  };
                } else if (voteDirectionType == "Split") {
                  const ayePercentage =
                    vote.balance.aye / (vote.balance.aye + vote.balance.nay);
                  const nayPercentage =
                    vote.balance.nay / (vote.balance.aye + vote.balance.nay);
                  balance = {
                    aye: Number(delegation.balance) * ayePercentage,
                    nay: Number(delegation.balance) * nayPercentage,
                    abstain: Number(0),
                  };
                } else if (voteDirectionType == "SplitAbstain") {
                  const ayePercentage =
                    vote.balance.aye /
                    (vote.balance.aye +
                      vote.balance.nay +
                      vote.balance.abstain);
                  const nayPercentage =
                    vote.balance.nay /
                    (vote.balance.aye +
                      vote.balance.nay +
                      vote.balance.abstain);
                  const abstainPercentage =
                    vote.balance.nay /
                    (vote.balance.aye +
                      vote.balance.nay +
                      vote.balance.abstain);
                  balance = {
                    aye: Number(delegation.balance) * ayePercentage,
                    nay: Number(delegation.balance) * nayPercentage,
                    abstain: Number(delegation.balance) * abstainPercentage,
                  };
                }

                const delegatedVote: ConvictionVote = {
                  // The particular governance track
                  track: vote.track,
                  // The account that is voting
                  address: delegation.address,
                  // The index of the referendum
                  referendumIndex: vote.referendumIndex,
                  // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
                  conviction: delegation.conviction,
                  // The balance they are voting with themselves, sans delegated balance
                  balance: balance,
                  // The total amount of tokens that were delegated to them (including conviction)
                  delegatedConvictionBalance:
                    delegation.delegatedConvictionBalance,
                  // the total amount of tokens that were delegated to them (without conviction)
                  delegatedBalance: delegation.delegatedBalance,
                  // The vote type, either 'aye', or 'nay'
                  voteDirection: vote.voteDirection,
                  // Whether the person is voting themselves or delegating
                  voteType: "Delegating",
                  voteDirectionType: voteDirectionType,
                  // Who the person is delegating to
                  delegatedTo: vote.address,
                };
                allVotes.push(delegatedVote);
              }
            } else {
              // The person they are delegating to does not have any votes.
            }
          } else if (d.length == 1) {
            // There is a delegated delegation
            delegatedVote = d[0];
          }
        }
      }
    }

    // Query the delegations for finished referenda at previous block heights
    for (const referendum of finishedReferenda) {
      const apiAt = await this.getApiAt(referendum.confirmationBlockNumber - 1);

      const votingFor = await apiAt.query.convictionVoting.votingFor.entries();
      for (const [key, entry] of votingFor) {
        // Each of these is the votingFor for an account for a given governance track
        // @ts-ignore
        const [address, track] = key.toHuman();
        // @ts-ignore
        if (entry.isDelegating) {
          // The address is delegating to another address for this particular track

          const {
            balance,
            target,
            conviction,
            delegations: { votes: delegationVotes, capital: delegationCapital },
            prior,
            // @ts-ignore
          } = entry.asDelegating;
          const delegation: ConvictionDelegation = {
            track: track,
            address: address.toString(),
            target: target.toString(),
            balance: balance.toString(),
            conviction: conviction.toString(),
            // The total amount of tokens that were delegated to them (including conviction)
            delegatedConvictionBalance: delegationVotes.toString(),
            // the total amount of tokens that were delegated to them (without conviction)
            delegatedBalance: delegationCapital.toString(),
            prior: prior,
          };
          // Try and find the delegated vote from the existing votes
          const v = allVotes.filter((vote) => {
            return (
              vote.address == delegation.target &&
              vote.track == delegation.track
            );
          });
          if (v.length > 0) {
            // There are votes for a given track that a person delegating will have votes for.
            for (const vote of v) {
              const voteDirectionType = vote.voteDirectionType;
              let balance;
              if (voteDirectionType == "Aye") {
                balance = {
                  aye: Number(delegation.balance),
                  nay: Number(0),
                  abstain: Number(0),
                };
              } else if (voteDirectionType == "Nay") {
                balance = {
                  aye: Number(0),
                  nay: Number(delegation.balance),
                  abstain: Number(0),
                };
              } else if (
                voteDirectionType == "Split" ||
                voteDirectionType == "SplitAbstain"
              ) {
                balance = {
                  aye: Number(0),
                  nay: Number(0),
                  abstain: Number(0),
                };
              }

              const delegatedVote: ConvictionVote = {
                // The particular governance track
                track: vote.track,
                // The account that is voting
                address: delegation.address,
                // The index of the referendum
                referendumIndex: vote.referendumIndex,
                // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
                conviction: delegation.conviction,
                // The balance they are voting with themselves, sans delegated balance
                balance: balance,
                // The total amount of tokens that were delegated to them (including conviction)
                delegatedConvictionBalance:
                  delegation.delegatedConvictionBalance,
                // the total amount of tokens that were delegated to them (without conviction)
                delegatedBalance: delegation.delegatedBalance,
                // The vote type, either 'aye', or 'nay'
                voteDirection: vote.voteDirection,
                // Whether the person is voting themselves or delegating
                voteType: "Delegating",
                voteDirectionType: voteDirectionType,
                // Who the person is delegating to
                delegatedTo: vote.address,
              };
              allVotes.push(delegatedVote);
            }
          } else if (v.length == 0) {
            // There are no direct votes from the person the delegator is delegating to,
            // but that person may also be delegating, so search for nested delegations

            let found = false;
            // The end vote of the chain of delegations
            let delegatedVote;

            delegatedVote = delegation;
            while (!found) {
              // Find the delegation of the person who is delegating to
              const d = allDelegations.filter((del) => {
                return (
                  del.address == delegatedVote.target &&
                  del.track == delegatedVote.track
                );
              });

              if (d.length == 0) {
                // There are no additional delegations, try to find if there are any votes

                found = true;
                const v = allVotes.filter((vote) => {
                  return (
                    vote.address == delegatedVote.target &&
                    vote.track == delegatedVote.track
                  );
                });
                if (v.length > 0) {
                  // There are votes, ascribe them to the delegator
                  for (const vote of v) {
                    const voteDirectionType = vote.voteDirectionType;
                    let balance;
                    if (voteDirectionType == "Aye") {
                      balance = {
                        aye: Number(delegation.balance),
                        nay: Number(0),
                        abstain: Number(0),
                      };
                    } else if (voteDirectionType == "Nay") {
                      balance = {
                        aye: Number(0),
                        nay: Number(delegation.balance),
                        abstain: Number(0),
                      };
                    } else if (voteDirectionType == "Split") {
                      const ayePercentage =
                        vote.balance.aye /
                        (vote.balance.aye + vote.balance.nay);
                      const nayPercentage =
                        vote.balance.nay /
                        (vote.balance.aye + vote.balance.nay);
                      balance = {
                        aye: Number(delegation.balance) * ayePercentage,
                        nay: Number(delegation.balance) * nayPercentage,
                        abstain: Number(0),
                      };
                    } else if (voteDirectionType == "SplitAbstain") {
                      const ayePercentage =
                        vote.balance.aye /
                        (vote.balance.aye +
                          vote.balance.nay +
                          vote.balance.abstain);
                      const nayPercentage =
                        vote.balance.nay /
                        (vote.balance.aye +
                          vote.balance.nay +
                          vote.balance.abstain);
                      const abstainPercentage =
                        vote.balance.nay /
                        (vote.balance.aye +
                          vote.balance.nay +
                          vote.balance.abstain);
                      balance = {
                        aye: Number(delegation.balance) * ayePercentage,
                        nay: Number(delegation.balance) * nayPercentage,
                        abstain: Number(delegation.balance) * abstainPercentage,
                      };
                    }

                    const delegatedVote: ConvictionVote = {
                      // The particular governance track
                      track: vote.track,
                      // The account that is voting
                      address: delegation.address,
                      // The index of the referendum
                      referendumIndex: vote.referendumIndex,
                      // The conviction being voted with, ie `None`, `Locked1x`, `Locked5x`, etc
                      conviction: delegation.conviction,
                      // The balance they are voting with themselves, sans delegated balance
                      balance: balance,
                      // The total amount of tokens that were delegated to them (including conviction)
                      delegatedConvictionBalance:
                        delegation.delegatedConvictionBalance,
                      // the total amount of tokens that were delegated to them (without conviction)
                      delegatedBalance: delegation.delegatedBalance,
                      // The vote type, either 'aye', or 'nay'
                      voteDirection: vote.voteDirection,
                      // Whether the person is voting themselves or delegating
                      voteType: "Delegating",
                      voteDirectionType: voteDirectionType,
                      // Who the person is delegating to
                      delegatedTo: vote.address,
                    };
                    allVotes.push(delegatedVote);
                  }
                } else {
                  // The person they are delegating to does not have any votes.
                }
              } else if (d.length == 1) {
                // There is a delegated delegation
                delegatedVote = d[0];
              }
            }
          }
        }
      }
    }

    const convictionVoting = {
      votes: allVotes,
      delegations: allDelegations,
    };
    return convictionVoting;
  };

This is a bit verbose and can probably be slimmed down and improved, but it addresses some trickyness of trying to find out how to put together delegations at historical block numbers. Some of the full queries (and some more useful ones) can be found here.

After aggregating the historical votes, one thing that might be useful is to quantify the consistency of voting, which can be sort of thought of how many sub-arrays of votes of non-linearly increasing order there are:

// Returns an array of sub-arrays of consecutive numbers.
// ie [1, 2, 3, 5, 6, 8, 9]
// return [[1, 2, 3], [5, 6], [8, 9]]
export const consistency = (array) => {
  const sorted = asc(array);
  return sorted.reduce((r, n) => {
    const lastSubArray = r[r.length - 1];

    if (!lastSubArray || lastSubArray[lastSubArray.length - 1] !== n - 1) {
      r.push([]);
    }

    r[r.length - 1].push(n);

    return r;
  }, []);
};

// Given an array, return a new array with the last _threshold_ amount of items from a lastValue
//     For example given:
//         - the array [1, 3, 4, 6, 7, 8, 9, 11, 12],
//         - a last value of 15
//         - a threshold of 5
//     This will return a new array [11, 12]
export const lastValues = (array, lastValue, threshold) => {
  const sorted = asc(array);
  return sorted.filter((x) => {
    return lastValue - threshold < x;
  });
};

// The window of the last votes we want to check consistency for.
//      For example, if the last referendum was 143 and there's a window of 5,
//      we want to check votes [139, 140, 141, 142, 143]
const RECENT_WINDOW = 5;

const LAST_REFERENDUM_WEIGHT = 15;
const RECENT_REFERENDUM_WEIGHT = 5;
const BASE_REFERENDUM_WEIGHT = 2;

// Inputs:
//    votes: an array of all the votes of a validator
//    lastReferendum: the last (or current) on chain referendum
// Returns:
//    baseDemocracyScore: the base score, sans multipliers
//    totalConsistencyMultiplier: the multiplier for the consistency of all votes
//    lastConsistencyMultiplier: the mulitiplier for the consistency of the recent window (ie the last 5 votes)
//    totalDemocracyScore: the base score * multipliers
// Scoring:
//     consistency is quantified as the batches of consecutive votes.
//
//     if someone has the votes [0, 1, 2, 4, 5, 6, 8, 10, 13],
//         there are 5 separate streaks of consistency: [0, 1, 2], [4, 5, 6], [8], [10], [13]
//
//     ideally we want to reward people that have the fewest separate streaks of consistency
//       (ie we want them to have fewer, longer consecutive streams of votes)
//
//     these consistency streaks are used to determine two different multipliers:
//        total consistency: how consistent are they with all votes
//        last consistency: how consistent are they within the recent window of votes (ie the last 5)
//
//     the multiplier is calculated as: 1 + 1 / consistency_streaks
//          resulting the more separate steams of consistency, the lower the multiplier
//
//      the total score is calculated as (base_score * lastMultiplier * totalMultipler)
//
//      The total score is capped at 250, the last consistency multiplier at 2x and the total consistency multiplier at 1.5x
//
//  Desired Outcome:
//     We want to balance the fact that new people may not have the number of votes as people that have
//       been voting for a while, so ideally if people start voting on the recent referenda (and are consistent)
//       they can achieve a good score from the multipliers.
//     We want to still benefit people that have been voting on lots of things though, so the points
//       they get from those gradually decrease over time (but still add up)
export const scoreDemocracyVotes = (
  votes: number[],
  lastReferendum: number
) => {
  if ((votes && votes?.length == 0) || !lastReferendum) {
    return {
      baseDemocracyScore: 0,
      totalConsistencyMultiplier: 0,
      lastConsistencyMultiplier: 0,
      totalDemocracyScore: 0,
    };
  }
  // Make sure votes are in ascending order
  const sorted = asc(votes);

  // Calculate the base democracy score:
  //     - if the referendum is the last/current, add 15 points
  //     - if the referendum is one of the last 3 most recent referenda, add 5 points per vote
  //     - everything else add 2 points per vote
  let demScore = 0;
  for (const referendum of votes) {
    if (referendum == lastReferendum) {
      demScore += LAST_REFERENDUM_WEIGHT;
    } else if (lastReferendum - referendum <= 3) {
      demScore += RECENT_REFERENDUM_WEIGHT;
    } else {
      demScore += BASE_REFERENDUM_WEIGHT;
    }
  }

  // Get the consistency sub-arrays for all votes
  const totalConsistency = consistency(sorted);

  //
  const lastConsistency = consistency(
    lastValues(sorted, lastReferendum, RECENT_WINDOW)
  );

  // The consistency of all historical votes, capped at 1.5x
  const totalConsistencyMultiplier =
    totalConsistency?.length > 0
      ? Math.min(1 + 1 / totalConsistency.length, 1.5)
      : 1;

  // The consistency of only the last _threshold_ votes
  const lastConsistencyMultiplier =
    lastConsistency?.length > 0 ? 1 + (1 / lastConsistency.length) * 1.5 : 1;

  // Calculate the total score, capping it at 400 points
  const totalDemScore = Math.min(
    demScore * totalConsistencyMultiplier * lastConsistencyMultiplier,
    400
  );

  return {
    baseDemocracyScore: demScore || 0,
    totalConsistencyMultiplier: totalConsistencyMultiplier || 0,
    lastConsistencyMultiplier: lastConsistencyMultiplier || 0,
    totalDemocracyScore: totalDemScore || 0,
  };
};

Of which some of the functions can be found here.

Using both the # of histical votes + the consistency you would randomize the top say ~50 accounts and present them to the user in the modal to select which ones they might want to delegate to.

The next step of this would be selecting which tracks they would want to delegate to (all of paritcular ones), of which regardless you will need to do a big batch tx of the following:

conts tx = api.tx.convictionVoting.delegate(class, delegationAddress, conviction, balance)

So if they want to delegate to all ~15 tracks it would be a batch tx of 15 of those txs. You would then prompt the user to sign and submit the tx.

The banner / toast might also want to detect something like how long ago they may have delegated to someone, and if that is too old, it would suggest revising their delegation since it was done a while ago.

The biggest challenges here is in querying, storing, aggregating that information, and being able to serve it to the user in a way that coforms to a good product ux experience, which would likely involve making a dedicated backend, indexer, db, and serving that via apis.

Staking Workflow

Compared to the above workflow, integrating this into staking products is much simpler.

In the modal where they select the amount of validators, there would be an optional checkbox for “also delegate governance votes to this validator”. This should be unchecked by default, but with a tag next it it saying it’s highly recommended that they so do.

If they check it it would bring a next modal where they would select which governance tracks they want to delegate for (either all or particular ones).

Selecting this would include these txs:

conts tx = api.tx.convictionVoting.delegate(class, delegationAddress, conviction, balance)

Since selecting validators already has a few txs in it that are already a batch tx, this would just include these additional ones in there as well.

Integrating this into staking would require zero additional querying, indexing, or aggregating, making it both from an implemenation standpoint and product / ux standpoint much easier and quicker to implement.

Challenges and Considerations

Overall I think the hardest part of making any products in the substrate space is giving the right and appropriate kind of data to users in a simplified and aggregated fashion, and doing so in a way that is performant and snappy. The reason most people don’t like Polkadot.js is because of all the data that is presented to people, and the time it takes to query the chain. This can be solved by having good backends that index and aggregate it, and only serve the minimal viable information to people via apis.

If we want to encourage people to delegate to people in the network, we want to make sure that this aligns with goals of the network, which means there is some informed decision making, but the user is not bombarded with a bunch of additional information that they may not need to know.

Additionally, from a product perspective, we want users to be able to succeed in these tasks with the smallest amount of friction possible, meaning they don’t go through a long list of modals or tasks to do, or have to make a million specific choices.

It’s within our best interest to combine these decisions where possible (ie the staking workflow have a simple checkbox that also includes delegation), or to aggregate the governance info where possible in a governace app so the user doesn’t need to click through a million things or scroll through a million things to try to determine something.

Making good products that actually have an impact of getting engagement is really hard, but some of the above is some factors that I consider help towards this goal. I would be curious to hear others thoughts on what they think of this, and what other factors might be good to include.

That all sounds reasonable:

Having a (simple) governance UI for governance-related information and choices (like the staking dashboard for everything staking-related) and enabling participation in other areas wherever possible.

Now for the staking dashboard, the question is who you can delegate your votes to. When the user already makes choices it would make sense to include a simple checkbox in the user flow too, potentially linking to the/a governance UI. That means if the user evaluates and selects pools, delegation to pool operators would make sense, if the user nominates directly they could delegate to validators.

From the discussion in the KSM Direction channel I hear that delegation for pool joiners would be valuable, which also makes sense because they currently can’t use their staked funds to participate in governance.

That would be the 2 delegation options I would see.

You’re also saying that making the choices should be easy and I’d agree. With the staking dashboard and with pools we are targeting newcomers or users who aren’t keen to dive in fully (yet), so for the full list of governance options they could go to more advanced solutions.

Same for the engineering teams btw - if we have high requirements for governance participation in the staking UI, it will probably not be feasible, whereas including a checkbox would already be great.

Likewise, we should ensure though that a governance UI exists, at the latest when OpenGov launches on Polkadot, otherwise validators / pool operators will get all the delegations and the Fellowship none.

Why are we interested in short term wins? Having short term impactful changes could be problematic in the future if people may not change their delegations.

I don’t want to defend the fellowship or anyone else that is currently not doing a great job in voting. However, the comparison is very unfair you are doing here. How many of these validators are in the 1000th validator program? How was their voting behavior before they got incentivized to vote? I mean they are getting points in the validator program to increase the likelihood of getting nominated, so they are directly incentivized to vote. I think when this is showing one thing, that incentivizing votes is working. The question would be on the general “quality” of these votes. As this is easily gameable by either voting randomly or always voting to abstain. Nevertheless, it could be used to pull in more people. We also already have talked about this and there is the lottery pallet by @shawntabrizi. Adding this to Kusama should be fairly easy (much easier than any other change proposed here) and Kusama is known for chaos any way.

I’m not convinced that this is a good idea. Yes, we should use all the different UIs and ways people are currently interacting with Polkadot/Kusama to highlight the voting delegations etc. However, we should not propose to delegate your voting to a validator. Validator operators are interested in the network for sure, but they are mainly interested in getting the biggest award (maybe not all, but this is the game they are playing). Pushing people to delegate their votes to validators is like we are developing us back to Bitcoin, because Bitcoin is essentially the same. Validator operators deciding on which update they like and which not. The number of validator operators is bigger than the number of Gov1 council members, but it is still a rather small group that could also easily work together to push through certain changes.

Every possible UI should instead link out to a proper page that is explaining the voting system etc. What you can do etc. Then we need some proper Gov2 UI that supports delegations etc. People should be able to delegate to any kind of person. Maybe have some kind of “tinder like” experience to select someone to delegate for based on their previous voting behavior. Maybe we should then also reward people for changing their delegations regularly or something like that, by putting them as well in the potential lottery winner pool. There are thousands of things we can do/we should try.

How is that improving the network besides pumping up the number of tokens in voting? Which may makes it look better to the outside, but doesn’t really change anything fundamentally?

There is a huge difference in trusting someone to run a validator/be a good pool operator to voting on certain things on your behalf. It is not in their interest in voting for nominators to get more rewards, but more in their interest in increasing the minimum validator commission for example. For the network it is probably also the best that people with the most delegations are different to the ones running a validator or being part of the fellowship. In the long run for very good informed decision about certain proposals you need to take part in discussion, read all the proposals etc. In the end we are all humans and can not do all the things at once. (sadly)

4 Likes

Id say we’re interested in short, medium, and long term wins. What I’m suggesting is one of many things that can be done in the short term to try to increase engagement until some of the longer term things come to fruition. Why wait around doing nothing until we have better and more usable products?

If Kusama is known for Chaos, why not add this sort of feature to nomination workflows and see how the experiment turns out? :stuck_out_tongue_winking_eye:

I’d be curious to hear your thoughts on what “quality” means. But one way I see it is (from the perspective of the chain), “quality” means voting with a lot of tokens, as this enables referenda to pass or fail more quickly. The chain itself does not necessarily care how “thoughtful” a vote is. Of course it would be nice to try to cultivate thoughtful discussion and feedback on proposals. But the chain itself does not care about this. It only cares about the vote direction (yes/no/abstain), and amounts.

If you’re curious what this looks like in more active governance ecosystems, in Cosmos validators are very active in governace (and many Cosmos validators are also Polkadot/Kusama validators). And people that back them actually do care about these decisions and their outcomes. See this reddit thread on the massive amount of backlash stakefish got for their last minute vote on prop 89, in which case many people undelegated and unstaked with them.

What I’m suggesting with that is a voluntary decision by a user - it would be unchecked by default and only exist there as a very streamlined way to also delegate if they want. Users currently have no incentive to delegate governance votes to the validators their backing, as it gives them no additional rewards. There’s also no good UI at the moment for someone to even delegate if they want to, so this would be an option for them to be able to also delegate in a streamlined manner until those ui’s exist. When they do it would be better to include links to those in the nomination workflow instead. And per what I mentioned above, also have banners / toasts/ reminders for people if they delegated a while ago to revisit their delegations (so if they would delegate to a validator now, they’re encouraged to change it in the future since it might be old).

This is being worked on in a few ways, but will probably take a while until it actually launches.

More tokens voting will improve the network by allowing things to be able to reach quorum to fail/pass more quickly.

If you look at discussions on Polkassembly / Subsquare, some of the most active contributors are Validators. Just because someone participates in the network as a Validator doesn’t mean they are only out to earn rewards and not meaningfully contribute.


Just to put some data to this, I’m aggregating a bunch of metrics for participating over time. I’m looking to segment the activity by various groups of people (if anyone has suggestions on ways to classify more groups of people, lmk). This also is being indexed over time, so we will be able to see how activity changes. As I mentioned above, at the moment one of the things that matters most is the amount of tokens voting, which is why I try to delineate that as well.

(all of these categories only count for votes themselves, and not the amounts that are delegated to them)

All Votes

Total Votes: 25285
Casting Votes: 18138 (71.73%)
Delegated Votes: 7147 (28.26%)

All Extremely Low Balance Votes (<0.5 KSM):  11574 (45.77%)
All Very Low Balance Votes (<1.5 KSM): 13485 (53.33%)
All Low Balance Votes (<5 KSM): 15742 (62.25%)
All High Balance Votes (>5 KSM): 9318 (37.75%)

As we can see from this, the vast majority of current votes are less than 0.5 ksm, which effectively don’t do anything. This is likely from the Proof of Chaos nfts, or trying to inflate the nubmer of yes / no votes, as we can see that accounts like this automate sending a ton of amounts of 0.015 ksm to accounts to then vote.

Validator Votes

Validator Votes: 14617 (57.80%)
Extremely Low Validator Votes (< 0.5 KSM): 6258 (42.81%)
Very Low Validator Votes (< 1.5 KSM): 6743 (46.13%)
Low Validator Votes (< 5 KSM): 6925 (47.37%)
High Validator Votes (> 5 KSM): 7569 (51.78%)

Validator accounts include any accounts that are also apart of the identity of validators. Per this we can see that validators accounts for more than half of all votes currently (at 57.80% of all votes). The percentages next to the amount segregations (for this and all the following) are out of just that group (instead of the total). The majority of them look to vote with above 5 ksm.

Nominators

Nominator Votes: 4668 (18.46%)
Extremely Low Nominator Votes (< 0.5 KSM): 1287 (27.57%)
Very Low Nominator Votes (< 1.5 KSM): 2070 (44.34%)
Low Nominator Votes (< 5 KSM): 3518 (75.36%)
High Nominator Votes (> 5 KSM): 1108 (23.73%)

Nominators are classified as acconts that are currently nominating (and not also a validator). Most of them look to be voting with between 1.5 ksm and 5 ksm.

Non-Stakers

Non-Staker Votes: 6000 (23.72%)
Extremely Low Non-Staker Votes (< 0.5 KSM): 4029 (67.15%)
Very Low Non-Staker Votes (< 1.5 KSM): 4672 (77.86%)
Low Non-Staker Votes (< 5 KSM): 5299 (88.31%)
High Non-Staker Votes (> 5 KSM): 641 (10.68%)

The majority of them vote with < 0.5 ksm, and are probably bots as I pointed out above.

Identity

Identity Votes: 16202 (64.07%)
Extremely Low Identity Votes (< 0.5 KSM): 6467 (39.91%)
Very Low Identity Votes (< 1.5 KSM): 7122 (43.95%)
Low Identity Votes (< 5 KSM): 7611 (46.97%)
High Identity Votes (> 5 KSM): 8398 (51.83%)

Identity votes are votes that include all the accounts apart of all accounts with an on-chain identity. Since most validators also have an identity, a lot of the people that this group is composed of is also likely them (there’s only 668 on-chain identites on kusama at the moment).

Fellowship

Fellowship Votes: 85 (0.33%)
Extremely Low Fellowship Votes (< 0.5 KSM): 5 (5.88%)
Very Low Fellowship Votes (< 1.5 KSM): 7 (8.23%)
Low Fellowship Votes (< 5 KSM): 18 (21.17%)
High Fellowship Votes (> 5 KSM): 42 (49.41%)

As @bkchr mentioned elsewhere, the fellowship might vote with accounts that are not directly tied to their fellowship account, so that might be why there’s a low rate of participation here. But of the 40 something members, the majority have 0 votes, these 85 votes are from a smaller subset of the total.


I’ll be aggregating and indexing this data over time, so will be curious to see what kind of experiments on kusama have different affects and influence it in different ways. If anyone else has ideas on how on-chain engagement and participation can be analyzed, or what kinds of experiments can be done to increase participation, would love to hear it :smile:

3 Likes

Imo this is not a good idea (at all).

If we want to drive forward diversity of decisionmaking and let a small cabal of validators dictate spending decisions the whole system will just end up being even more middle of the road than it currently is.

We need to delegate away from validators

Quoting @GabrielJ in Kusama direction:

Referendum 108 is a call to action for everyone to get involved in governance! The largest voter on the medium track so far has been Stake Plus with a 70,000KSM weight of delegated votes. At current turnouts on that track this represents a stake of 70% of the votes! Meaning Stake Plus alone influences the outcomes on this track.

This is alarming on its own but especially alarming for Referendum 108 as Stake Plus is a financial beneficiary of this Referendum. Hopefully we can get a bigger turnout on this referendum as not to end up in a situation where a single wallet/party can push through referenda that benefit themselves.

I am NOT saying that Stake Plus acts in bad faith and also not commenting on the referendum itself with this message. I am just saying that we desperately need more involvement and votes!

this in MASSIVE CAPITAL LETTERS

I’m curious why this is and how it relates to the quotations you’ve shared. Stakeplus doesn’t have massive governance power because of validation but because of being a whale–many validators aren’t whales, and being a validator doesn’t mean someone’s a whale.

And since we’re doing token-voting, I don’t think there’s any way to delegate away from whales–all we can do is delegate so much that we create new whales.

Unless I’m missing something… (?)

Adding my 2 gweis to the convo:

A simpler treasury system

How to incentivize voters while taking care of all these burned tokens?

We could just use treasury tips to redistribute the remaining tokens in the treasury to voters at the end of each era. IMO this is leading to a more optimal allocation of resources.

The problem of this design (basically “Vote to Earn” or smth like that) is that it could create an adversarial relationship between voters and proposal makers, but If we only redistribute to Aye voters then you’re basically incentivized to support treasury spending more wisely.

How to incentivize proposal makers to make better proposals?

We could also include a treasury tip for each proposal maker who passed a proposal through OpenGov.

This would incentivize people to make proposals as drafts and discussions and even if they don’t participate in the actual implementation of the proposed product they’ll still bring their ideas and talent to OpenGov.

IMHO these are two simple features that we could implement in OpenGov to enhance the overall voting system.

we need to diversify the collective intelligence of the network. validators are expert in a specific domain and will of course have varying expertise across other areas, but ultimately, we need to encourage delegation away from this one group - to optimise for other things.

I don’t think this is a tenable assumption. Validators, like anyone else, have various areas of expertise and experience, and so your premise isn’t a reasonable one. If your call to “delegate away from validators” were just a call to diversify, I’d understand the position, but as it stands, it’s both arbitrary and presumptuous.

As a relative newcomer, I’ve found that the validators I’ve come in contact with have been some of the most knowledgeable, engaged, committed, and circumspect members of the community, and your comments here read like a vendetta against them, with bold print, “massive capital letters,” etc. Am I missing something or do you have some particular reason to be targeting validators the way you are?

validators are a ‘type of network participant’.

we need to diversify the type of network participant. that is the point.

We’ve never actually discussed a multi-asset treasury before, but I’ve noticed the ideas come up twice, and fairly half-baked. In particular, we’ve no sensible threat model for oracles, so afaik they cannot be used by the treasury, but humans can form opinions about prices not and in the future, and make their own choices.

1 Like

This has been mentioned a few times by folks in Parity and as part of @joepetrowski’s Common Good Parachains roadmap:

It has been a long-standing idea within the Polkadot network for the on-chain Treasury to hold many assets, in addition to its native token. One, users making Treasury proposals may want to request funding in other assets besides DOT. In addition, the Polkadot stakeholder community as a whole may want Polkadot to acquire stakes in other assets. By providing the multi-asset support that Statemint specializes in, it can allow more options for Treasury use and management.

And then indirectly in relation to @xlc’s comments on XCM3:

If a multi-asset treasury is a way off - for the reasons you mention, then we can still experiment with governance ‘hacks’ of the system, with asset locks on parachains representing designated funds?

What do you think?

Just wanted to bump this thread noting that we have a big need for making retroactive payments the norm instead of proactive payments. It is becoming increasingly clear to me that the treasury system is being abused.

There is just too little accountability once users are already paid for their work, for the results to be lazy, low quality, or have little to no impact.

We need treasury funds to go to high quality projects, and for people to be competitive in the landscape to produce better products, not feel that they are getting easy payouts.

I see this akin to some of the inefficiencies of government spending and “no-competition contracts” vs the free market.

1 Like

One suggestion I might have for helping allocate treasury funds is matching ad rewards.

Content looking for funding, for example:

  • alternative documentation
  • guides
  • youtube channels
  • podcasts
  • etc…

Have all been traditionally successful for individuals using ads as a revenue stream for their content. Polkadot treasury could make generating content in our ecosystem doubly valuable by matching the ad revenue generated by the content. For example, if a youtube channel receives $1000 per month in ad revenue, then Treasury would match that and allocate another $1000 in DOT to that youtube channel.

We would set an upper limit, for example, $5,000 where we match up to a total of $10,000 per month. For example, if a youtube channel makes $7,000, we match $3,000. After that point, I think it is pretty fair to call a content source self-sustainable.

What does NOT make sense to me is overpaying for content which is never seen, and ultimately makes no impact to the ecosystem. Like, yes, I am very happy that people are out there making videos for Polkadot, but your content, at 10-50 views, is not worth $1,000 per video to the treasury yet.

4 Likes

this is an interesting model of co-financing.

ad revenue essentially becomes your “independent authority / oracle” assessing ‘quality’.

the key question is whether this is optimising for a specific outcome or general awareness. Ad models relie on CPMs (cost per thousand views) - low quality paid media is $0.01 or less, high quality/premium can be up to $30 - big sporting events are around this mark as advertisers know they have captive attention and generally seek out highly engaged YouTubers with aligned audiences and values.

Big sporting events are generally around this high level, content about Polkadot would be much lower, meaning views would have to be much higher. For $5000 ad revenue per month, a YouTuber might be doing 500,000 views per month, or more, not impossible, but its worth understanding the economics.

An alternative but similar approach is to see the treasury as a co-financing partner akin to the BBC/Channel 4 where it invests in media projects (IP) alongside other groups and takes specific ‘rights’ on behalf of its community / public.

When Netflix began, there was no such thing as ‘streaming rights’, it was a new niche that was carved out that the existing broadcast and cable companies saw no real value in.

You can see the ability to ‘token gate’ content as a new kind of rights model that can sit alongside TV, Cable and Streaming.

Now there is a path to the next ‘Silicon Valley’ comedy having some specific distribution window via token gated access. This show could even be developed by a decentralised collective…

This model would return fees to the treasury as people wanting to access the series and its additional functionality / content / experiences beyond that offered by TV / cable / streaming would pay for access.

An episode of Silicon Valley (30’ episode) would be roughly $500k per episode, over 8-10 episodes. As for the development lifecycle, generally a treatment comes first, then a script, then a pilot episode would be produced, with the series co-financed by a range of groups who all take some window for viewing.

A pilot would likely be less than $500k, but if the treasury funded the first 50%, its much easier to go to another party and get the remains.

Indeed a 1:1 match may not be enough to keep a Polkadot content maker funded, but there should be some proportion which makes sense.

End of the day, content should be:

  1. Found by the community to be valuable, and thus supported by the treasury.
  2. Found by the analytics to have large reach, thus proportional to something like a CPM system.

We should step away from content creators placing their own value onto the content they create, especially before anything is even made.