Limbo Dev Update: Governance
On April 29th, I released an article explaining the need for a token bonding curve preseeding event for tokens about to be listed on Behodler. The article ended with a high level explanation of a token listing liquidity mining dapp code named Morta. Since then, the Behodler community enthusiastically participated in a name vote and the upcoming dapp is now known as Limbo. This article is the first of a series of development updates on Limbo, exploring the challenges in building out such a novel dapp as they’re encountered and solved.
Limbo can be divided into three main components: the Limbo staking contract, the reward token Flan, and LimboDAO, the governance module. Since the staking contract and the issuance of Flan both require ongoing calibration by the DAO, the first contract to be deployed will be LimboDAO. From there, the rest of the system will be bootstrapped. For this reason, the rest of this article will delve into some of the finer points on the governance of Limbo.
Outline of article
- Breakdown of Limbo into 3 parts
- LimboDAO and proposals
- Fate, the voting currency
- Earning Fate by staking EYE or EYE LP
- Fictional example to demonstrate how governance would work
- Dev update
- A word on code transparency
A high level view of LimboDAO: proposal oriented governance
LimboDAO will allow holders of EYE and EYE LP tokens to directly control the calibration of Limbo staking parameters. It will also give holders the ability to alter the rules of the DAO itself and in the extreme, replace the DAO altogether. If someone wants to effect a change in the system, they deploy a contract that makes a change on either LimboDAO or Limbo. We call such a contract a proposal. By default, a proposal does not have permission to make any changes until the community has approved its usage through an onchain voting system. In order to have a proposal approved, it has to be submitted to LimboDAO with a deposit. The proposal contract can then be voted on for a certain period of time. At the end of the period, if it has accumulated sufficient yes votes, it can be executed by anyone once, at which point it is set to unapproved once more. If a similar proposal is lodged in the future, there’s no need to deploy another contract. Simply re-calibrate the old proposal with new values and submit it again. The proposer gets back half of their deposit. If the proposal is rejected by accumulating more no votes than yes votes, the proposer forfeits the entire deposit.
Fate: the currency of voting
When designing incentives for voting, we want to achieve the following goals:
- Create a reason for holders of EYE and EYE LP tokens to gain voting power.
- Encourage long term holding of EYE over having people acquire EYE just in time to vote, only to dump it post vote but…
- Give voters control over how long they wish to hold their EYE rather than require mandatory lock up periods.
- A negative incentive to prevent proposal submission spam but…
- we don’t want voters to lose any EYE or EYE LP.
- Smallholders (fish) should have a reasonable incentive to participate in the presence of whale voters.
“Do you believe in Fate, Neo?” — Morpheus, Matrix, 1999
Goals 2 and 3 are in tension since we want to encourage long term holding of EYE without making it unpleasant to hold EYE. Similarly goals 4 and 5 are in tension because we want to prevent a situation where voters are required to continuously reject bad proposals. If proposers faced no cost to proposing then malicious actors could perform a denial of service attack through voter fatigue by continually proposing malevolent changes. However, we don’t want honest proposers to be penalised in the pursuance of security.
The resolution to the above tensions is to introduce a staking points system. When voters and proposers stake their EYE assets, they receive voting points, known as fate, on a per second basis. Fate is non-transferable and can be used to either vote or to make proposals. When you vote, you spend fate so that your fate balance is reduced. You receive fate in proportion of the EYE staked and in proportion of the time spent staking. From point 6, we also want a holder who stakes X for 2 hours to receive more fate than someone who stakes 2X for 1 hour. This protects the system from flash whales and rewards long term commitment. To understand how this all fits together, we’ll go through a fictional example from beginning to end but before that, we’ll first outline how fate is earned in the system and how much proposals cost.
- Stake EYE. Staking EYE yields the square root of the staked position per day. Eg. if you stake 100 EYE, you receive 10 Fate per day. The fate is issued on a per second basis so that if you stake 500 EYE, you receive 0.000258804 Fate per second.
- Stake EYE LP. Staking Uniswap V2 or Sushiswap LP that contain EYE will yield fate. Take the square root of the EYE balance of your LP tokens and multiply it by 2. For instance, suppose you have an ETH/EYE LP that contains 1.4 ETH and 400 EYE. You will receive (2√400) =40 Fate per day.
- Burn EYE and immediately receive 10x the EYE burnt in Fate. This option, though undesirable, is for when a very bad proposal appears to be on the verge of winning and there isn’t enough time to acquire the necessary fate in time. Alternatively when a good proposal is in danger of losing. To illustrate how potent this is, suppose you require 100 Fate to seal a vote. You have 10 EYE on hand. If you stake your EYE, you’ll receive 3.16 Fate per day. This will take 32 days to acquire 100 Fate. Alternatively if you burn the 10 EYE, you’ll receive the 100 Fate right now. The benefit of this power play is that it effectively gives small holders veto power to intercede in the case of bad votes but the cost of that veto power is an enormous skin in the game sacrifice.
- Burn LP tokens. Here, the LP tokens aren’t technically burnt but simply locked permanently in the LimboDAO contract. The formula is 20x the balance of EYE in the LP tokens.
Cost of Proposals: To start with, proposals will require 446 Fate. If the proposal is a success, half of this (223) is returned to the proposer. If it fails, the entire 446 is spent. To put this number into perspective, if you stake 50000 EYE, it will take two days of staking to acquire 446 Fate. Of course this can be acquired quicker. If you stake 1 million EYE, you can lodge a proposal after approximately 11 hours. Conversely staking 1000 EYE would require 14 days of staking.
Alice decides to stake 10000 EYE on LimboDAO. Bob has recently created a token pair of EYE and USDC on Sushiswap. His LP position contains 400 EYE. He decides to stake the entire amount. Phillipa wishes to lower the cost of issuing proposals. However, in order to make this proposal, she will have to acquire 443 Fate. She has 20 000 EYE and begins staking. After 54 hours of staking, she has the necessary Fate to make the proposal. Prior to this, someone has already created a ‘change proposal cost’ proposal so she doesn’t need to deploy a new proposal contract. She simply needs to set the number on the existing contact. She’d prefer if more people could realistically make proposals so she sets the cost to 40 Fate. The means that if her proposal succeeds, someone with a balance of 2000 EYE can lodge a proposal after only 21 hours and 30 minutes of staking.
Phillipa sets the proposal cost parameter on the contract and lodges the proposal. However, there’s currently another proposal up for vote. This one concerns how much SCX should be burnt when a listed token reaches its threshold value. The proposal is to burn less SCX than is currently happening. There are 19 hours left to vote on the current proposal. For this reason, Phillipa’s attempt to lodge a proposal fails.
In LimboDAO, only one active proposal can exist at any given point in time.
After a day of waiting, Phillipa tries to lodge her proposal again. The SCX proposal has not acquired enough votes to succeed and so cannot be executed. Phillipa lodges her proposal. There is now a period of 48 hours during which fate holders can spend their votes on yes or no. Phillipa takes to discord and eagerly argues her case. She wins over Alice who casts 100 fate on the vote. Bob is still undecided so she takes to Twitter to broaden her campaign to win over those who haven’t yet cast their vote. Her proposal becomes immensely popular and wins an overwhelming majority of support. 600 Fate is cast in favour and 332 is cast against her proposal. Bob has acquired 43 Fate over this period but decides to abstain from this particular vote since 43 Fate won’t have a significant influence either way. After 48 hours, Phillipa can now execute her proposal. One of the OG members decides to trigger execution on her behalf. Thanks to Phillipa’s proposal, the cost of proposals going forward is now 40 Fate.
Immediately after winning, an eager new community member wishes to list a pump and dump scam token called Rottweiler on Limbo. The proposal is a standard token listing proposal and so must contain such details as how much flan per Rottweiler stakers receive per second, the price oracle used to determine when Rottweiler has reached the critical threshold to be listed and so on.
The community is angered by the suggestion of Rottweiler because it fails to meet many of the critical standards of Behodler including a vulnerability that would allow an attacker to mint infinite Rottweiler. The vote against the proposal is overwhelming with 1000 fate against and 20 for. However, the proposer burns 100 EYE and receives 1000 fate and pushes the yes votes in favour by 1020 to 1000. In order to prevent a sneaky last minute win for either side in the last hour, any votes cast with one hour remaining extend the voting deadline by 2 hours. EDIT: it has been suggested by a community member that the deadline only be extended when a vote flips. This is being investigated.
Last minute votes extend the deadline by 2 hours to prevent miner orchestrated wins.
One of the benevolent whales notices this problematic situation and stakes DAI/EYE SLP containing 240 000 EYE. This generates enough fate to push the yes votes past 1020 before the voting period ends. The Rottweiler proposer doesn’t wish to commit any more cost to this endeavour, having learnt in finer detail the listing requirements for Behodler and so decides to let the proposal fail.
Progress in Dev
I’ve finished the staking logic and testing is complete. The proposal logic has been mostly fleshed out in code and requires a little refining and then test writing to cover edge cases. Governance is by far the hardest part to get right and so absorbs the most dev time but the hardest part — figuring out the mechanics in a way that is internally consistent — is complete. Since all of Limbo will be deployed to Kovan for user testing, this article can serve as a refresher on the justification of the operations as everyone familiarises themselves on the testnet.
After proposals have been completed, the next step is to write the staking component. The most challenging part here will be getting the pricing numbers right. As the recent attack on Pancake Bunny demonstrated, naive price oracles that can be easily manipulated by flash loans can pose enormous risk. For instance, in Limbo we require that the quantity of tokens staked exceeds the average bonded value on Behodler. If the price of the token is overstated through oracle manipulation then the minimum quantity will be too low and attackers can then engage in an early bird strike on the new bonding curve.
The source code for Limbo is open sourced and can be found here. Any soft audits and code reviews are very welcome! In the testing folder, there is a markdown file documenting test coverage. For checked items, if you find a bug or an implementation issue that I haven’t picked up, please submit a pull request or open an issue and if you submission contributes to the final code base, you will receive a free batch of Fate when Limbo launches. If you’d like to perform a thorough audit report on the final source code, please contact me or one of the team members directly to discuss pricing.