Special thanks to Auryn MacMillan and Kirill Goncharov for reviewing this post.

Originally posted on Max Goodman's Blog.

This post is Part 3 of a series entitled “clr.fund:Explained”.

Please see Part 1 and Part 2 for pre-requisite knowledge.

Disclaimer: This is a living process. For more up to date proof of concept information, please see the sequence diagram.


Introduction

In this post, I will explain how MACI fits into clr.fund's flow and what clr.fund's flow is. Let's get familiar with the key ingredients to our clr.fund's flow. According to our sequence diagram (see link or end of post), we have an Owner, Pool Contributor, Recipient, Funding Round Factory, MACI Factory, Contributor, Funding Round, MACI, and a Coordinator.


Defining the Contracts

Each contract is integral to clr.fund. The main contracts are as follows:

Funding Round Factory

The Funding Round Factory is the generic vehicle through which a funding round is started. It is used to register recipients, collect matching funds, deploy new rounds of funding, deploy MACI, transfer the matching funds, finalize the round (preventing additional funding), as well as potentially cancel the round.

MACI Factory

MACI Factory is a vehicle for creating replicable instances of MACI to be used for the funding round that is created. When Owner calls the deployMaci() function in the FundingRoundFactory, it creates a new MACI instance. After this instance is created, the FundingRoundFactory contract calls setMaci(). The MACIFactory is deployed first by the Owner before any other actions are taken.

Funding Round

The FundingRound contract is used for contributors to donate to the pool of funding (not the matching pool), to sign up users for voting, and finalize the round, as well as submit the vote tally and a proof (claimFunds()), and tells MACI to verify the proof given by claimFunds(), then transfers the funds to the Recipient.

MACI

MACI is in charge of the signing up of users/voters for a funding round, voting, processing messages/votes, tallying votes, and verifying the proof that is created by claimFunds() in FundingRound.


Defining the Key Roles

Each role is represented by a separate public key and does different actions. Here’s an overview:

The Owner

Initially, the Owner is the instantiator of clr.fund. The Owner deploys the MACI factory, deploys the Funding Round Factory, transfers ownership to Funding Round Factory, sets the MACI parameters (if necessary), and is to whom the Coordinator provides their public key. Once the Owner has the public key of the Coordinator, then they set the coordinator. The Owner can also set a new address as the Owner.

Pool Contributor

The Pool Contributor is someone, or something (app, protocol, etc), who donates to the matching pool of funding for allocation after voting takes place…

Recipient

The Recipient(s) are the ones who receive the funding once the voting is done. Adding a recipient can only be done by the Owner by calling addRecipient() on the FundingRoundFactory.

Coordinator

The Coordinator is in charge of providing their public key to the Owner for registration in the funding round as a Coordinator. They process the messages (votes) after the voting deadline has passed, tally the votes and prove the correctness of it and publish the results of vote tally after processing the messages/votes. They provide the Recipient with the voting tally and a proofs which can be used by the Recipient to claim their share of the funds each round.

Contributor

After the Owner deploys the FundingRound and calls setMaci(), the Contributor donates to the regular pool for the funding round by calling contribute() function on the FundingRound contract. After the contribution is made and the voting period has started, contributors create a message to vote and call by calling publishMessage(). Voting proceeds until the voting deadline.


Let’s visualize what’s happening step by step

Now that we know all the roles and contracts that get called, let’s visualize the whole process of setting up clr.fund and finalizing a funding round from start to finish.

Step 1-4

First, the Owner does 4 things: deploys the MACIFactory and FundingRoundFactory contracts. Second, they transfer ownership of the FundingRoundFactory contract to the MACIFactory. Lastly, if necessary, they set the MACI parameters, which include, among others, the signUpDuration and votingDuration . For more information on MACI parameters, see the clr.fund MACIFactory contract.

Step 5-6


After clr.fund’s basic setup, the Recipients ask the Owner to be added to the registration for projects eligible for receiving funds from clr.fund after a funding round has been finalized. This happens on a loop with as many projects as there are that get vetted and participate in the funding round.

Step 7-11


After adding all the Recipients, the Owner deploys a new funding round by calling deployNewRound() on FundingRoundFactory. This creates a new funding round for users to come and contribute funds to the funding pool for different projects. When the Owner calls deployMaci() on FundingRoundFactory contract, the FundingRoundFactory contract calls deployMaci() on MACIFactory, which creates a new instance of MACI for the funding round’s use. FundingRoundFactory calls setMaci() on the FundingRound contract to link the MACI instance to the funding round. This completes the setup of the funding round. Now, we need to enroll the Contributors in the funding round before the signUpDuration is over.

Step 12-15


In the proof of concept, only vetted Contributors are registered. The Contributors are vetted by clr.fund’s team. In later rounds, this curation process will be more permissionless. The Contributor can then donate to the funding pool for projects, at that moment, they signUp() to vote/send a message (an encrypted command). The Contributor creates a message and calls publishMessage(). This process happens with each Contributor until all Contributors are signed up and have published a message/voted. Once all the voting happens, then we hit the voting deadline.

Step 17-21


Once the voting deadline has passed, the Coordinator can process and tally the votes, as well as provide the vote tally to the Recipient. The Owner proceeds to call transferMatchingFunds() on the FundingRoundFactory contract, which then transfers funds and finalizes the round of funding. The Recipient submits the vote tally and a proof via the claimFunds() function on the FundingRound contract. The FundingRound contract passes the proof that the Recipient sent to MACI for verification. Once verified, the FundingRound contract transfers the funds to the Recipient. This completes the funding round.

Conclusion

MACI fits into clr.fund’s flow by providing the minimum architecture needed to prevent collusion; mainly, it provides a way for voters to hide their votes via encryption to prevent impact of bribery. By now, we know how all the different parts fits together in the 21 step process to complete a funding round. We know that there is an Owner who instantiates the entire process as well as registering vetted Contributors, a Coordinator who is in charge of processing and tallying the votes, Recipients who receive funding, PoolContributors who contribute to the matching pool for funding allocation, and Contributors who provide funds and votes for different Recipients/projects. This whole process is the entire process behind clr.fund’s proof of concept. I hope you enjoyed learning this as much as I did!
In case you want more information, I’ve done a ton of research for you to fall down different rabbit holes.


References/Further Readings:

BrightID. https://www.brightid.org/. Accessed: June 17th, 2020.
Buterin, Vitalik. "On Collusion". https://vitalik.ca/general/2019/04/03/collusion.html. April 3rd, 2019.
Buterin, Vitalik, Hitzig, Zoe, and Weyl, Glen. "Liberal Radicalism: A Flexible Design for Philanthropic Matching Funds". https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3243656. September 18th, 2018. Accessed: June 19th, 2020.
Clr.fund Data Flow Image.https://imgur.com/RAeSksA.jpeg.
"Collusion". Wikipedia. https://en.wikipedia.org/wiki/Collusion. Accessed: June 17th, 2020.
"edDSA" -- Wikipedia. Accessed: June 17th, 2020
"ECDH" -- Wikipedia. Accessed: june 17th, 2020.
Eason, Brian. "$120 million in requests and $40 million in the bank. How an obscure theory helped prioritize the Colorado budget." https://coloradosun.com/2019/05/28/quadratic-voting-colorado-house-budget/. May 28th, 2019. Accessed: June 17th, 2020.
FlatOutCrypto. "Crypto Intro: Sybil Attacks". https://flatoutcrypto.com/home/cryptointrosybilattack. Accessed: June 18th, 2020.
Kronovet, Daniel, Fischer, Aron, and du Rose, Jack. "Decentralized Capital Allocation via Budgeting Boxes". Paper. December 11th, 2018. Accessed: June 19th, 2020.
Lalley, Steven and Weyl, Glen. "Quadratic Voting: How Mechanism Design Can Radicalize Democracy". American Economic Association Papers and Proceedings, Vol. 1, No. 1, 2018. February 13th, 2012. https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2003531. Accessed: June 16th, 2020.
"MACI". https://github.com/appliedzkp/maci. Accessed: June 19th, 2020.
"maci/specs/". https://github.com/appliedzkp/maci/tree/master/specs. Accessed: June 19th, 2020.
"Protocol". Wikipedia. Accessed: June 17th, 2020.
"Quadratic Voting".Wikipedia. Accessed: June 17th, 2020.
Rogers, Adam. "Colorado Tried A New Way to Vote: Make People Pay -- Quadratically". Wired. https://www.wired.com/story/colorado-quadratic-voting-experiment/. Accessed: June 17th, 2020.
Rosic, Ameer. "What are zk-SNARKs?: The Comprehensive Spooky Moon Math Guide". https://blockgeeks.com/guides/what-is-zksnarks/. June 22nd, 2017. Accessed: June 22nd, 2020. "Sybil Attack". Wikipedia. Accessed: June 17th, 2020.
Weyl, Glen."Radical Markets". 2018. Amazon.
"What are zk-SNARKs?". https://z.cash/technology/zksnarks/. Accessed: June 17th, 2020.
Wei Jie Koh. "Minimum Anti-Collusion Infrastructure". YouTube. May 2020.

For more information on how clr.fund works, here is a technical SequenceDiagram.

Here is clr.fund’s code repo: clr.fund