Let's talk about governance

How the clr.fund should be governed?

I suggest that we use the principle of separation of powers and keep the the matching funds out of funding round factory contract.

The matching pool contract should serve as a wallet with a permanent address, so dapps and protocols can direct fees into it. The govenance of this contract would mean choosing which clr.fund instance receives funding and at what rate. The exact model of governance should depend on the nature of funding sources.

The clr.fund instance will not hold anything and the matching funds will be transferred directly from matching pool contract to funding round contract at the end of voting period, allowing the governors of the matching pool to cut the funding at any time. The governance of clr.fund instance would be limited to selecting recipient curation and user verification mechanisms, setting MACI parameters and appointing the coordinator. To cover the costs of managing the clr.fund instance its owners may register as a QF recipient or they can take a small fee from each transfer of matching funds.

2 Likes

We actually talked a lot about governance early on and came to the conclusion that introducing any governance on matching funds (beyond QF) would introduce more problems than it would solve.
For example, many funding sources (protocols, block rewards, etc) would be unable to participate in governance, and thus those that could would have disproportionate control over funding.

Ultimately, we decided that once funds are contributed to the matching pool, the former owner loses all claim over them and the only way they should leave the matching pool is via the QF mechanism.

The governance power that matching fund contributors do have is to vote with their feet. If the instance of clr.fund is not distributing funds in a way they like, contributors can simply cut off future funding.

Obviously, this puts a lot more power in the owner role, so we should be cautious to make sure it is not captured / perverted.

The route that I imagine for that is to start with owner being the clr.fund multi-sig. The owner role should then be transferred to a DAO of some description, some options that I like are:

  • the clr.fund colony, assuming that it 1. has the capability to call arbitrary functions on other contracts and 2. has a wide enough reputation distribution to be better than the multi-sig (this is my primary reason for pushing us to use the colony from early on).
  • a futarchy DAO. While none exist yet, I actually think that this would be the best option, as we could base decisions on predictions of how they will impact key metrics (like funds contributed, number of contributors, funds distributed, etc). It would also be much less prone to capture, since there is such a strong profit motive for prediction market participants to be accurate, rather than self-motivated.

One thing this does bring up is our contract’s upgradability method. One thing we could look at doing is using EIP-2535 diamond standard contracts to separate concerns and limit which functions can be upgraded.

1 Like

Transferring control over funds to the same people who run QF is not a solution either. This just makes the whole thing centralized. The funds should be kept separately and governed by the entity that provides funding:

“Governance” could mean anything here, because there’s always some governance. In most extreme case there’s a governance by hard fork.

I agree, and this is what I’m talking about in my post: we need archictecture that would allow matching pool contributors to implement their own exit mechanism. Because in cases of funding by protocol fees or block rewards there will be no simple off-switch. Basically, we need to replace “push” transfers with “pull”.

1 Like

I think there is a distinct difference between transfering the funds to something like the clr.fund multi-sig and transferring funds to the matching pool (fundingRoundFactory), because no code exists at the fundingRoundFactory for the owner to exit with the funds directly.

This is the part that I disagree with, because:

  1. having additional code that allows people to withdraw from the matching pool increases the attack surface.
  2. many of the matching pool contributors will have no ability to exit with the funds they contributed.

Right, I agree. But there also would be no simple governance mechanism for those fees either.
And even if the only governance is that contributors can remove whatever they’ve deposited, it hasn’t really solved the problem for protocol fees and block rewards because, as you mentioned, there is no simple off-switch for those funding sources.

In reality though, all this is doing is kicking the can down the road. There is always going to be a “push” transfer, either to fundingRoundFactory or to the governed wallet that you’re suggesting.
Given that some funding sources will be unable to participate in this governance, it seems like inappropriate (and may introduce perverse incentives) to hand over governance to those contributors who can participate.

So what are you actually suggesting for governance of these pools then?
I think it’s really difficult to make a call on this without an idea of what the governance mechanism will be.

1 Like

I’m not suggesting anything, there’s no governance mechanism that fits everyone. The governance is to be decided by those who provide funding, that’s the whole point.

1 Like

Run me through how you imagine this pull mechanic working.

Say we’ve got one instance of clr.fund and three different pools, each with a different governance mechanism. How do funds get from these pools to the recipients?

1 Like

Each pool contract should implement a standard interface that enables interaction with clr.fund contracts.

Then clr.fund instance admins register these three pools as funding sources in FundingRoundFactory contract. When the funding round ends, someone calls transferMatchingFunds(), which iterates over the list of funding sources, calls each pool contract and transfers the allowed amount to funding round contract.


Let’s consider the simplest case, when there’s no governance mechanism at all. This means that the matching pool contract is immutable and transferMatchingFunds() simply transfers all available funds. This is equivalent to our current design where matching funds are locked in FundingRoundFactory. But the architecture is more flexible and enables customization.

The matching pool contract could be used for various purposes, not necessarily for governance. For example, rate limiting: someone puts 100K DAI into matching pool contract but allows clr.fund instance to spend only 10K DAI per month.

1 Like

Ok, I’m onboard with this architecture change. It’s a solid improvement.

How do you think this should be prioritized?

Important for R1 or something we should implement as an improvement post R1?

I’d lean towards after R1, just so we don’t slow ourselves down with scope creep. But I could be convinced otherwise.

I think it’s definitely not a top priority and could be implemented post R1 (once we collect enough feedback from the potential funding partners).

1 Like

When clrfund instance receives a lot of contributions it becomes very important to minimize the trust needed to operate it. But is it possible to get rid of the Owner role entirely? I would argue that yes, it is possible, though it involves some tradeoffs.

Here’s a list of functions in FundingRoundFactory contract which currently only owner can call and the plan of how we can move to a fully trustless and permissionless system:

  • constructor() - deployer selects MACI factory, verified user registry and in the future will also select a recipient registry. These variables could become immutable once the factory contract is deployed.
  • contributeMatchingFunds() - already permissionless. We can also give control over matching funds to a separate matching pool contract as I described in the first post of this thread.
  • addRecipient(), removeRecipient() - these methods will be moved to a separate recipient registry contract.
  • setMaciParameters() - the MACI parameters could be selected during deployment and become immutable after that (the Owner can also tranfer ownership to zero address after setting MACI parameters).
  • deployNewRound() - if there’s a predefined schedule of funding rounds, we can allow anyone to call this method. For example, a minimum interval between funding rounds can be set to 2 months during deployment.
  • transferMatchingFunds() - can be called by anyone when coordinator publishes the vote tally. We’ll add a challenge period so the finalization of funding round can be disputed in decentralized court. If the court rules against finalization, round gets cancelled and contributors withdraw their funds.
  • setToken() - the native token can be set during deployment and become immutable after that.
  • setCoordinator() - it’s hard to make this operation permissionless. Hopefully this role will become unnecessary in the future. In the absence of the Owner the control over selection of coordinator can be transferred to the same entity that governs matching pool (if this entity is capable of decision-making).

Yeah, this is how we approached it at the hackathon. Just about everything was a public function and the owner was only responsible for setting some variables.

Agreed, I think this would be a great outcome.
What do you see as the best avenues to removing the need for an owner?

I think the best way to remove the need for an owner here is to transfer the burden of coordinator selection to the funding source because they have an incentive to choose a honest coordinator. By funding source I mean a committee of sponsors or the community of governance token holders in DeFi protocol. Although in some cases (e.g. when funding comes from rollup fees) there could be no entity capable of making decisions.

I don’t know if there’s a way remove the need for coordinator.

It seems that what you’re really talking about is changing the owner to some other entity whose interests are perhaps better aligned. This need not require any changes to our current contracts.

Agreed, I think the coordinator is necessary for the time being.

No I’m not talking about changing owner. Although such comparison would be correct in one specific case where setCoordinator() works exactly as today and all other methods are permissionless, that still requires changes to our contracts. And there are other possible configurations.

This could also be accomplished with changes to the Owner’s contract, no?

i.e. you could make an owner contract that gives public/permissionless access to specific functions.

I think it’s possible to make this action permissionless by conducting an auction. The one who puts the biggest stake wins and becomes the coordinator. But this action should be disputable, similarly to transferMatchingFunds(), so if the winner is not meeting certain criteria, anyone can challenge the action in decentralized court and burn winner’s stake.

(I’m not sure it will actually work though)

Yes, it can be done. In that case it will act as a proxy contract that checks whether an action can be executed, and then routes the call to the FundingRoundFactory.

This approach is probably better because it allows customization. We can create a governance proxy contract for clrfund that makes everything permissionless but at the same time we allow other instances to choose centralized governance.

1 Like

This is an interesting concept. To work as intended, I think it would require that the coordinator’s stake be larger than the value of the matching pool + contributions (or at least larger than some percentage of the matching pool that that you’re unlikely to be able to claim without coordinator collusion). Anyone that can provide a proof that the coordinator cheated or was involved in collusion is able to claim the coordinator’s stake. So it’s always more profitable to cheat the coordinator than to cheat the round.

I had a brief chat with Vitalik the other day, he suggested that another option might be to run the coordinator in trusted hardware. Something similar to the magic.link’s delegated key management solution.

I don’t particularly like the idea of relying on AWS, but this might provide a lower cost way of creating a coordinator that cannot be compromised (assuming you trust AWS).

1 Like

After a conversation with Barry, I’d actually like to prioritize this.
Let’s work on specifying it next week.

1 Like