A Better Treasury System

Hey shawn - this is a good approach, and marries closely to where we’re moving with our approach to designing a more engaging, interactive and exciting governance stack.

A few thoughts based on our experience / what we’re working on / where this can get better.

My primary point is what is the fundamental outcome we have in mind when designing all of this stuff.

We should be very clear as to what certain developments aim to optimise, for who and what purpose, since these problems are all part of a bigger picture wrt to driving ‘intelligent adoption’ of the underlying tech/resources/funding, rather than just creating incentives that independently optimise for one narrow but specific outcome.

Without aligning on a core motivation we are in danger of collectively making the overall system worse in aggregate.

A starting point for this is sourcing, sustaining and scaling collective network intelligence.

This is a very different proposition to simply ‘driving adoption / participation’.

First problem, outcome, solution:

  • Problem: complex voting systems
  • Outcome: poor voter participation
  • Solution: simpler mobile app UX/UI w/ swipe / notifications

Making governance voting easier is definitely something we should aim for and simple more proactive UX/UI is important, but it can also optimise for dumb decisions - aka, there should be some time cost to making a good decision and that should be implicit in the design principles.

Other projects such as Proof of Chaos also aim to create incentive systems for encouraging voters - an idea that’s both brilliant in its simplicity, but also potentially dangerous in its current design… e.g. you earn an NFT for voting, but that NFT is not necessarily non-transferable, and so might be tradeable, which leans into the emergence of financial incentives which abstract away the actual ‘vote’ as the purpose of the interaction, ultimately making the system dumber over time.

When seen this way, we can also understand voter participation as an entertainment problem. Yes we want to get people to swipe easily, (tinder for governance) but we also need to make proposals more engaging, so people will stop and read / listen / watch and make decisions in a proactive way - delegating their intelligence, as well as their vote.

Second problem, outcome, solution:

  • Problem: no standardisation of proposal data
  • Outcome: standardised proposals
  • Solution: proposal forms

This is a problem I’m very well aware of, having written Edgeware’s proposal templates, written / structured many proposals for myself and others and reviewed/voted/commented on many more.

The standardising of data inputs - is something we have debated endlessly, since we have no lack of proposals, but a lack of data standardisation causes issues nonetheless, with everyone essentially creating their own structures.

The issues of no standardisation:

  • For proposers: a blank sheet of paper is harder to fill out than a few boxes which trends towards proposer apathy / missing info / administrative time suck.

  • For voters: makes comparison hard when comparing two proposals on a like for like basis this effects voter engagement, participation and confuses overall sentiment.

A form is the obvious answer to address short term issues, but when we approach this challenge from a longer term perspective of optimising for a bigger picture - sourcing, sustaining and scaling collective network intelligence we can see that some standardisation is useful, but given the diversity of talent we have the potential to fund, across many domains, who each may prefer a different medium of expression, we can then see that standardisation also constrains the intelligence of the collective - voters and proposers.

Put more simply - when we start with the idea that no proposal is final, or correct, or cannot be improved, then we begin to design different systems, optimising for advancing coherence between proposers and also voters. who we should aim to move towards contributors and even co-creators of proposals.

This starting intention has two primary effects on the way we imagine and design these systems:

  1. We take the pressure off people to write ‘great proposals’ to convince voters.

Currently we are headed towards ever longer documents, associated materials, references, spreadsheets, colourful language and big promises that adds to the administrative burden on everyone and further reduces voter / proposer participation and makes the whole process into something that optimises for a certain type of specialist proposal writer.

  1. We move the system away from binaries - proposers / voters who end up in an adversarial relationship, and towards outcomes that prioritise collective endeavours.

This is perhaps a radical, but obvious approach, that aims to optimise once again for a larger aim, than simply engineering away the symptoms of much bigger issues.

Preferendums

Referendums offer binary votes on some package of information, but as we know they are very dumb tools.

We are developing Root - a collective creation and decision making system that uses a composable voting system inspired by Borda’s Count to compute voting and allow contributors to create preferendums directly linked to the governance pallets.

As a proposer, I can create a draft with as much or as little information as I am able, using whatever titles / headings / structures or even in the future mediums (code/text/gif/image/video etc).

As a participant, I can create alternatives to a proposal’s content, structure or subject, add my own view directly on-chain, and tend to a consensus by voting for my preferences for a better decision-making process.

On first impression, this might sound like it will have an even greater administrative burden on proposers and voters - but this is before we consider:

  1. Economic participation.

Suddenly we can open up proposals as economic opportunities for anyone to review/improve/iterate proposals and indeed the projects themselves.

Engagement can have a reputational, financial and creative upside, that aligns incentives between all parties better than the current system.

It also starts to solve other issues - namely, information assymetry between voters (who are likely more familiar with the core chain/tech/culture) and outsiders, namely those wandering into the lion’s den - with excitement and energy ready to be pummeled out of them…

We can see how from here we also begin to solve issues such as talent acquistion, development and accreditation using the system to bootstrap the sourcing, sustaining and scaling collective network intelligence.

  1. Imaginative proposals

We inspire more originality and opportunity. By pushing in exactly the opposite direction to form creation and data standardisation, we design in a more humane way… appreciating that what works for some, will not work for all.

  1. Boostrapping on-chain organisations (creative collectives)

If the process just ends with voters approving funds into a ‘project’multsig’, we essentially leave the funded team successful in one part of the process (getting funding approval) but left entirely on their own to figure out delivery of what is in essence something that almost always needs to mesh with a complex and ever changing underlying system.

This leads to proposals taking far longer, teams being paid far less, whilst voters get irate at the time taken, which leads to mistrust, which further exacerbates tensions, which further degrates the governance process. Both sides move apart, and lessons are never learned.

If we can drive forward more nuanced and interactive decision making, that leads to more imaginative proposals that enable us to share financial value and credit more fairly across a group, we can then begin to see this whole process as the pre-formation process for talent sourcing and the setup of fluid and optimistic on-chain organisational structures like shokunin’s proxies.

I’ve written more about this approach in a previous post.

We need to manifest the murmeration - in many ways, with this forum, we are already seeing that happen…

This is core to realising and indeed releasing polkadot’s potential - if we don’t, the existing incentives will exert their own gravity and pull inwards with inevitable consequences.

this is what we have been doing at a small scale for 2+ years in Edgeware - we call it 'network services’.

oversight can be achieved using anon-proxy based organisations w/ governance oversight from relay/parachains.

and

Saw this too - we’re also UK based, worth chatting.

Thank you, Rich. Very interesting. I will delve into it over the weekend, with pleasure.

Regarding Network Services in Edgeware, it would be interesting if you could share the learnings from these experiments: what worked better, what worked worse? If you happen to have any links to recommend where there is some commentary on the model results, I would happily read them.

About optimistic orgs or other solutions to ensure oversight, I will look into that, I am intrigued. In general, an oversight function or optimistic organizations and dispute resolution are two tools that fulfill different needs. Dispute resolution should be handled neutrally, ensuring both sides of the relationship (a fair judgment). Oversight through anon proxy seems to me more like a governance tool to protect the money proxy.

Gladly! We are not UK based, but let’s surely chat

1 Like

Hey Folks,

I’ve conducted an analysis of all things mentioned in this post, let me try to sum these up and add my own suggestions, as Ordum is building solutions for parts of this user journey and we’d be happy to expand our scope if additional features are in the interest of the community.

Let’s start from the beginning as @rich mentioned: what is the treasury for and for whom does it need to be optimised for.

@shawntabrizi has opened the initial post with this answer:

Based on this response, I think we need further segregation and project references to demonstrate to new teams what the treasury is looking for (eg. Events, media…).

Let me share a user journey map which documents:

  • Off chan processes
  • On chains processes
  • Different groups/users involved in the process (delegates / delegators , community, fellowship, applicants)

Now let’s sum up all the pain points everyone mentioned and see where they create friction within the user journey:

Let’s breakdown every step and address problems / solutions:

  1. Understanding of Blockchain / crypto currency (For an example, we can have a Polkadot educational site where a new user can generally learn about blockchain. One of the issues we are facing is this GENERAL lack of education and understanding of what cryptocurrency is, let alone what DOT/KSM tokens are used for. The general public needs to comprehend the utility behind our tokens… and stop perceiving them as purely an investment / security - which clearly they are not. This would increase participation and voter turnout if our current holders / investors are interested in doing so, they just did not know).

  2. Understanding of the Paraverse technology and functionalities; Substrate, Rust, Pallets etc—> I believe we are tackling these with the educational content that keeps coming out(academies, videos quizes etc) as well as plenty of builders programs which we wish to aggregate within Ordum and guide potential applicants to relevant programs and grants within the ecosystem. There will be a steep learning curve but we can tackle this friction with time and links within a dash board to relevant educational information. Also; we need 1 place for all of our documentation, currently it is a bit scattered.

3. Understanding the treasury system and submissions

Our system is quite complex and we have 3 types of grants(+ NORMAL grants lol :sweat_smile:). In my honest opinion, the new Polkadot site explains very well how all of these work. If we link in our dashboard the relevant docs and videos, this would probably be very clear(would need data to confirm, it’s obviously an assumption). There is also a question here to be raised. Do we want to make our treasuries available to non crypto people? Eg. what if someone wants to build a power grid in a third world country?

I would personally like to see more examples of different types of projects such as events etc.
Additionally, it could be helpful to have a more detailed list of projects the treasury is interested in funding, as well as a detailed breakdown of the active bounties so that projects are directed to the right place (eg. Why go to the ksm/dot treasury if there is an events bounty? And if the events bounty rejects an event I think the treasury should do so as well and vice versa. This protocol would prevent bad actors).

Bonds could be lower for cheap projects, but I think for very high amounts it should be relatively high just to prevent spam (not personally sure what this price be. Possibly just on Polkadot and not on Kusama as it currently stands).

  1. Idea - I think this is self explanatory lol

  2. Application
    I have to admit, filing out those google docs templates feels like torture and that whole process feels like a crime against humanity. On that note, I agree here with Rich and Shawn here;

We need to make our template much more UX friendly, precise, with boxes(lol). Within Ordum, each grant issuer / foundation would be able to custom build their own boxes(lol again xD) for each program and grant. Filling out a non UX friendly, hardly legible and low customisable template with serious, academic content is off putting. This needs to change. Now.

It is bad for the applicant due to a straining process, while the delegators / delegates and community will have a tough time reading these due to low legibility. We have summaries on Polkassembly / Subsquare / Commonwealth. We should have clear guidelines for TL;DRs so delegates/delegators and the community can quickly read the summary and additional details if needed to make a decision(eg. problem, solution, amount, category).

(we can also hint here to voters that there are multiple proposals in the same Origin they can vote for, so they can choose which one they prefer)

I’d also like to touch upon Shawn’s other two points regarding guidelines:

—> we can add these guides within Ordum and were thinking of doing so

We are capable and interested in adding all of these to Ordum. One of our research milestones focuses on the interaction of non-transferable, reputation based NFTs, smart contracts and DIDs / wallet addresses. So we will be working on the reputation system which will be displayed in an applicant’s profile. Since we are building on Phala, I think it would be good for applicant / grant recipients to be able to showcase when, by whom and to what extent have they been funded in their road maps. Each grant issuer would be able to mint their reports as non-transferable, reputation based NFTs which would be bound to the teams wallets or DIDs. We can continue the discussion and see which metrics would be beneficial to this system.

—> on the other side, we can add warning signs to the community for teams asking for high amounts with a low delivery score. But we must be very careful here, as this might have ethical consequences. In terms of plagiarism, we could implement machine learning to compare the published proposals with existing ones (like we have for academic papers in universities etc) and flag them for plagiarism or some kind of non-compliance. Again, touchy subject, we would have to be careful here.

We can add this as a drop down when applying for a grant within Ordum.

—> as well as all funding types and builders programs.

6. Evaluation & 7. Voting

Again, I very much agree here with Rich and have raised the same issues regarding proof of chaos; our users should not be “bribed to vote”. I can understand entities issuing non transferable NFTs to voters… but tradable… IRL this would be considered bribery.

On that note, I disagree with the Tinder interface (it already dehumanised people, let’s not bring it into something as serious as voting. If linked with proof of chaos then people will just press vote to get NFTs. However, I think there should be quizes around technical referendums which grant awards for some kind of completion and comprehending knowledge, rather than the vote itself). I think Nova Wallet has an amazing governance integration. Let’s face it, most people don’t know that there are motions and proposals out there that they can vote on. With mobile notifications and such a slick UI, everyone can easily be reminded of what is going on.

I agree with this statement. Until we put an idea out there and get constructive feedback on how to go on about some things, gather information and data in the fields that we are not experts in or exposed to… then we simply do not know what is out there. Hence why we should let the community speak and give feedback. And perhaps this is where something like proof of chaos comes in… to incentives those giving constructive feedback.

I think we should have relevant sub-daos and bounties set up for evaluating projects within their expertise. I am sure that all councillors in the past needed to vote on something which was not their area of expertise. In my opinion, those providing feedback should be incentivised and compensated, ESPECIALLY if they are experts in their own rights. We need their help to shape better proposals. These could also be groups to which we delegate our votes to (eg. Shokunin Network could put forward and evaluate \ give feedback to public goods, VR projects, etc). I am not saying these should be official bodies like councils, nor that their opinions can sway the vote, but they provide relevant and quick feedback on the proposals relevant to their fields. These “experts” can be ranked the same way as the fellowship, but let’s say the group consists of UX/UI experts.

Once the applicant is ready to submit the proposal on chain, we can enable this in Ordum with a simple UI when they are ready with their draft.

One thing from my perspective that is missing is some kind of timeframe to signal that the proposal is ready to go on chain or some kind of notification that says: OK FOLKS YOU HAVE ENOUGH INFO GO AND SORT THIS OUT. Not sure how to automate this, but would be good to do so, as it would take a lot of responsibilities from other people… but I differ.

Payment execution, accountability(I’ve broken the solutions for this down above so I won’t comment further on it) and volatility -

a. Accountability —> resolved with reputation management, DIDs, non transferable NFTs

b. In case the payment is a reoccurring spend, I suggest that we have an X amount of time until it lasts, then the spend of the teams gets re-evaluated and adjusted based on metrics and delivery success ratio.

c. We either create or use existing web3 accounting software or on chain / off chain statements and databases to see how the funding was spent

d. If someone from the community sounds the alarm that the funds were given to back actors, we should be able to put the proposals up again for a vote, have AMAs, look into data etc to clarify situations —> then add this to the rep score.

e. Build custom pallets for payment systems so we can automate this process but also block it

f. Stable coins - this is erm… a touchy subject, especially in regards to the Luna crash, aUSD de-peg, questionable affairs by USDT etc. If we would to issue our own algorithmic stable coin, we would have to keep in mind that regulators will probably put a giant magnifying glass on us . The other option would be to use USDC (which I am not a fan of for certain reasons, but I have my reasons to believe that it won’t ever de-peg) and/or encourage teams to use a preferred stable coin or which one NOT to use.

g. It was mentioned elsewhere that is up to the team to decide what they do with the grant finances, however, I would personally prohibit them from using these funds in defi etc, just to prevent, erm the loss of funds.

Report + 10. credit -

a. Once a milestone is complete the team will be able to submit these via Github and/or Ordum UI for evaluation (the grant issuers can define the template on their side of the UI)

b. if the team had any changes to the milestones they can mention it here and adjust accordingly

c. I think the sub-daos, delegators and community should together evaluate the proposal they voted on or consulted on

d. Each treasury / foundation / grant issuer mints a non-transferable NFT with the report that is bound to the address of the applicant. This would be displayed in the Ordum profile section or an entity can enquire to see it if needed.

11. Analytics
a. We can define specific parameters and scrape data from the chain and reports(and we have some awesome tools already out there for this purpose)

b. We’d need to define KPIs by which we measure treasury impact in order to see what the project success rate it

Right. So I think this kind of sums it up. I won’t refer further to Shawn’s UX/UI post as I think we need to define the overall architecture of these systems and how they interact together. After all, we discussed 3 different things:

  • treasury
  • governance
  • designing dapps for both

I’d love to hear your feedback above on the user journeys and the summary… and once you do share your thoughts I’d personally be very eager to:

  • design the information architecture
  • we at Ordum can conduct technical research and actually build this in cohesion with other projects
  • design the UI
  • Build with the help of treasury backings

Thanks for your time, I hope we can all work together to make this experience amazing and our ecosystem even better.

1 Like

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?