An Explanation of Continuous Voting and the Peculiarities of the 7/26 Executive Stability Fee Vote

(Note, the Maker system uses ‘fanciful’ names for its internal system objects and functions, which tends to put off readers initially, but the author believes they are absolutely fundamental to quickly grasping the system’s complex concepts)

IMPORTANT INITIAL NOTE: This issue does not ultimately put the platform at any real risk, but will require more MKR to be put into changing the rate than one might have guessed initially.

Explanation of the Voting System, Overall

When the MakerDAO governance polling system signals for a Stability Fee change, it does not automatically change the official rate in the system. Instead, the rate outcome from the poll is crafted into a proposal with a spell, which is a contract deployed to the blockchain that, when cast, will do the magic necessary to update the system to the new rate. Once a spell is cast, it cannot be cast again.

The Executive Voting system is managed by the chief contract, which is where this new proposal is added. MKR holders who have locked their MKR into the governance system can choose to commit their votes toward any approved proposal in this list. The chief also has one proposal designated as its hat. The hat is changed when someone calls the lift function on an approved proposal that has more MKR voted towards it than the current hat. If cast is called on a spell that is currently the hat on the chief, the new rate change will go into effect (provided that the spell has not been cast previously).

A note about the ‘continuous’ nature of voting: The proposals themselves may never ‘expire’, and any MKR holder can support any one (but only one) of the approved proposals at any given time (including a proposal with a previously cast spell). Adding or creating proposals, voting a new hat, and casting a proposal’s spell are all actions that do not effect the MKR dedicated to the other proposals. MKR holders themselves must either withdraw their support completely, or change to support a new proposal, otherwise they will be continually voting for the same rate in perpetuity. This is the crux of where the issue arose today.

Explanation of the issue MakerDAO faces today

Historically, the system has worked such that the proposal with the most MKR committed to it was also the current Stability Fee (or the new upcoming voted fee). Over the past few months MKR holders have been voting in smaller amounts than back when there was more of a perception of a ‘rate emergency’. This has led to a number of former proposals in our system which have ‘dead’ MKR still supporting them, even though their spells have already been cast. One such proposal in the system has 78k MKR voting for it (whereas, the most recent executive vote on 7/19 passed with ~32k MKR). The 7/19 proposal passed because it had more votes than the current hat; however it was not the proposal with the most votes in the system (our 78k proposal that has already had its spell cast).

While older proposal’s spells cannot be cast a second time, they CAN be lifted to become the hat. What happened today is that someone with knowledge of this situation used the lift command on the older proposal, which promoted it to hat status. In order for us to cast the spell that matches our 18.5% proposal, we must get its MKR support to be above the current hat, not whichever proposal’s spell was most recently cast.

Either some of the ‘dead’ MKR must be withdrawn/changed from the old proposals, or we have to rally enough new MKR in support of the new proposal, such that we surpass the current hat's votes. Once the 18.5% proposal has more votes, we will be able to successfully lift it and cast the spell to enact the new rate.

Why is this a problem today, and not previously?

The short answer is that no one has lifted an old proposal before. The current UI doesn’t display proposals that have already been cast. Someone simply lifted an old proposal with lots of votes, which was a known and perfectly legal action.

I leave discussion of positive or negative impacts of this action for the comments, but one thing is certainly true: if this action drives more MKR voter engagement, then the system as a whole is much healthier.

ELI5/TL;DR:

  • Every rate change is stored as a votable proposal that can cast a spell

  • Voters support one proposal in the system

  • Proposals may not expire, though their spells can only be cast once

  • To change the rate:

    1. Add MKR to any proposal until it has more MKR than the current hat
    2. lift that proposal, it becomes the new hat (replaces old one)
    3. cast the proposal’s spell, (if hat, then the Stability Fee will change)
  • If you don’t actively change or withdraw your vote, you will continue to support that rate forever

  • Older votes were more popular than newer votes, and had more MKR still sitting on them

Today:

  • Someone lifted an older proposal with 78k MKR still on it (making it the new hat)
  • To pass the new 18.5% proposal, we now have to overcome 78k MKR (or get older MKR to move)
    • After that we can lift and cast the new rate into effect

[please correct if anything here is inaccurate, and super shout-out to @cmooney for much valuable editing and knowledge of the content within this post]

7 Likes

Great summary Psy, good work on the write-up.

As far as I understand this is not quite correct. If you don’t actively change or withdraw your vote after a spell is cast on your voted hat, you are not then continuing to support that rate forever, you are continuing to support the current rate (whatever it is, even if it changes) forever.

Any vote that isn’t directed at a never-cast proposal is a vote for the status quo, however it may be an ‘inactive’ vote for the status quo if it is not directed at the current hat. What happened this week was that someone activated (lifted) a collection of these inactive votes.

The governance system can be confusing.

1 Like

Would it be 1) reasonable 2) easy to at least make previously cast spells unliftable? It would partially get rid of dead votes without explicit timers etc: if ‘active MKR’ gradually goes down and then gets split over competing proposals, old MKR in already unseated proposals has no influence.

1 Like

I believe your understanding is inaccurate, though someone else can please correct me. In the case where you supported a rate that was subsequently replaced, your MKR will stay committed to the older rate (is not moved to the new rate by any automated process, except when the user actively votes for a new rate). If the older spells had not been lifted, we would not have had to beat their MKR to lift our new spell, and they are not combined in any way as part of the process.

There is no point where the user has indicated that they support any rate other then the most recent one they voted for, and I think the system accurately reflects that state. I, personally, think this is unlikely what the voters intended, but I also think we should take a measured and thoughtful approach to changing it.

1 Like

The fact that previous spells cannot be recast is the sticking point for me. There seems be little to no value in continuing to support a rate that has no meaningful impact on the future of the system (beyond being liftable and messing with our newer votes :stuck_out_tongue: )

I believe, upon a cast event for a new spell, that all MKR committed to other, already casted spells, is dropped from support. Essentially, drop all active MKR support that is not on the newly cast vote. This is distinctly different from shifting all MKR to support the new rate; I would think, whether by apathy or dissent, these voters were not in favor of the new rate, and should not be counted as such.

The main downside to this, in my mind, seems to be that it will ultimately require more active engagement from voters to keep total MKR support high, and to lower our potential for whale attacks/games. Having a large chunk of apathetic/dead MKR doesn’t seem to really prevent us from this situation today, so I’m not considering it much of a risk-shift.

Interested in other’s thoughts on this, though

1 Like

So, I looked into making a proof-of-concept fix that would prevent previously cast spells from being lifted. In the lift function I was expecting this to be as easy as:

--- a/src/chief.sol
+++ b/src/chief.sol
@@ -106,6 +106,10 @@ contract DSChiefApprovals is DSThing {
         note
     {
         require(approvals[whom] > approvals[hat]);
+
+        (bool done,) = whom.call(abi.encodeWithSignature("done()"));
+        require(!done, "ds-chief-spell-already-cast");
+
         hat = whom;
     }

But, a few tests failed and when I dug into that, I realized the test suite was just using plain old addresses. That is, it was never really stubbed out for spells. That made me dig around and I found this bit from the documentation:

Though anthropocentric language is used throughout this document when referring to the “chief,” you should keep in mind that addresses can represent contracts as well as people. Thus, ds-chief works just as well as a method for selecting code for execution as it does for realizing political processes.

This means the behavior I was seeing in the tests wasn’t just a shortcut to save work writing the tests. In fact, if you look at the chief there is really nothing that indicates these functions need to be spells. They easily could be multi-sig wallets, proxy contracts, or even just a single address.

So the easy fix above would certainly solve this particular case, but it would be a redesign that would prevent anything but spells (or contracts that implemented the done() interface) from being lifted. This certainly breaks the original intent of the chief, and our view into the problem would appear to be clouded by the lens governing the SF.

I had mentioned in chat, after thinking-out-loud a bit, that the community should simply run a script off-chain that always lifts the proposal with the most votes as a guard against this unexpected behavior. This would also guard against abuse of this system as a result of apathetic voters.

The downside is that, with a deflationary MKR and possibly a more apathetic voter base over time, we eventually are unable to pass anything because we simply can’t overcome the MKR left on a proposal where the participants are no longer active in the system.

So, I suggest a short term fix against unexpected behavior is that we always lift the proposal with the most MKR, while the longer term fix is likely a change in the constraint that anything can be promoted so long as it has the most votes, or “simply” a redesign of the chief.

NOTE: There is currently active work on a redesign.

EDIT: Looks like this case is handled in the proposed v2 of the contract here

7 Likes