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 07ef68724..2ac34b7e2 100644 --- a/docs/tally-verify.md +++ b/docs/tally-verify.md @@ -98,7 +98,7 @@ The Ethereum private key (`eth-private-key`) can be any private key that control The process may take several hours. Results can be found in `tally.json` file, which must then be published via IPFS. -Finally, the [CID](https://ipfs.io/ipns/docs.ipfs.io/concepts/content-addressing/) of tally file must be submitted to `FundingRound` contract: +Finally, the [CID](https://docs.ipfs.tech/concepts/content-addressing/) of tally file must be submitted to `FundingRound` contract: ``` await fundingRound.publishTallyHash('') @@ -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/components/IpfsCopyWidget.vue b/vue-app/src/components/IpfsCopyWidget.vue index eab9fb5df..7941ada74 100644 --- a/vue-app/src/components/IpfsCopyWidget.vue +++ b/vue-app/src/components/IpfsCopyWidget.vue @@ -15,6 +15,7 @@