GithubHelp home page GithubHelp logo

dcgtc / dgrants Goto Github PK

View Code? Open in Web Editor NEW
83.0 83.0 39.0 9.63 MB

Decentralized Grant platform that implements quadratic funding.

License: GNU Affero General Public License v3.0

JavaScript 1.60% Solidity 8.01% HTML 0.50% Vue 29.32% Shell 0.01% CSS 0.84% TypeScript 59.70% Dockerfile 0.03%

dgrants's People

Contributors

ajand avatar apbendi avatar arentweall avatar carletex avatar corydickson avatar frankchen07 avatar gdixon avatar hackmd-deploy avatar jferas avatar jjwoz avatar kammerdiener avatar mds1 avatar metafraction avatar owocki avatar phutchins avatar scco avatar strongcoder91 avatar thelostone-mc avatar wildmolasses avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dgrants's Issues

Support Bulk Grant Contributions in a Single Transaction

The current swapAndDonate method supports:

  • Donating an arbitrary token
  • To a single Grant
  • In multiple Rounds

We want to update this to support:

  • Donating an arbitrary token
  • To multiple Grants
  • In multiple denominations (e.g. 1 Grant receives 5 Dai and another 10 Dai)
  • In multiple Rounds

To do this efficiently, it probably makes sense to first execute a single swap to donationToken, then transfer donationTokens to N Grant payee addresses. The assumption here is that 1 swap and N transfers is going to be less expensive than N swaps.

The caller should provide a proportion for each grant receiving a donation. The proportions should be applied to the total donationToken received after the swap.

Single GrantsRound Contract Spec

This issue is a minimal spec for a smart contract implementing a single matching grant round.

  • Has an owner with special internal privileges (see below)
  • Has a startTime timestamp that must in the future at the time of deployment
  • Has an endTime timestamp that must be after the startTime
  • Has a donationToken address that is assumed to be an ERC20 implementation
  • Has a registry address that refers to a GrantsRegistry instance
  • Has a string metaPtr variable that is set at deployment
  • Has a minContribution variable that is set at deployment, representing the minimum matching contribution that can be made to a grant during the round
  • Has a hasPaidOut boolean variable that defaults to false, and toggles only when the owner calls the payout method (see below)
  • At any point before endTime, (including before the startTime), the Round can accept matching pool donations in donationToken via a special method. Internally, the method should use transferFrom to take custody of the funds and emit an event.
  • During the active period, the Round can accept matching donations for a specific Grant via a special method.
    • The method should take the grantId as a parameter, and verify that the Grant exists in the GrantRegistry
    • The method should ensure the amount is greater than or equal to the minContribution amount
    • The method should use transferFrom to send the funds the Grant's payee address
    • The method should emit an event
  • After the active period, the Round should disallow matching pool donations and grant contributions
  • After the active period, the owner should be able to transfer all matching pool funds to a payoutAddress via a special method call. Only the owner should be able to call this method, and only after the endTime has passed.

Open Questions:

  • Should matching donations be allowed while the round is active, or only before?
  • Should a minimum round duration be enforced in any way at deployment?

Notes:

  • For simplicity and further consideration, I've left any concept of whitelisting or curating grants from the round out of this spec. The only requirement is that the grant exist in the Registry.

Choose a license for the project

We need to choose a license for the project. We would like to allow usage of the project and codebase by others but require that changes be made available to the public so that everyone can benefit from building on top of our work. We would also like to make sure that we reserve the right to change or update the license.

Separate Donation Tokens Used In Round

Currently, a GrantRound has one donationToken, which is used for both the matching pool contributions and individual grant contributions. We want to separate these such that the entity who creates the GrantRound can specify a different token used for each case:

donationToken for individual contributions
matchingToken for the matching pool contributions

Both should be provided in the GrantRound constructor. The GrantRoundManager should continue to hold the donationToken fixed for all Grants deployed through it, but should allow the matchingToken to be passed as a parameter.

This PR is dependent on #61.

Discuss: How To Support Multiple Token Donations

The in-progress implementation of GrantRound defines a single token— selected by the Round owner at deployment— which is used for all contributions, including matching pool contributions, grant contributions, and the subsequent payout of matching contributions after the round is over.

For at least the case of grant contributions— and possibly for matching pool contributions as well— we want to support users being able to give in the token of their choice. There are at least two broad approaches for doing so.

One approach would be to allow the user to specify the token they're donating at the time of a contribution in the GrantRound itself, and accept the token donation regardless of this address.

Another approach would be to retain a single accounting token for each GrantRound, and instead swap the token for the accounting token before paying it to the Grant or adding it to the matching Pool. This could be done internally on the GrantRound, using an external wrapper contract, or using a more advanced approach like a delegatecall to a swapping strategy contract defined at deploy time (in order to allow alternative or upgradeable swapping strategies in future Rounds).

This issue is for discussing and weighing the tradeoffs involved in each approach before making a decision on how to implement it.

discuss: how round payout happens

Description

Right now on GrantRound once the round end there is a method payoutGrants which the GrantRound owner invokes to transfer funds to another address
https://github.com/dcgtc/dgrants/blob/main/contracts/contracts/GrantRound.sol#L103-L107

How does this flow work post that?

This is my understanding as it stands.

  1. Round Ends.
  2. Anyone can calculate distribution using the clr match algorithm (mostly done by the user who created the GrantRound )
  3. They deploy MerkleDistributor contract with :
    • merkle root : to make it easier for others to validate the distribution
    • actual distribution
  4. Once the contract is deployed -> there will be an action by approval_address (currently gitcoin but later on DAO ) to validate and approve the distribution
  5. The Round creator invokes funds payoutGrants and transfer funds to the approved MerkleDistributor contract
  6. once the funds are moved. we have to pick one of 2 options
    a) Grant owner claim funds from this contract (this will have to be tracked on who claimed and who didn't)
    b) the address that deployed the MerkleDistributor does a bulk transfer and transfer on the contract

Q:

  • Do we need the MerkleDistributor contract, can this not be stored on the GrantRound contract ? (Are we avoiding it cause it would be expensive ? -> we are already storing the list of grants)
  • In step 4 -> how do we decide what the approval_address is

Decide on a CSS approach/framework

Context from @phutchins in #2 (comment):

We may opt to replace Nightwind and Tailwind but we can leave them for now.

Our designer @scco is big on simple design without frameworks. If we decided not to use it, it may be more of a completely remove type of thing than a replace with something else so the effort might not be as bad as you think. Our direction right now is to build just enough UI to test/try the platform that we're building so we shouldn't spend time just yet worrying about making it pretty so if we were to wipe the ui in a few weeks and start over it shouldn't mater. I'm currently in between as I have not used Tailwind before and would like to give it a shot. I also would like for it to be relatively easy for people that are not our designer to make additions/changes so a framework might be useful. On the other hand they can make things more complex. I think we can decide this over the next week or two as @scco will be devoting more time to this project a few days from now.

Enable WETH & ETH Donations In Same Checkout

A limitation that results from this is you can't donate WETH and ETH in the same transaction, which I think is an edge case that's ok if we don't support. Wonder if Uniswap V3 router would let you multicall an ETH->DAI and WETH->DAI swap in the same transaction. My guess it supports that via multicall where you deposit ETH->WETH in the router as the first call, then execute the swap as the next call.

-- @mds1

Context

Discuss: Use a grant's `payee` address as it's ID

Problem

As described in #40, every contribution requires, at a minimum:

  1. A call to the GrantRegistry (2600 gas the first time, 100 gas subsequent times)
  2. A read of the payee address fo the specified grant ID (2100 gas each time)

This adds up quite a bit, and is very inefficient. To contribute to 100 grants at once, you're looking at (2600 + 100 * 99) + (2100 * 100) = 222,500 gas, or about $16 at current prices just to read all payee addresses. This is not counting token transfers, swaps, and other contract calls made as part of the donation transaction, and checkouts will likely become prohibitively expensive for many users.

I'd like to propose an alternate approach, which instead use the the payee address itself as the grant's ID. There's two initial problems with this idea:

  1. A user cannot use the same payee address for multiple grants
  2. A malicious user can create a dummy grant with your address as payee to block you from creating a grant

Solution

The solution proposed here is as follows:

  • When a user creates a new grant, deploy a contract wallet for them
  • Save the address of that contract as the grant's id
  • All contributions to that grant are sent to the contract wallet
  • The contract wallet has an owner, and only the owner of that contract can withdraw the funds (the owner can be anything, e.g. an EOA, a multisig, a timelock, a DAO, etc.)
  • When donating, we no longer need to verify the ID and read the payee address, which saves gas

This makes creating grants more expensive for grant owners, but makes contributions cheaper for donors

Implementation Ideas

Maker's DSProxy and Gnosis Safe have both been around for a long time and are sufficiently battle-tested that they can be used as a drop-in solution for the contract wallets we need to deploy. This gives us 3 options for using the grant's payee address as it's ID (using current gas prices for dollar amounts):

  1. Use Gnosis Safes, which deploy minimal proxies at a cost of ~280k gas ($20) when created with a single owner. The gnosis safe interface can be used by grant owners until we decide to create our own interface for grant owners (assuming we decided to do that for better, application specific UX). No audit needed here
  2. Use DSProxy and the DSProxy Factory, which by default deploys full contracts at a cost of `~600k gas ($42). There is no interface, so we'd have to build one upfront for grant owners to access their funds. No audit needed here
  3. Use DSProxy but modify the factory to deploy minimal proxies. I'd estimate this would cost 100k-200k gas (~$7-$14 to $200). There is no interface, so we'd have to build one upfront for grant owners to access their funds. Audit preferable since we'd be changing the contract code

We don't need to pick one of those 3 options in this issue—this issue is focused on whether or not we should make this change—and if so we can finalize implementation as part of a separate issue

Tradeoffs

Here are the pros/cons of the two approaches that I can currently think of:

Using an explicit ID and payee address as we currently do:

  • ✅ Cheaper to create new grants (on a one-time basis, per grant)
  • ❌ Makes contributions more expensive (on a recurring basis)
  • ✅ Familiar interface to access your grants funds (i.e. however you normally access wallet funds is how you access received donations)

Using a contract wallet as the grantId, where the wallet's owner can be whatever the user wants:

  • ❌ Grants are more expensive to create (rough estimates on cost above)
  • ✅ Contributions are cheaper (by ~2225 gas per contribution if everyone donates to 100 grants at once, more info below)
  • ❌ New interface needed to access your grant's funds (new for the user)

"Worst case" estimated cost savings of contract wallet:

  • Assume everyone donates to 100 grants at a time. This means current approach adds an average of 222,5000 / 100 = 2,225 gas per contribution
  • Assume worst case contract wallet of DSProxy costs 600k gas to create
  • This means that in terms of total gas spent on grants, contract wallets as IDs become cheaper after 600,000 / 2,225 = 270 contributions

Even though the proposed idea has two X's and the current approach has 1, I think that:

  1. A new interface for grant owners to get their funds is ok, as they are incentivized to figure it out and redeem their funds anyway
  2. The costs saved by users will outweigh the increased grant creation cost, resulting in a net cost savings in the long run.
  3. A goal of Grants IMO is maximum inclusivity of who can participate, which means making it as accessible as possible, and since there will be more contributors than grant owners, minimizing costs for contributors at the expense of grant owners seems like a reasonable tradeoff, especially since contract wallet deployment costs are not unreasonable

Add MockERC20 contract to improve testing

Currently #17, uses Waffle Mock ERC20 framework to set return values for IERC20 functions called in the GrantRound contract. Instead we'd like to introspect into the state of token transfers. This is to ensure that the methods implemented successfully send the tokens and balances are updated correctly; along with handling common revert edge cases.

Fix type checks so build succeeds

Running yarn build from the app folder fails with various TS errors.

  • These TS errors don't show up when building for dev mode, i.e. yarn dev
  • These TS errors don't show up when running yarn lint
    • These TS errors don't show up in the VSCode UI
  • Building directly with yarn vite build succeeds
  • The errors are thrown from the vue-tsc --noEmit check in yarn build

@wildmolasses Curious to hear your inputs on the best way to handle this so those TS errors surface sooner, whether that's in the VSCode UI, the dev console, or during linting

spec: dcurve

Description

The clr calculation would be done offline at the end of the round.
To allow dApp to calculate this. ducurve is being built as node package and will exist as a subdirectory.
It will expose functions that will be invoked by dApp during / the end of the round. and the response will either return a single GrantsDistribution or an array of GrantsDistribution

When will this package be used

  • during a round to show users

    • what the current distribution is
    • how the distribution would vary
  • at the end of the round

    • to generate the final distribution
    • to validate the distribution which has been proposed for payout

The package allows for flexibility

  • to select the hashing algorithm to be applied on the distribution curve
  • to select the algorithm used to determine the pairwise match (currently we will support linear)
  • to pass in your filtered contributions set (as opposed to letting the package fetch the data). This is useful when folks would like to perform sybil analysis and exclude certain contributions
Folder structure

All code is maintained within the dcurve repo (this will change as we'll have to account for diff hash / clr algo ).
Will update this structure

/src
   /internal/fetch.ts      # exports functions to fetch information about GrantRound and Contributions
   /internal/main.ts       # exports the core logic (will be supplied with a calc & hash command on construct)
   /internal/calc/*        # exports command pattern implementations of the clr algorithms
   /internal/hash/*        # exports command pattern implementations of the hashing functions
   /types.ts               # exports the types used in the project
   /index.ts               # exports the modules outlined above
/tests                     # tests are written here
/dist                      # packaged src which will be published to npm

You can check out the structure here: https://github.com/dcgtc/dgrants/tree/dcurve/dcurve

Functions exposed by the module to the dApp

These functions would live inside /src/internal/main.ts.

  • calculateCurrentDistribution - calculates the distribution + hash based on contributions

  • calculatePredictedDistribution - how the curve would vary if a grant were to get a contribution worth X tokens.

  • calculatePredictedDistributions - same as calculatePredictedDistribution but returns an array of GrantsDistribution as if you have 5 grants -> generates 5 distribution where each distribution one grant gets a contribution worth X tokens.
    This would be a slightly expensive operation and would increase in time as number of grants increase. Useful to show data on the explorer page.

What will the response structure look like

The response of any of these methods will be a distribution.

The actual struct can be found here
https://github.com/dcgtc/dgrants/blob/dcurve/dcurve/src/types.ts#L38

Things to account/ call out (for v2)

  • Will have to allow users to pass a list of addresses to ignore (optional)
  • how to store the distribution curve as opposed to calculating on demand
  • ability to pass in multiple GrantRound and generate total distribution match. (useful when 1 entity is running multiple rounds and wants to show cumulative )

Extend GrantRoundManager from Uniswap v3's SwapRouter

Making the GrantRoundManager inherit from SwapRouter (#74) would remove approvals and transfers that are currently needed.

Inheriting from SwapRouter means we'd become a custom v3 router, and users would have to trust (or verify by inspected verified source code) that we did not modify the router functionality in some malicious or unsafe way.

dcurve: integrate trust bonus score into linear QF

Description

While running a dGrants round.
The dcurve package should be able to fetch the trust bonus score of the contributor's address ( hosted on gitcoin ) while determining the distribution. Until DID are generated and are issues NFT/stored on-chain, a temporary solution here would be integrating an API

  • introduce a new endpoint on gitcoin to fetch the trust bonus score gitcoinco/web#9349
  • identify wherein linear.ts would we'd add the trust bonus score
  • wire in the API call and make the changes
  • @frankchen07 to validate algo with big dataset

Grant Registry UI Page Prototyping

This issue defines the routes and baseline functionality for the prototype UI's interaction with the GrantRegistry contract implmented in #7

  • Registry List Page
    • route: /dgrants
    • Shows a list of every grant in the registry
    • For each grant, displays: grant id, owner address, payee address, metaPtr bytes
    • Each Grant listed can be clicked to go to the Grant Detail age
    • Has a "Create Grant" button that goes to the New Grant page
  • Grant Detail Page
    • route:/dgrants/:id where :id is the integer ID of the Grant in the GrantRegistry
    • Shows each field for the grant: id, owner address, payee address, metaPtr bytes
    • If the user's connected wallet address is the owner address for this grant, it should have an "Edit Grant" button. Clicking the button should make the various fields for the grant editable. Clicking "Save" should submit the appropriate transaction iff the fields have changed.
  • New Grant Page
    • route: /dgrants/new
    • Should have fields for each (configurable) Grant field: owner address, payee address, metaPtr bytes
    • Should do basic validation on the fields (all 3 are required; addresses should be valid addresses)
    • Should have a "Create" button that is clickable when the fields are populated properly and submits the appropriate on-chain transaction
    • Should redirect the user to the Grant Detail Page of the newly created Grant when the tx is mined

Note: in the long term we will (obviously) not show the metaPtr bytes, but rather load, parse, and display the metadata from the storage location. This is a temporary requirement to demonstrate interaction with the Registry is working properly.

Add functionality to cart checkout button

Picks up where #94 left off, and is dependent on a PR for that being merged

  • The cart page should allow the user to "check out" and contribute to grants
    • It should collect the grant Ids, tokens, and amounts, and submit them via the bulk contract checkout method; dependent on #76
    • It should only submit the grant for matching to a single, pre-configured grant round
    • After checkout, the user should be shown a "Thank you for contributing" message and redirected to the Round page; the user's cart should be empty after checkout

Discuss: Use a Router Pattern For Grant Contributions

Currently, the GrantRound contract is architected such that contributions are made to a Grant through the Round. An alternative, and perhaps superior approach, would be to have users make contributions through a ContributionRouter contract.

Such a Router would:

  • Allow the user to make a contribution directly to the Grant if a Round is not specified
  • Allow the user to make a contribution through a Round by specifying a Round address
    • Questions on this point
      • Should the Router then call through to the Round contract to do the transfer?
      • OR should the Router simply verify that the Round is active, and that the donation token is correct, then do the transfer itself and emit an event with the Round contract address included and indexed?

Advantages to this approach include:

  • Enabling trackable/indexed contributions outside the context of a active Round
  • Reducing the number of times a user needs to approve a contract that can do the appropriate transferFrom

Possible downsides:

  • Depending on how it's architected, it might require each Round to whitelist a Router (or Routers?) that is approved to process contributions

Other questions:

  • Should this routing functionality actually be built into the GrantRegistry directly, as opposed to in a separate Router contract? What are the tradeoffs for each approach?
  • Should matching contributions also be sent through a Router?
  • If we wanted to implement a feature that allowed users to donate in any Token, then swapped into the Token required by the GrantRound before making the contribution, how would the Router approach impact this? Could/should swapping be implemented in the Router, or in another contract wrapping the Router?
  • Would the Router also be the right place to enable bulk contributions?

GrantRound UI Spec

  • Round Detail Page
    • route: /dgrants/rounds/:address where :address is the hex address of a GrantRound contract instance
    • Displays the following information:
      • Round status: Upcoming, Active, Ended (better copy here?)
      • Appropriate time relation to status: "Starts in _____", "Ends in ______", "Ended ______ ago"
      • Hovering over the time description should show a discrete, human readable timestamp for the appropriate time
      • Funding Token (Show name and/or symbol; clicking should take to Etherscan token site?)
      • Matching Pool Balance OR the amount of the matching pool that was paid out, if it was
      • Owner address (with Etherscan link?)
    • Matching pool contribution form: iff the Round is Upcoming or Active, show a form allowing the user to make a matching pool contribution; specify an amount, then sign/submit the token approval and addMatchingFunds method call txs
    • Payout contract destination: iff the Round hasPaidOut, show the contract address (and transaction hash?) where the payout was made to. Link to Etherescan.
  • Round List Page: dependent on #19
    • route: /dgrants/rounds/factory/:address where :address is the hex address of a GrantRoundFactory contract instance — route: /dgrants/rounds (updated with assumption that the Factory is a singleton)
    • Show a list of GrantRound instances deployed by this factory, identified by address (in the future, this will be the Round title as fetched from MetaDat)
    • Show three discrete sections: Active, Upcoming, Ended
    • Sort Grants in each section by: Active: End Date, soonest first; Upcoming: Start Date, soonest first; Ended: End Date, recently ended first
    • Clicking each GrantRound should take the user to the appropriate Grant Detail Page

Additional notes/questions:

  • A list of Grants included in this Round is not yet included, because as currently conceived, the list of canonical grants is stored in the metadata
  • Should we show a list of matching pool contributions? If so, should we have an event emitted from the addMatchingFunds method? Should we have a minimum matching pool contribution threshold?

Add validation logic to `GrantRound` constructor arguments

The following should be verified in the constructor. Recommend waiting for #21 to be completed first since there will likely be some merge conflicts, as the tests should be probably refactored here to use Waffle fixtures to simplify snapshots/reverts for going forwards/backwards in time

require(_donationToken.totalSupply() > 0, "GrantRound: Invalid token");
require(_startTime >= block.timestamp, "GrantRound: Start time has already passed");
require(_endTime > _startTime, "GrantRound: End time must be after start time");
require(_registry.grantCount() >= 0, "GrantRound: Invalid registry"); // verify this call doesn't revert

Add code coverage for `contract` directory to the CI

Part of this issue should be investigating the best way to do this, such as:

  • Can we check that coverage on a PR hasn't decreased from the coverage on main?
  • If not, can we check that coverage is above a certain threshold?

Context: #75

IPFS Grant Metadata Storage Proof-of-Concept

This issue is a spec for a proof of concept/prototype implementation of storing Metadata on IPFS via Fleek. It's meant to demonstrate the end-to-end functionality, without including all details, such as the full Grant Schema, the ability to edit, or viewing past edits of the metadata. All such functionality can be added in future tasks once this proof of concept is built out.

Create Grant Flow

  • Update the /dgrants/new form to remove the Metadata URL field, and replace it with two new fields: Name & Description
  • When the user submits the form via "Create Grant":
    • Add the name & description to a JSON object with corresponding keys, encode this JSON as a file, and add it to IPFS via Fleek (more below)
    • Publish the IPFS CID generated above as the metadata ptr property when creating the GrantRound via transaction
      • Question: should we publish only the CID, and let the frontend presume this is an IPFS hash and fetch it from whichever gateway it chooses, or should we publish a full URL, specifically the URL to this IPFS data via a particular IPFS gateway that we choose. In other words, if the content hash is QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy, should the metaPtr be simply "QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy" or should it be something like "https://cloudflare-ipfs.com/ipfs/QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy"
      • Answer: see here

Grant Display

Update various places where Grant Metadata is displayed to actually load the data and show, specifically:

  • Update /dgrants to show the Grant Name & (possibly shortened) Description in each Grant Card; remove the Grant ID and mtaptr URL
  • Update /dgrants/:id to show Grant Name & Description, remove metadata URL and Grant ID

Sample JSON Object (partial schema for this initial implementation)

{
    "name": "My Grant Name",
    "description": "This is my Grant. There are many like it, but this one is mine."
}

Fleek API

  • Use the ipfs-http-client package
  • Use the Fleek endpoint with the api key for authentication as such:
import { create } from 'ipfs-http-client'

const ipfs = create({
  url: "https://ipfs-api.dev.fleek.cool",
  headers: {
    Authorization: "v2 <apiKey>", // or Bearer <JWT> or public <AppKey>
  },
});
  • Use ipfs.add to add and pin the data, and get back the new CID
  • Use ipfs.get to fetch the content hash

Add to grant bootstrap

  • make sure that when grants are created locally that their content is properly linked

Add a style guide

We could use a style guide to give clear direction on what we expect in our codebase. I imagine that this could be pulled from the Gitcoin style guide.

Implement Cart Checkout (spec)

Adding To Cart

  • When on the grant detail view, there should be an "Add to Cart" button
  • When viewing a list of grants, there should an Cart icon for add to cart
  • When either aforementioned button is clicked, the following data should be added to local storage:
    • The grantId
    • The donation token address (default to Dai)
    • The donation token amount (default to 5 in raw decimal format)
  • There should be utility methods that abstract writing/reading cart data from local storage
  • The data should be returned in the following format:

Sample Schema

[
    {
        grantId: 5,
        contributionTokenAddress: "0x6b175474e89094c44da98b954eedeac495271d0f",
        contributionAmount: "5000000000000000000",
    },
    {
        grantId: 7,
        contributionTokenAddress: "0xde30da39c46104798bb5aa3fe8b9e0e1f348163f",
        contributionAmount: "1337000000000000000",
    },
]

Cart Page

  • The cart page should list the grants the user has added
    • It should load metadata for the grant via IPFS; dependent on #86
    • It should display the token and the human readable amount
  • The cart page should show a summarizations of how much of each token is being donated in total (e.g. "Donating 25 Dai + .4 ETH")
  • The cart page should allow the user to update their token and amount individually for each row
    • The UI should list the following tokens in the UI (for now): DAI, ETH, USDC, GTC, UNI
    • The list of supported tokens should use the token list format, and should be generalized to work with tokens of any name/decimals/symbols etc....
  • The cart page should allow the user to remove items from their cart
  • The cart page should allow users to clear their cart

Additional Notes

  • The implementation should tend toward @scco's figma designs, but do not need to match it precisely for now; more precise styling will be handled in a separate task
  • We can also leave matching estimates and USD conversions out of the initial implementation and address them as separate features

Discuss: How should dGrants handle obtaining and utilizing sybil scores?

The dGrants system needs to a way to obtain sybil scores (a score based on risk for a donor with the intent of verifying that they are a unique individual) and utilize them when calculating quadratic matching for Grant Round payouts.

Separately we are driving an initiative to standardize and decentralize sybil score aggregation and exposure.

Grants Round 11 Scope
Gitcoin Holdings currently is an aggregator of sybil risk indicators and captures that data in a closed database. An initiative exists to export the sybil score aggregated by Gitcoin as a DID (Decentralized ID). In the short term (by Grants Round 11) the best approach may be to utilize this DID which would be attached to a users ETH account which they used to make their donation. This would make it such that a user could make a donation then subsequently verify their identity through the Gitcoin trust bonus process as long as they did this prior to the payout calculation event.

I imagine the blob of data that we would consume from this DID could look something like the following:

  • Confidence Factor: 0-100 (100 being ultimate confidence)
  • Unique Identifier: Should be one per unique human
  • Signature(s): Making it possible to verify validity of the DID and Sybil Score data
  • Time validity: Period of time that the Confidence Factor is assumed good for

Lets discuss what this could look like and what we need in order a solution like this to work.

grantsCLR - subgraph 101

This was the subhraph i was meesing around with for the GrantRound and my assumption was these fields would be available on the contract.

enum GrantRoundState (
  Setup "This is default state"
  Active "This is when it's got all the funds and has started"
  Pending "This is pending payment state"
  Closed "Round is done"
)

type GrantRound @entity {
  id: ID! "hash"
  owner: Bytes!
  name: String!
  state: GrantRoundState!
  amount: BigInt!
  metadata: ID!
  grants: [Grant!]! "do we store list of grant ID or pointer here"
  startDate: String!
  endDate: String!
}

spec: GrantRoundPayout contract

Description

Once a GrantRound has ended, the payoutAdmin would invoke payoutGrants to transfer funds into the GrantRoundPayout contract.

What is this contract do

Global variables

  • mapping(address => uint256) public payouts; stores the address to amount

Functions

  • invoke function setPayout to be able to upload the distribution of type PayoutFields (this function should be callable multiple times if incase all payouts can't be set in 1 transaction )
  • invoke function payoutGrants which
    • checks to ensure total amount in payouts =< amount in contract
    • loop through the grants and do the actual transfer
mapping(address => uint256) public payouts;

struct PayoutFields {
  address recipient; // grant receiving address
  uint256 amount; // match amount for that grant
}

Separate GrantRound Privileges

Currently, in the GrantRound contract, there is one privileged role, called owner, that has one specific right: calling payoutGrants to move matching funds to the payout address. The new system should have:

  • Two specific roles, defined via constructor arguments
  • The payoutApprover can call payoutGrants
  • The metaUpdater can call a new, setMetaPtr method, which updated the metaPtr method

Note: Open to new naming schemes.

WIP: Grant Metadata Fleek Prototyping Spec

This issue is a spec for a proof of concept/prototype implementation of storing Metadata on IPFS via IPNS via the Fleek API. It's meant to demonstrate the end-to-end functionality, without including all details, such as the full Grant Schema or viewing past edits of the metadata.

Create Grant Flow

  • Update the /dgrants/new form to remove the Metadata URL field, and replace it with two new fields: Name & Description
  • When the user submits the form via "Create Grant":
    • Generate a new IPNS key via the /key/gen Fleek endpoint (TBD: What should the name be?)
    • Publish the name & description to IPFS via the appropriate Fleek IPFS endpoint using the schema below
    • Publish the IPFS CID generated above to the IPNS key using the /name/publish
  • After the Fleek API responds, use the response to store the appropriate identifier as the metadata ptr on the Grant that was created (TBD does the grant need to be created first, in order to get its identifier to use as the name, this would require a second tx to update the metaptr once IPNS was configured).

Grant Display

  • Update /dgrants to show the Grant Name & (possibly shortened) Description in each Grant Card; remove the Grant ID and mtaptr URL
  • Update /dgrants/:id to show Grant Name & Description, remove metadata URL and Grant ID
{
    "name": "My Grant Name",
    "description": "This is my Grant. There are many like it, but this one is mine."
}

Open Questions:

  • What should the IPNS name be for a give grant?
  • What is the "Arg" in the Publish IPNS key?

Rename Infura env variable for clarity

In our .env files, the VITA_INFURA_API_KEY variable could be renamed to VITA_INFURA_PROJECT_ID (or VITA_INFURA_ID) to match the way that the value is named on Infura. For consistency, the INFURA_ID variable could be renamed to INFURA_PROJECT_ID (or left alone if we use VITA_INFURA_ID).

Investigate issue from onboard.js Gnosis module

Stack trace in console below. I have not yet tried connecting a Gnosis Safe so unsure if the module works:

Uncaught (in promise) TypeError: _context.t2 is not a constructor
    at _callee$ (gnosis-19b90e57.js:39)
    at tryCatch (runtime.js:63)
    at Generator.invoke [as _invoke] (runtime.js:293)
    at Generator.next (runtime.js:118)
    at asyncGeneratorStep (gnosis-19b90e57.js:7)
    at _next (gnosis-19b90e57.js:9)
_callee$ @ gnosis-19b90e57.js:39
tryCatch @ runtime.js:63
invoke @ runtime.js:293
(anonymous) @ runtime.js:118
asyncGeneratorStep @ gnosis-19b90e57.js:7
_next @ gnosis-19b90e57.js:9
index.ts:213 Uncaught (in promise) Error: call revert exception (method="getAllGrants()", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.4.0)

Decide which version of JavaScript to support

Currently configured to ES2018, but would be nice to move to ES2020 for bigint support. Need to research browser support for ES2020 and decide if it's sufficient before going this route.

Grant Round Manager Spec

  • Rename the GrantRoundFactory to GrantRoundManager
  • Add an immutable variable donationToken to the Manager contract that is defined at deployment
  • Remove _donationToken from createGrantRound, instead using the immutable donationToken when creating a Round
  • Add an immutable variable registry, an instance of GrantRegistry to the Manager contract that is defined at deployment
  • Add an immutable variable router, an instance of the Uniswap v3 Router contract that is defined at deployment
  • Define a new Donation struct as follows:
// Define inputs required for each contribution in a struct
struct Donation {
  uint96 grantId; // grantId
  uint256 amount; // amount of that token
  address[] swapPath; // Uniswap router swap path
  address[] rounds; // rounds they want the contribution to count for
}
  • Create a new method, swapAndDonate, which:
    • Takes a a Donation struct as a parameter
    • Verifies the grantId is valid
    • Iterates over each round, validating it:
      • Is an active round (between start and end time)
      • Uses the donationToken defined by this Manager
    • IF swapPath[0] defined in Donation is the same as the Manager's donationToken, uses transferFrom to move the token to the Grant's payee address
    • Otherwise:
      • Use transferFrom to move amount of the token swapPath[0] to the Manager
      • Use router and swapPath to swap swapPath[0] for donationToken
      • Use transfer to send the donationToken.balanceOf(this) to the Grant's payee address
    • In both cases: emit an event with the grantId, swapPath[0] token, amount, and rounds included
  • Remove donateToGrant from the GrantRound contract

Allow Arbitrary Input Tokens for Swap and Donate

Current implementation requires an existing pool for the pair. Determine how to do arbitrary "paths" between tokens for Uniswap v3. Implement this in current contracts, and/or alongside the bulk checkout functionality.

does `yarn dev` expose a RPC url?

does yarn dev expose a RPC url?

if so what is it?

looks like all dgrants are posted to a local RPC, but i dont see an address in the terminal when i run yarn app dev, and i dont see in the docs how to run this

Local testing on Avalanche

We would like to test dGrants on Avalanche and then contribute to the codebase as we test and find any issues.

Currently the dApp relies on Infura which only serves Ethereum.

We would like to add support for Avalanche so that our developers can test locally.

What would be the best approach to do this? I looked for contribution guideline, but couldn't find anything. Should we fork the code and then build those changes in a new repo or should we add multiple L1 support into this repo and submit PR's?

Get Tests Running In CI

Set up GitHub Actions for CI. The full monorepo test suite should run. Preferably, the tests would run when a PR was opened and/or udpated.

Implement Grants Round Factory

This issue is dependent on #17

We will use the factory pattern to allow users to create new GrantRound instance. The factory should:

  • Take all the parameters needed to initialize a new GrantRound and deploy one via a public method call
  • Emit an event with the parameters when it does so
  • Remove existing GrantRounds contract and tests

Questions:

  • Should the factory method or the GrantRound constructor do any sanity checking of the address provided as the ERC20 token? For example, calling the symbol method, which would cause revert if the address passed was not an ERC20 implementation.

discuss: script calculating clr matching curve

What

Once a GrantRound is complete, we calculate the distraction using the CLR pairwise matching algorithm

This lists out what each grant would get based on the

  • who made the contribution
  • amount of contribution
  • and a few other factors (like trust bonus)

This calculation is also done during the round to let folks know

  • what the current match is
  • how much the amount would increase by if the user were to add a few tokens
V0 (in the next 1-2 weeks)
  • 1:1 match algorithm and not even do pairwise (DO WE WANT TO DO THIS )
V1 (before round 11)

Personally, I'm not convinced that CLR calculation should be done onchain -> cause it's expensive.
But irrespective of which route we take we'd want a script that would for V1

  • consume -> GrantRound txn hash
  • consume DonationSent event. Check here
  • consume a list of addresses to ignore contributions
  • calculate the
    • current match for every grant taking part in the round
    • match if 1 DAI was contributed
    • matching if 100 DAI was contributed

At the end of it , the dApp would have function which it would invoke to calculate this curve which returns the

  • distribution curve
  • current CLR matching
  • hash of the current CLR matching
V2
  • can take it other parameters to influence contribution match like trust bonus score
Related

Once this script is ready, the next steps would be deciding where to store this hash and the actual distribution curve once a round ends

cc @gdixon

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.