diff --git a/README.md b/README.md index 0ee3e2c32..84622234f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The clr.fund smart contracts consist of a factory contract that deploys a new co 3. **Contributor:** Any address that contributes tokens to the funding round. 4. **Recipient:** Any address that is registered as funding recipient. -The clr.fund application can use any [EVM-compatible chain](https://ethereum.org/) as a backend. The application can be hosted on [IPFS](https://ipfs.io/) and can also run locally. +The clr.fund application can use any [EVM-compatible chain](https://ethereum.org/) as a backend. The application can be hosted on [IPFS](https://ipfs.tech/) and can also run locally. For more details, see the [sequence diagram](docs/clrfund.svg) and [clr.fund constitution](https://github.com/clrfund/constitution). diff --git a/contracts/.env.example b/contracts/.env.example index cd34f8dd9..629924f37 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -45,3 +45,7 @@ ETHERSCAN_API_KEY= # ZK proof circuit type, based on MACI v0.10.1 circuit type as defined in utils/deployment.ts # e.g. test, small, medium, x32, prod CIRCUIT_TYPE=prod + + +# The IPFS gateway url used by the prepare-results.ts script +IPFS_GATEWAY_URL= \ No newline at end of file diff --git a/contracts/scripts/prepare-results.ts b/contracts/scripts/prepare-results.ts index 65eb25202..e979d2f27 100644 --- a/contracts/scripts/prepare-results.ts +++ b/contracts/scripts/prepare-results.ts @@ -2,8 +2,9 @@ import fetch from 'node-fetch' import { ethers } from 'hardhat' import { Event } from 'ethers' import { gtcrDecode } from '@kleros/gtcr-encoder' +import { DEFAULT_IPFS_GATEWAY } from '../utils/constants' -const ipfsGatewayUrl = 'https://ipfs.io/' +const ipfsGatewayUrl = process.env.IPFS_GATEWAY_URL || DEFAULT_IPFS_GATEWAY interface Project { id: string diff --git a/contracts/tasks/auditTally.ts b/contracts/tasks/auditTally.ts index 619b5281d..03c7c294b 100644 --- a/contracts/tasks/auditTally.ts +++ b/contracts/tasks/auditTally.ts @@ -1,6 +1,14 @@ +/** + * This script is used for auditing the tally result for a round + * + * Sample usage: + * yarn hardhat audit-tally --round-address 0x4a2d90844eb9c815ef10db0371726f0ceb2848b0 --network arbitrum --output ./ethcolombia.json + */ + import { task, types } from 'hardhat/config' import { utils, providers, Contract, BigNumber } from 'ethers' import { EventFilter, Log } from '@ethersproject/abstract-provider' +import { Ipfs } from '../utils/ipfs' import fs from 'fs' interface Project { @@ -21,12 +29,6 @@ function isRemoval(state: number): boolean { return state === 1 } -async function fetchTally(tallyHash: string): Promise { - const url = `https://ipfs.io/ipfs/${tallyHash}` - const result = utils.fetchJson(url) - return result -} - async function fetchLogs({ provider, filter, @@ -152,12 +154,10 @@ function tsvStringify(projects: Project[]): string { return outputString } -/** - * Audit the tally result for a round - */ task('audit-tally', 'Audit the tally result for a round') .addParam('roundAddress', 'Funding round contract address') .addParam('output', 'Output file path') + .addOptionalParam('ipfs', 'The IPFS gateway url') .addOptionalParam( 'tsv', 'Create tab seperated values as output file format, default JSON format', @@ -184,7 +184,7 @@ task('audit-tally', 'Audit the tally result for a round') ) .setAction( async ( - { roundAddress, output, tsv, startBlock, endBlock, blocksPerBatch }, + { roundAddress, output, tsv, ipfs, startBlock, endBlock, blocksPerBatch }, { ethers, network } ) => { console.log('Processing on ', network.name) @@ -243,7 +243,7 @@ task('audit-tally', 'Audit the tally result for a round') }) const tallyHash = await round.tallyHash() - const tally = await fetchTally(tallyHash) + const tally = await Ipfs.fetchJson(tallyHash, ipfs) console.log('Merging projects and tally results...') const projects: Project[] = [] diff --git a/contracts/tasks/fetchRound.ts b/contracts/tasks/exportRound.ts similarity index 92% rename from contracts/tasks/fetchRound.ts rename to contracts/tasks/exportRound.ts index 112534ba8..ae4993bf3 100644 --- a/contracts/tasks/fetchRound.ts +++ b/contracts/tasks/exportRound.ts @@ -1,3 +1,13 @@ +/** + * Export all the round data after finalization to generate the leaderboard view + * + * Sample usage: + * + * yarn hardhat export-round --round-address
--out-dir ../vue-app/src/rounds --operator --ipfs --start-block --network + * + * To generate the leaderboard view, deploy the clrfund website with the generated round data in the vue-app/src/rounds folder + */ + import { task, types } from 'hardhat/config' import { HardhatConfig } from 'hardhat/types' import { utils, Contract, BigNumber } from 'ethers' @@ -245,21 +255,22 @@ async function getRoundInfo( } /** - * Fetch all the round data for static site + * Export all the round data for the leaderboard */ -task('fetch-round', 'Fetch round data') +task('export-round', 'Export round data for the leaderboard') .addParam('roundAddress', 'Funding round contract address') .addParam('outputDir', 'Output directory') .addParam('operator', 'Funding round operator, e.g. ETHColombia') + .addOptionalParam('ipfs', 'The IPFS gateway url') .addOptionalParam( 'startBlock', - 'First block to process from the recipient registry contract', + 'First block to process events from the recipient registry contract', 0, types.int ) .addOptionalParam( 'endBlock', - 'Last block to process from the recipient registry', + 'Last block to process events from the recipient registry', undefined, types.int ) @@ -278,6 +289,7 @@ task('fetch-round', 'Fetch round data') endBlock, blocksPerBatch, operator, + ipfs, }, { ethers, network, config } ) => { @@ -328,7 +340,7 @@ task('fetch-round', 'Fetch round data') } try { - tally = await Ipfs.fetchJson(round.tallyHash) + tally = await Ipfs.fetchJson(round.tallyHash, ipfs) } catch (err) { console.log('Failed to get tally file', round.tallyHash, err) throw err diff --git a/contracts/tasks/index.ts b/contracts/tasks/index.ts index c37ce2f42..35ade368e 100644 --- a/contracts/tasks/index.ts +++ b/contracts/tasks/index.ts @@ -8,7 +8,7 @@ import './verifyAll' import './cancelRound' import './evmIncreaseTime' import './auditTally' -import './fetchRound' +import './exportRound' import './mergeAllocations' import './setDurations' import './deploySponsor' diff --git a/contracts/tasks/mergeAllocations.ts b/contracts/tasks/mergeAllocations.ts index fa410a905..d6beab4fb 100644 --- a/contracts/tasks/mergeAllocations.ts +++ b/contracts/tasks/mergeAllocations.ts @@ -1,3 +1,12 @@ +/** + * For cancelled rounds, merge the allocation data to the round JSON file + * This task must be run after the export-round task as this is merging + * data into the output file of the export-round task + * + * Sample usage: + * yarn hardhat merge-allocations --allocation-file /tmp/downloads/clr.fund-round8.tsv --round-file ../vue-app/src/rounds/xdai/0xd07aa7faeba14efde87f2538699c0d6c9a566c20.json + */ + import { task, types } from 'hardhat/config' import { utils, BigNumber } from 'ethers' import fs from 'fs' @@ -104,13 +113,8 @@ function readAllocationFile( return allocations } -/** - * For cancelled rounds, merge the allocation data to the round JSON file - * This task must be run after the fetch-round task as this is merging - * data into the output file of the fetch-round task - */ task('merge-allocations', 'Merge the allocations data into the round JSON file') - .addParam('roundFile', 'The JSON file exported from the fetch-round task') + .addParam('roundFile', 'The JSON file exported from the export-round task') .addParam('allocationFile', 'The allocation file in tab separated format') .addOptionalParam( 'skipAllocationRows', diff --git a/contracts/utils/constants.ts b/contracts/utils/constants.ts index 0dd5d8bf5..c7777989e 100644 --- a/contracts/utils/constants.ts +++ b/contracts/utils/constants.ts @@ -1,5 +1,6 @@ import { BigNumber } from 'ethers' +export const DEFAULT_IPFS_GATEWAY = 'https://ipfs.io' export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' export const UNIT = BigNumber.from(10).pow(BigNumber.from(18)) export const VOICE_CREDIT_FACTOR = BigNumber.from(10).pow(4 + 18 - 9) diff --git a/contracts/utils/ipfs.ts b/contracts/utils/ipfs.ts index 09e74c918..e2157a093 100644 --- a/contracts/utils/ipfs.ts +++ b/contracts/utils/ipfs.ts @@ -1,8 +1,7 @@ // eslint-disable-next-line @typescript-eslint/no-var-requires const Hash = require('ipfs-only-hash') import { utils } from 'ethers' - -const IPFS_BASE_URL = 'https://ipfs.io' +import { DEFAULT_IPFS_GATEWAY } from './constants' export async function getIpfsHash(object: any): Promise { const data = Buffer.from(JSON.stringify(object, null, 4)) @@ -11,7 +10,7 @@ export async function getIpfsHash(object: any): Promise { export class Ipfs { static async fetchJson(hash: string, gatewayUrl?: string): Promise { - const url = `${gatewayUrl || IPFS_BASE_URL}/ipfs/${hash}` + const url = `${gatewayUrl || DEFAULT_IPFS_GATEWAY}/ipfs/${hash}` const result = utils.fetchJson(url) return result } diff --git a/docs/tally-verify.md b/docs/tally-verify.md index c8b50142b..2ac34b7e2 100644 --- a/docs/tally-verify.md +++ b/docs/tally-verify.md @@ -199,7 +199,7 @@ If there's error and the tally task was stopped prematurely, it can be resumed b Result will be saved to `tally.json` file, which must then be published via IPFS. -**Using [command line](https://docs.ipfs.io/reference/cli/)** +**Using [command line](https://docs.ipfs.tech/reference/kubo/cli/#ipfs)** ``` # start ipfs daemon in one terminal @@ -254,7 +254,7 @@ After finalizing the round, enable the leaderboard view in the vue-app by export ```sh cd contracts -yarn hardhat fetch-round --output-dir ../vue-app/src/rounds --network xdai --round-address --operator +yarn hardhat export-round --output-dir ../vue-app/src/rounds --network --round-address --operator --start-block --ipfs ``` 3) Build and deploy the app diff --git a/vue-app/src/rounds/README.md b/vue-app/src/rounds/README.md index a302aa10b..8a9199e02 100644 --- a/vue-app/src/rounds/README.md +++ b/vue-app/src/rounds/README.md @@ -1,6 +1,6 @@ # Static Rounds -This folder is used to store the static round files, which can be generated once a round is finalized. +This folder is used to store the round data generated once a round is finalized. The leaderboard is populated with data from these round data files. The app will load round tally information from this file if it exists, otherwise, it will load dynamically from the subgraph or the smart contracts. @@ -11,7 +11,7 @@ cd contracts # make sure the JSONRPC_HTTP_URL environment variable is set to # the arbitrum provider in the .env file -# extract the EthColombia round data into the rounds folder -yarn hardhat fetch-round --operator ETHColombia --output-dir ../vue-app/src/rounds --round-address 0x4a2d90844eb9c815ef10db0371726f0ceb2848b0 --network arbitrum +# export the clr.fund round 9 data into the rounds folder +yarn hardhat export-round --operator Clr.fund --output-dir ../vue-app/src/rounds --round-address 0x806F08B7DD31fE0267e8c70C4bF8C4BfbBddE760 --ipfs 'https://ipfs.io' --start-block 96912490 --network arbitrum ```