Giter VIP home page Giter VIP logo

matching_contracts's Introduction

Gitcoin Matching Contracts

Non-custodial match payouts for Gitcoin Grants.

About

Architecture

This contract allows for non-custodial Gitcoin Grants match payouts. It works as follows:

  1. During a matching round, deploy a new instance of this contract
  2. Once the round is complete, Gitcoin computes the final match amounts earned by each grant
  3. Over the course of multiple transactions, the contract owner will set the payout mapping stored in the payouts variable. This maps each grant receiving address to the match amount owed, in DAI
  4. Once this mapping has been set for each grant, the contract owner calls finalize(). This sets finalized to true, and at this point the payout mapping can no longer be updated.
  5. Funders review the payout mapping, and if they approve they transfer their funds to this contract. This can be done with an ordinary transfer to this contract address.
  6. Once all funds have been transferred, the contract owner calls enablePayouts which lets grant owners withdraw their match payments
  7. Grant owners can now call claimMatchPayout() to have their match payout sent to their address. Anyone can call this method on behalf of a grant owner, which is useful if your Gitcoin grants address cannot call contract methods.

This contract is deployed on mainnet and Rinkeby at 0xf2354570bE2fB420832Fb7Ff6ff0AE0dF80CF2c6

For round 9, the contract has been deployed on mainnet at 0x3342e3737732d879743f2682a3953a730ae4f47c .

Contract Design and Security

When designing and developing this contract, security was the number one goal. This led to keeping things as simple as possible in many places, and as a result some aspects of the design may seem inefficient or suboptimal. This section will explain those design decisions. To start, let's review the contract flow over its lifecycle as shown below:

image

Now lets review a few of the specific design decisions in this context.

The payout mapping: Match amounts are saved in a mapping called payouts, and because there are about 1000 grants that receive match payouts, it takes multiple transactions and a lot of gas to set this mapping. Using a Merkle distributor may feel like the cleaner way to do it, but we intentionally decided against that here. One reason is because it's more complex, and since this contract was not formally audited we wanted to keep it simple. Another reason is because if we set the payout mapping wrong, we can easily override it with additional calls to setPayouts without having to generate a new merkle root.

Funding: All funds to be paid out are expected to come from the Gitcoin Grants multisig. We take advantage of the fact that we trust this funder to keep things simple. The contract is funded with an ordinary transfer of DAI to the MatchPayouts contract. If the funder makes a mistake during this transfer, they have the ability to withdraw funds using the withdrawFunding method, which lets only the funder withdraw any tokens from the contract. Notice how there are no restrictions on when or what token the funder can withdraw—they can withdraw any amount of any token at any time! In an adversarial environment, this would be a problem. But because we trust the funder, we enable this functionality as a safeguard so funds can be withdrawn at any time in case something goes wrong.

Claiming Funds: Some grants use contract wallets and it may not be easy for them to call a method allowing them to claim funds. As a result, the claimMatchPayout method allows anyone to withdraw on behalf of a grant, and transfers the funds to that grant's receiving address. Additionally, because there is no msg.sender usage, and because all match payouts are DAI, there is no reentrancy risk to worry about here.

Development

Create a copy of .env.example and fill in the environment variables, the proceed to the following sections. If you don't plan on deploying the contracts, you should be able to leave these with the defaults.

Contract Setup

# Install dependencies
$ yarn install

# Compile the smart contracts with Hardhat
$ yarn compile

# Generate TypeChain artifacts
$ yarn typechain

# Lint the Solidity code
$ yarn lint:sol

# Lint the TypeScript code
$ yarn lint:ts

# Run tests
$ yarn test

# Generate the code coverage report
$ yarn coverage

# Delete the smart contract artifacts, the coverage reports and the Hardhat cache
$ yarn clean

Python Setup

For the following section you'll need python. To get setup, follow the commands below:

# Create a new virtual environment in this directory
python3 -m venv venv

# Activate the virtual environment
source ./venv/bin/activate

# Install dependencies
pip install -r requirements.txt

Test Workflow

This section explains how to deploy a local instance of the contract and run Python scripts to check the contract state. The final versions of this script live in the gitcoin/web repo, but were developed here.

First let's deploy the contracts locally.

$ yarn deploy:local

Wait a few seconds for that to complete.

Now we want to set a payout mapping and verify the results before finalizing it. We can do this with the below command, which will set the payout mapping and save the mapping to outputs/payouts.json so we can verify it later

$ yarn sim:set-payouts

Let's compare the total value of the payouts mapping from the events to what we'd expect from payouts.json, and let's compare that to the DAI balance of the contract.

# Run the python script
yarn sim:verify-payouts

As expected, the contract does not have enough DAI to cover all match payouts.

Run yarn sim:fund to simulate the funder adding DAI to the contract. Now run yarn sim:verify-payouts again and it will show the contract has sufficient funds! At this point, the owner can call enablePayouts to let grant owners withdraw their match amounts.

Acknowledgements

This project was built with Paul Berg's solidity-template.

matching_contracts's People

Contributors

mds1 avatar owocki avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.