Flow Governance


There’s been some talk about our governance model. We’ve briefly discussed:

  • Turning governance into a sort of RPG-like thing. I threw some ideas into that thread, but it also led to my post later that day in which I explained why I think something’s “paint job” is important. Yeah, the post was a little nutty, but I stick to it.
  • Rewarding voters via reward “streams”; basically, a vote is placed, and the voter receives a month-duration stream rewarding them for their participation. This encourages consistent participation, as more votes = more streams = more income, but if the voter abstains for too long all of their streams will run out, tapering off their income until it eventually ceases.
  • Conviction voting, and voter decay. The former is a mechanism by which the voter expresses their preference, and the longer they keep that preference the more weight it holds; changing your preference takes the weight out and you start from 1x again. The latter is a mechanism by which the voter is encouraged to vote consistently, else the weight of their vote is reduced (until after they vote again); our interest in this concept was to use it in our quorum, such that “decayed” voters would count less towards quorum requirements.
  • Augmenting rewards with NFTs that amplify earnings (it’s towards the bottom of the OP). The idea is that (in the case of the linked proposal) voters, no matter which way they vote so long as they do so, are rewarded with an NFT that, when staked, amplifies LP rewards on Bancor.

Now that you’ve a little background, let’s delve a bit more into the specifics that I’ll use to build this new governance model.



I explain the technical aspects in a later post. I still love this idea. I think it has a great potential for helping us passively regulate and maintain the governance process.

Sure, it’s kinda-sorta meant for payroll, but it has so many applications beyond that; it’s a generic money pipe through which value flows at an arbitrary constant rate for a specified duration, and we can use it to time-delay the flow of value on arbitrary processes, and with compounding streams, it’s a money pipe that emits more money towards an arbitrary destination as its transiting the money you originally sent.

Decay, Conviction

I haven’t been able to get this entirely out of my head; it’s sticky–leaves residue. Basically, what I’m proposing here is that we use an APY-type mechanism to provide us with a live feed of voter engagement. Decay and Conviction are two substances that are used to measure activity from an individual voter; you may think of them as auras or fluids, but if that offends your defi sensibilities, they’re internal tokens/values of some description that you don’t start accruing until your governance stake is first established; new voters are not subject to decay from previous proposals, but also can’t vote on them (much like the current system).

When you stake vBNT, Decay will start accruing from proposals presented thereafter. If your Decay income is higher than your Conviction income, eventually you will slip into a condition known as Rot, which is where your Conviction is no longer counted until you pull yourself out of Rot. The gap between your Decay and Conviction levels implies the level of your voting activity, which falls roughly into one of four categories:

  • High Decay, High Conviction: These are older voters that may not be very active and may have recently slipped into Rot, but are active enough such that we can consider their return reasonably likely.
  • High Decay, Low Conviction: These are old voters that are not active, and their Conviction isn’t counted on any proposals they previously voted on until they vote on enough proposals that are giving them Decay to reverse the polarity of the neutron flow; the wider the gap, the more proposals they’ll need to vote on.
  • Low Decay, High Conviction: These are older voters that are very active and do not let proposals sit for long. The wider this gap is, the longer they can “vacation” from voting before their Decay gets too high.
  • Low Decay, Low Conviction: These are new voters. They’ve only recently entered a stake in governance, and we don’t know how active they are yet.

This model:

  • Inherently gives us pseudonymous (not totally anonymous because there’s people like me that put their name on it) historical voting data about each voter.
  • Automatically greatens the penalty for greater periods of inactivity in real time.
  • Automatically grants consistent voters “vacation days”
  • Cleanly measures voting activity from each voter, meaning rewards are easier to calculate.

Speaking of rewards…


There’s a good bit to take in there. We can take this idea and adapt it to the Flow Governance system laid out above. Voters never hold Decay or Conviction, they are entirely internal value/tokens, but we can use the gap between Conviction and Decay to calculate the rate of a third flow which the voters will receive, the reward stream, which I’ll call “Sap” for now. You can essentially take most of the talk about “steeping” and apply it to Sap instead of vBNT.

The “powerups” take the form of the aforementioned yield-augmenting NFTs, but we can do other things with them too, like reducing decay flow, or augmenting the effective voting weight used in calculating Conviction/Decay (and by extension, Sap).

Voters realize their Sap rewards by “tapping” their Sap reserve (an internal “tank” where the Sap flow from good voting health collects). They can then trade the Sap, or “steep” crates in Sap to receive augment NFTs, which they can use or trade. How they get the crates in the first place is matter for discussion; perhaps some kind of raffle where your Sap flow determines your chances of getting a crate?

The Governance Model, From The Perspective Of The User, Start To Finish

  • A user stakes vBNT.
  • A proposal is presented, and the user begins accruing Decay.
    • The user votes, and the Decay flow is stopped while a Conviction flow is started. The Conviction is “stored” in the proposal it comes from so it can continue to lend weight to the user’s vote.
    • The user doesn’t vote, and their Decay flow continues. If their total Conviction income is insufficient, they will eventually slip into Rot.
  • As time goes on, the user accrues a Sap balance according to their Decay and Conviction flows.
  • The user can “tap” this balance, which initiates a stream of Sap (via Sablier) from the protocol to the user.
    • Eventually, the user gets an augment crate (somehow) and steeps it in their Sap. When the crate is “cracked”, they receive an augment NFT which affects their position in governance somehow.
    • Eventually, the user accrues a significant pile of Sap and sells it for a sum which they can then deposit into an LP, or buy more voting power, or go buy an NFT that they want, or whatever they decide to do with it.

Share thoughts!

EDIT: That’s what I was forgetting!

Consequences For Delegates

Under delegation, the system works almost the same; delegates have larger flows because they have more voting weight, but they split rewards with their delegators. The portion could be determined by the delegate’s own flow (that is, what they would have without any delegates); active delegates get a fatter cut of the Sap resulting from their votes.

Extra Notes

  • Flows must never be capped, as it would effectively put an expiration date on voters; the continual and unlimited accrual of balances is our live data feed from a given voter, and capping the rate at which those balances can accrue means that after a certain point, we’ll have a much harder time judging that voter’s activity and deciding whether or not they should be included in the governance calculations. It also means we’ll have to force a final state for the voter–either active, rotted, or neutral (Conviction = Decay)–and we should not do that to voters who have been participating for a certain length of time.
1 Like

Since this was posted, I have begun building a Haskell program to serve as a proof-of-concept. I initially posted a functional code snippet; I later retracted it as I thought it was sub-par. (I’m a bit of a perfectionist when it comes to my code.)

Progress has been consistent but slow; I have very limited free time to work on this right now. Code is coming, but I want to make sure it’s good code so it’s (hopefully) easier to implement.

Finally, I have code that, though imperfect, I’m satisfied is worthy of review. The core of the program is more or less complete; I’ve not implemented APY as this is just a proof of concept, and “snapshots” should suffice for now. I am having trouble writing a simulator for it–I am awful at I/O, which in programming I’m pretty sure is equivalent to being bad at breathing, but fortunately you can do quite a lot with Haskell before you even need to know you have I/O and oddly I haven’t needed it a lot; I mostly write personal experiments rather than production software. That code is in a separate module, and the core is not dependent on it. I’ve posted the code on PasteBin; before I make further commits to this project I will ensure I have a git repository.

I’ve never showed anyone my code before… I apologize if it’s hard to read, I depend heavily on Layout and I chose (well… constructed, because I haven’t seen it anywhere else) a style that I found pleasing; as much as possible, I like the higher-order portions of my code to read like prose, so that the only thing that really looks like code is the most primitive stuff that most people don’t need to look at anyway. This is very much low-order, but I still tried (largely unsuccessfully) to keep the computer gobbledygook to a minimum.


Work continues. Part of the reason it’s so slow-going is that I more or less have to re-figure out where I am every time I pick the project back up again. This is because the only workspace I really have is my computer:

As you can see, I have two screens (I’m running Debian with GNOME, in case anyone’s curious); however, it could have a thousand screens and it still wouldn’t do the job, simply because it’s my personal/gaming computer and I use it for everything; I actually spend 75% of my not-at-work time in front of this damned contraption. All that means that when I want to stop working on this and go do something else, I have to put everything away, but I use the thing so much that when do that, mostly anything I save tends to get lost in a sea of personal-and-ultimately-pointless data if I don’t pick it up again less than 24 hours later. (Best case scenario, a week or so; worst case, it gets lost indefinitely, accidentally obliterated, then remembered six months after only for me to discover my dumb ass vaporized it months ago. I recently lost over a year of code this way.)

Notes are hard for me to keep because I’m either limited to a text editor (where I can make arbitrarily long plaintext notes so they can get lost in the abyss of my hard drive) or keeping around a lot of paper, which I can draw pictures on but simply cannot do the job to my satisfaction; among other reasons, because it’s too small and its use ultimately proves quite wasteful here. Notekeeping apps are just text editors with extra junk crazy-glued to it–truly it usually is junk, and the glue is usually extra crazy. Additionally, with paper, you either have to spend a lot of time burning the eraser on your pencil (and probably ruining the paper anyway), or you get one use and have to throw it away

The answer? Whiteboards, enough so I can put my entire brain on the wall without having to repaint said wall every week and without having to physically put my brain on the wall (which tends to get messy in my experience and is somewhat not possible to fix).

I’m going to start getting whiteboards and putting them up where I can. They are perfect for my purpose, because they can effectively be any size you want (they come in standard sizes but the nicer ones can be fit together really nicely), they’re reusable, and it’s really easy to wipe it clean and start over.

But critically, they allow me to write down/draw out what I’m thinking or what I’m working on, keep it neat and clean, and keep it there for as long as I need it without it needing energy or cluttering my desk or getting in anyone’s way. Obvious, yes, but mentioning these properties shows the kind of technology I’m after and what I need out of it, in case anyone has a better suggestion.

I think, once I do this, speed will pick up. Then the next thing after that is git. I’m kinda stuck doing one thing at a time and everything takes forever because I have, at most, an hour every day or so to work on things like this.

1 Like

Last night, I sat down and learned git, and now the project is a proper git repository… That exists only on my hard drive, for the time being. I’m looking into hosting a SourceHut instance (I’m a reader of Drew DeVault, which is how I discovered it). It’ll go on its own web server, though; I’m not sure it’d be the best idea to run it on my website’s server. Eventually, I’d like to have my own network of web services, but that’s later down the line. In any case, while web servers are actually kinda cheap, they do represent an expense which I will have to consider carefully.

I don’t use GitHub because GitHub is not so great, and I don’t use GitLab because it seems like it basically tries to be GitHub but more user-empowering, and I don’t really want a GitHub-like experience, because at a fundamental level, I just don’t like how GitHub works. I won’t even talk about the dumpster fire that is SourceForge; typically, if a project is hosted on SourceForge, I’ll just go find a different one if possible, I don’t like it there.

I’ve noted how this thread has turned into a kind of mini-blog for this project. I hope that’s okay. If not, I can confine such posts to a dedicated section on my website and just post here when something important happens.

I’ve had plenty of time over the past couple of days to work on this, the test program. There have been a good few branches while I experimented with alternative implementations, and played with features.

I refactored the core of the program, the Flow Governance system logic, so that I’m satisfied it’s as perfect as my current knowledge of software engineering in Haskell allows. (I may be mistaken of the field, and regardless my knowledge in it is probably rudimentary; I don’t really have any contact with any software engineers that I’m aware of, so I have some trouble gauging my knowledge in this field.)

In a separate branch I’ve also begun work on the program’s Voting subsystem, which will operate the core in the simulation to produce data–I haven’t begun work on the simulation subsystem yet. I’ve finished the Staking module of the Voting subsystem for now, however. Currently, it allows a (virtual) user to:

  • Enter an initial stake.
  • Grow/shrink stake by an arbitrary amount.
  • Multiply the stake.
  • Divide the stake.
  • Check to see if the stake weight is <= 0; if so, they’re unstaked–stop this line of computation now. The user would have to enter a new initial stake to be re-included in the simulation going forward.

I added these additional functions for when I can finally release the repository publicly, in case people wanted to build their own simulation scenarios and/or wanted to play with different reward/penalty ideas.

The other modules are the Voting module itself, and the Ballot module.

  • I plan to have the voting behavior (defined in the Voting module) controlled by PureMT on a fundamental level, somehow; I’m not 100% sure how to use the darn thing yet, but I’m figuring it out. I don’t know how detailed I can get with the simulated behavior, but it will have to be compatible with the Ballot module for sure. Ultimately, it’s just a counter, but I want to compute it in such a way that we get useful datasets out of this program.
  • Right now the Ballot module is fairly empty. I’m not sure how I’ll handle this one. I could have the number just go up randomly (again, with PureMT) and go no deeper than that, and I may do that just to get it working. Alternatively, I could go the route of some more complex attributes attached to individual ballots that might affect voting behavior in some way. Again, just a counter, but I want useful data from meaningful computations here.

I hope that this thread and the commit log of the repo (assuming it survives until I can get it onto a server :sweat: will do my best) continue to serve as a sufficient journal of my work on this project.

As an aside, if anyone saw the code that I actually commit, I imagine they’d scoff at how short my source files actually are considering how long it takes me to commit code; all the code in the voting branch, the heaviest one, only weighs in at around 4kb. The thing is:

A lot of planning of code happens on this very small (14"x14") whiteboard mounted in an awkward corner next to my desk, the only whiteboard I currently own. In this photo, the top half of the diagram is roughly where I was before refactoring (my design notation is garbage; I am not wise in the ways of category theory, yet), while the bottom half is the design I was considering. This design was ultimately scrapped, by the way, in favor of the current implementation, which I hope will prove to be vastly more elegant.

Last night I started experimenting with integrating cabal into the project. By my understanding, it’s incredibly useful for managing dependencies, and I plan to use it to make my life easier when expanding my local code library. My only concern is that it’s possible that in the best case, it just won’t fit my use case, but in the worst case it might weigh the project down. I’m not sure how it’ll go yet, since I’m still new to it.

Today I’m beginning work on the Simulation subsystem–in a separate branch off stable, which hasn’t merged the Voting subsystem in voting yet. I’m trying to keep development of the different subsystems siloed, so as to ensure they don’t end up depending on each other because I did something goofy. When I write the main program, it’ll depend on the subsystems and manage feeding data between them.

I’m learning the State Monad so I don’t have to reinvent the wheel here. So far it’s different to the other Monads I’ve used (Maybe, Either, etc.), but not drastically so; speedy thing goes in, speedy thing comes out. The overall concept I have so far is that the simulation will have its own state, called the “ballot state”, and the voters will each have their own state on each their own separate pipeline. As the ballot state advances, ballots are generated. The ballots are tagged with the state number they were generated in so that when the voter pipelines catch up they know which ballots to process. As voter states advance, they process ballots from the corresponding ballot state and decide whether or not to vote on them.

These pipelines must be bound by a few rules:

  • Voter states cannot advance before the ballot state does, but not the other way around. This is because voter pipelines require data from the ballot pipeline, but nothing the voters can do affects the ballot pipeline and it requires nothing from them; voting has no effect on the ballots themselves nor the generation of ballots, all it does it change how the system distributes flows to that voter.
  • Voter pipelines must not depend on each other, so they can be processed in arbitrary order.

A given voter’s flows in a given state are a product of the following data:

  • How many ballots have appeared between the time the voter first entered the system (by staking) and the state immediately before the one we’re in?
  • How many of those ballots has this voter voted on?
  • What is the size of the voter’s stake?

So far I’ve only implemented things in integers; this is purposeful. The logic is that we’re dealing with indivisible, fundamental units, and we’re not doing anything that should result in a floating-point value.

There will be no further updates to this project.
Close thread at moderator’s discretion; vaporize if desired.