diff --git a/.github/workflows/mantle_cancel_round.yml b/.github/workflows/mantle_cancel_round.yml new file mode 100644 index 000000000..80254827a --- /dev/null +++ b/.github/workflows/mantle_cancel_round.yml @@ -0,0 +1,40 @@ +name: Mantle testnet - Cancel current round + +on: + workflow_dispatch: + inputs: + branch_name: + description: 'Clrfund branch name' + required: true + default: 'cohort/EthSingapore' + factory: + description: 'Clrfund factory address' + required: true + default: '0x006f39E6a6D15323334Be1db34C73088550BB20a' + network: + description: 'Network' + required: true + default: 'mantle-testnet' + + +env: + NODE_VERSION: 16.x + WALLET_PRIVATE_KEY: ${{ secrets.MANTLE_TESTNET_COORDINATOR_WALLET_PRIVATE_KEY }} + +jobs: + cancel-round: + runs-on: ubuntu-22.04 + steps: + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Checkout source code + uses: actions/checkout@v3 + - name: Build CLR + run: | + yarn && yarn build + - name: Run the cancel round script + run: | + cd contracts + yarn hardhat cancel-round --factory "${{ github.event.inputs.factory }}" --network "${{ github.event.inputs.network }}" diff --git a/.github/workflows/mantle_create_new_round.yml b/.github/workflows/mantle_create_new_round.yml new file mode 100644 index 000000000..40de9dac0 --- /dev/null +++ b/.github/workflows/mantle_create_new_round.yml @@ -0,0 +1,40 @@ +name: Mantle testnet - Create new round + +on: + workflow_dispatch: + inputs: + branch_name: + description: 'Clrfund branch name' + required: true + default: 'cohort/EthSingapore' + factory: + description: 'Clrfund factory address' + required: true + default: '0x006f39E6a6D15323334Be1db34C73088550BB20a' + network: + description: 'Network' + required: true + default: 'mantle-testnet' + +env: + NODE_VERSION: 16.x + WALLET_PRIVATE_KEY: ${{ secrets.MANTLE_TESTNET_COORDINATOR_WALLET_PRIVATE_KEY }} + +jobs: + create-new-round: + runs-on: ubuntu-22.04 + steps: + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Checkout source code + uses: actions/checkout@v3 + - name: Build CLR + run: | + yarn && yarn build + - name: Run create new round script + run: | + cd contracts + export FACTORY_ADDRESS="${{ github.event.inputs.factory }}" + yarn hardhat run scripts/newRound.ts --network "${{ github.event.inputs.network }}" diff --git a/.github/workflows/mantle_finalize_round.yml b/.github/workflows/mantle_finalize_round.yml new file mode 100644 index 000000000..1298cd77c --- /dev/null +++ b/.github/workflows/mantle_finalize_round.yml @@ -0,0 +1,76 @@ +name: Mantle testnet - Finalize round + +on: + workflow_dispatch: + inputs: + branch_name: + description: 'Clrfund branch name' + required: true + default: 'cohort/EthSingapore' + maci_start_block: + description: 'MACI contract creation block' + required: true + default: '1' + subgraph_url: + description: 'Clrfund subgraph url' + required: true + default: 'https://graph.testnet.mantle.xyz/subgraphs/name/clrfund-ethsingapore-test' + network: + description: 'Network' + required: true + default: 'mantle-testnet' + +env: + NODE_VERSION: 16.x + NETWORK: "arbitrum-goerli" + COORDINATOR_ETH_PK: ${{ secrets.MANTLE_TESTNET_COORDINATOR_WALLET_PRIVATE_KEY }} + COORDINATOR_PK: ${{ secrets.MANTLE_TESTNET_COORDINATOR_MACI_PRIVATE_KEY }} + +jobs: + finalize: + runs-on: ubuntu-22.04 + steps: + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Install g++ library dependencies + run: | + sudo apt update + sudo apt-get install build-essential libgmp-dev libsodium-dev nlohmann-json3-dev nasm g++ curl + - name: Install rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Install zkutil + run: | + cargo install zkutil --version 0.3.2 + - name: Checkout source code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch_name }} + - name: Download batch 64 params + run: | + ls -la $GITHUB_WORKSPACE + $GITHUB_WORKSPACE/.github/scripts/download-batch64-params.sh + - name: Build + run: | + yarn && yarn build + - name: Run finalize scripts + run: | + export SUBGRPAH_URL="${{ github.event.inputs.subgraph_url }}" + echo $SUBGRAPH_URL + export NODE_CONFIG=$(node -e "const snarkParamsPath=process.env.GITHUB_WORKSPACE + '/params'; console.log(JSON.stringify({ snarkParamsPath }));") + export ROUND=$(curl -X POST -d '{"query":"{fundingRoundFactories {id currentRound {id maci}}}"}' $SUBGRPAH_URL) + export FACTORY_ADDRESS=$(node -e 'console.log(JSON.parse(process.env.ROUND).data.fundingRoundFactories[0].id)') + export ROUND_ADDRESS=$(node -e 'console.log(JSON.parse(process.env.ROUND).data.fundingRoundFactories[0].currentRound.id)') + export MACI_ADDRESS=$(node -e 'console.log(JSON.parse(process.env.ROUND).data.fundingRoundFactories[0].currentRound.maci)') + export MACI_START_BLOCK="${{ github.event.inputs.maci_start_block }}" + echo "MACI_START_BLOCK:" $MACI_START_BLOCK + # tally and finalize + cd contracts + yarn hardhat tally --round-address "${ROUND_ADDRESS}" --network "${NETWORK}" + curl --location --request POST 'https://api.pinata.cloud/pinning/pinFileToIPFS' \ + --header "Authorization: Bearer ${{ secrets.PINATA_JWT }}" \ + --form 'file=@"tally.json"' + yarn hardhat run --network "${NETWORK}" scripts/finalize.ts diff --git a/.github/workflows/mantle_set_duration.yml b/.github/workflows/mantle_set_duration.yml new file mode 100644 index 000000000..ccfa8cf5e --- /dev/null +++ b/.github/workflows/mantle_set_duration.yml @@ -0,0 +1,43 @@ +name: Mantle testnet - Set voting duration + +on: + workflow_dispatch: + inputs: + voting_period: + description: 'Voting period in minutes' + required: true + default: '120' + reallocation_period: + description: 'Reallocation period in minutes' + required: true + default: '1' + factory: + description: 'Clrfund factory address' + required: true + default: '0x006f39E6a6D15323334Be1db34C73088550BB20a' + network: + description: 'Network' + required: true + default: 'mantle-testnet' + +env: + NODE_VERSION: 16.x + WALLET_PRIVATE_KEY: ${{ secrets.MANTLE_TESTNET_COORDINATOR_WALLET_PRIVATE_KEY }} + +jobs: + set-durations: + runs-on: ubuntu-22.04 + steps: + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Checkout source code + uses: actions/checkout@v3 + - name: Build CLR + run: | + yarn && yarn build + - name: Run the set duration script + run: | + cd contracts + yarn hardhat set-durations --factory "${{ github.event.inputs.factory }}" --signup "${{ github.event.inputs.voting_period }}" --voting "${{ github.event.inputs.reallocation_period }}" --network "${{ github.event.inputs.network }}" diff --git a/contracts/contracts/recipientRegistry/BaseRecipientRegistry.sol b/contracts/contracts/recipientRegistry/BaseRecipientRegistry.sol index dcf66048e..6cd022472 100644 --- a/contracts/contracts/recipientRegistry/BaseRecipientRegistry.sol +++ b/contracts/contracts/recipientRegistry/BaseRecipientRegistry.sol @@ -146,4 +146,19 @@ abstract contract BaseRecipientRegistry is IRecipientRegistry { function getRecipientCount() public view returns(uint256) { return slots.length - removed.length; } + + /** + * @dev Make a unique recipient id for different registries + * @param _registry Recipient registry address + * @param _recipient Recipient address + * @param _metadata Recipient metadata + * @return recipient id + */ + function makeRecipientId(address _registry, address _recipient, string calldata _metadata) + internal + pure + returns(bytes32) + { + return keccak256(abi.encodePacked(_registry, _recipient, _metadata)); + } } diff --git a/contracts/contracts/recipientRegistry/OptimisticRecipientRegistry.sol b/contracts/contracts/recipientRegistry/OptimisticRecipientRegistry.sol index 9b479ebf3..d10afe677 100644 --- a/contracts/contracts/recipientRegistry/OptimisticRecipientRegistry.sol +++ b/contracts/contracts/recipientRegistry/OptimisticRecipientRegistry.sol @@ -97,7 +97,7 @@ contract OptimisticRecipientRegistry is Ownable, BaseRecipientRegistry { { require(_recipient != address(0), 'RecipientRegistry: Recipient address is zero'); require(bytes(_metadata).length != 0, 'RecipientRegistry: Metadata info is empty string'); - bytes32 recipientId = keccak256(abi.encodePacked(_recipient, _metadata)); + bytes32 recipientId = makeRecipientId(address(this), _recipient, _metadata); require(recipients[recipientId].index == 0, 'RecipientRegistry: Recipient already registered'); require(requests[recipientId].submissionTime == 0, 'RecipientRegistry: Request already submitted'); require(msg.value == baseDeposit, 'RecipientRegistry: Incorrect deposit amount'); diff --git a/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol b/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol index 02265f2d5..744c31b6f 100644 --- a/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol +++ b/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol @@ -97,7 +97,7 @@ contract PermissionedRecipientRegistry is Ownable, BaseRecipientRegistry { { require(_recipient != address(0), 'RecipientRegistry: Recipient address is zero'); require(bytes(_metadata).length != 0, 'RecipientRegistry: Metadata info is empty string'); - bytes32 recipientId = keccak256(abi.encodePacked(_recipient, _metadata)); + bytes32 recipientId = makeRecipientId(address(this), _recipient, _metadata); require(recipients[recipientId].index == 0, 'RecipientRegistry: Recipient already registered'); require(requests[recipientId].submissionTime == 0, 'RecipientRegistry: Request already submitted'); require(msg.value == baseDeposit, 'RecipientRegistry: Incorrect deposit amount'); diff --git a/contracts/contracts/recipientRegistry/SimpleRecipientRegistry.sol b/contracts/contracts/recipientRegistry/SimpleRecipientRegistry.sol index 9f37215e8..efa40a093 100644 --- a/contracts/contracts/recipientRegistry/SimpleRecipientRegistry.sol +++ b/contracts/contracts/recipientRegistry/SimpleRecipientRegistry.sol @@ -47,7 +47,7 @@ contract SimpleRecipientRegistry is Ownable, BaseRecipientRegistry { { require(_recipient != address(0), 'RecipientRegistry: Recipient address is zero'); require(bytes(_metadata).length != 0, 'RecipientRegistry: Metadata info is empty string'); - bytes32 recipientId = keccak256(abi.encodePacked(_recipient, _metadata)); + bytes32 recipientId = makeRecipientId(address(this), _recipient, _metadata); uint256 recipientIndex = _addRecipient(recipientId, _recipient); emit RecipientAdded(recipientId, _recipient, _metadata, recipientIndex, block.timestamp); } diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index e9b5b931e..be598ff63 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -59,6 +59,10 @@ const config: HardhatUserConfig = { process.env.JSONRPC_HTTP_URL || 'https://goerli-rollup.arbitrum.io/rpc', accounts, }, + 'mantle-testnet': { + url: process.env.JSONRPC_HTTP_URL || 'https://rpc.testnet.mantle.xyz', + accounts, + }, rinkarby: { url: process.env.JSONRPC_HTTP_URL || 'https://rinkeby.arbitrum.io/rpc', accounts, diff --git a/contracts/tasks/exportRound.ts b/contracts/tasks/exportRound.ts index ae4993bf3..83304385b 100644 --- a/contracts/tasks/exportRound.ts +++ b/contracts/tasks/exportRound.ts @@ -23,6 +23,7 @@ type RoundListEntry = { network: string address: string startTime: number + votingDeadline: number isFinalized: boolean } @@ -378,6 +379,7 @@ task('export-round', 'Export round data for the leaderboard') network: network.name, address: round.address, startTime: round.startTime, + votingDeadline: round.endTime, isFinalized: round.isFinalized && !round.isCancelled, }) } diff --git a/contracts/tests/recipientRegistry.ts b/contracts/tests/recipientRegistry.ts index 2b1b76c0b..180a8bca6 100644 --- a/contracts/tests/recipientRegistry.ts +++ b/contracts/tests/recipientRegistry.ts @@ -6,7 +6,7 @@ import { keccak256 } from '@ethersproject/solidity' import { gtcrEncode } from '@kleros/gtcr-encoder' import { UNIT, ZERO_ADDRESS } from '../utils/constants' -import { getTxFee } from '../utils/contracts' +import { getTxFee, getEventArg } from '../utils/contracts' import { deployContract } from '../utils/deployment' use(solidity) @@ -18,6 +18,17 @@ async function getCurrentTime(): Promise { return (await provider.getBlock('latest')).timestamp } +function getRecipientId( + registryAddress: string, + address: string, + metadata: string +): string { + return keccak256( + ['address', 'address', 'string'], + [registryAddress, address, metadata] + ) +} + describe('Simple Recipient Registry', () => { const [, deployer, controller, recipient] = provider.getWallets() @@ -69,10 +80,6 @@ describe('Simple Recipient Registry', () => { let metadata: string let recipientId: string - function getRecipientId(address: string, metadata: string): string { - return keccak256(['address', 'string'], [address, metadata]) - } - beforeEach(async () => { await registry.connect(controller).setMaxRecipients(MAX_RECIPIENTS) recipientAddress = recipient.address @@ -81,7 +88,7 @@ describe('Simple Recipient Registry', () => { description: 'Description', imageHash: 'Ipfs imageHash', }) - recipientId = getRecipientId(recipientAddress, metadata) + recipientId = getRecipientId(registry.address, recipientAddress, metadata) }) it('allows owner to add recipient', async () => { @@ -110,6 +117,7 @@ describe('Simple Recipient Registry', () => { const anotherRecipientAddress = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' const anotherRecipientId = getRecipientId( + registry.address, anotherRecipientAddress, metadata ) @@ -256,9 +264,17 @@ describe('Simple Recipient Registry', () => { // Replace recipients const removedRecipient1 = '0x0000000000000000000000000000000000000001' - const removedRecipient1Id = getRecipientId(removedRecipient1, metadata) + const removedRecipient1Id = getRecipientId( + registry.address, + removedRecipient1, + metadata + ) const removedRecipient2 = '0x0000000000000000000000000000000000000002' - const removedRecipient2Id = getRecipientId(removedRecipient2, metadata) + const removedRecipient2Id = getRecipientId( + registry.address, + removedRecipient2, + metadata + ) await registry.removeRecipient(removedRecipient1Id) await registry.removeRecipient(removedRecipient2Id) const addedRecipient1 = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' @@ -571,10 +587,6 @@ describe('Optimistic recipient registry', () => { let metadata: string let recipientId: string - function getRecipientId(address: string, metadata: string): string { - return keccak256(['address', 'string'], [address, metadata]) - } - beforeEach(async () => { await registry.connect(controller).setMaxRecipients(MAX_RECIPIENTS) recipientAddress = recipient.address @@ -583,7 +595,7 @@ describe('Optimistic recipient registry', () => { description: 'Description', imageHash: 'Ipfs imageHash', }) - recipientId = getRecipientId(recipientAddress, metadata) + recipientId = getRecipientId(registry.address, recipientAddress, metadata) }) it('allows anyone to submit registration request', async () => { @@ -808,7 +820,11 @@ describe('Optimistic recipient registry', () => { imageHash: 'Ipfs imageHash', }) recipientAddress = `0x000000000000000000000000000000000000${recipientName}` - recipientId = getRecipientId(recipientAddress, metadata) + recipientId = getRecipientId( + registry.address, + recipientAddress, + metadata + ) if (i < MAX_RECIPIENTS) { await registry.addRecipient(recipientAddress, metadata, { value: baseDeposit, @@ -953,5 +969,40 @@ describe('Optimistic recipient registry', () => { ) ).to.equal(ZERO_ADDRESS) }) + + it('creates different recipient id for different recipient registries', async () => { + const txOne = await registry.addRecipient(recipientAddress, metadata, { + value: baseDeposit, + }) + const idOne = await getEventArg( + txOne, + registry, + 'RequestSubmitted', + '_recipientId' + ) + + const anotherRegistry = await deployContract( + deployer, + 'OptimisticRecipientRegistry', + [baseDeposit, challengePeriodDuration, controller.address] + ) + const txTwo = await anotherRegistry.addRecipient( + recipientAddress, + metadata, + { + value: baseDeposit, + } + ) + const idTwo = await getEventArg( + txTwo, + anotherRegistry, + 'RequestSubmitted', + '_recipientId' + ) + + expect(idOne.length).to.be.gt(0) + expect(idTwo.length).to.be.gt(0) + expect(idOne).to.not.eq(idTwo) + }) }) }) diff --git a/subgraph/src/FundingRoundFactoryMapping.ts b/subgraph/src/FundingRoundFactoryMapping.ts index 5233214e6..6a081c229 100644 --- a/subgraph/src/FundingRoundFactoryMapping.ts +++ b/subgraph/src/FundingRoundFactoryMapping.ts @@ -17,6 +17,7 @@ import { FundingRound as FundingRoundContract } from '../generated/FundingRoundF import { OptimisticRecipientRegistry as RecipientRegistryContract } from '../generated/FundingRoundFactory/OptimisticRecipientRegistry' import { BrightIdUserRegistry as BrightIdUserRegistryContract } from '../generated/FundingRoundFactory/BrightIdUserRegistry' +import { createRecipientRegistry } from './RecipientRegistry' import { FundingRound as FundingRoundTemplate, @@ -31,48 +32,6 @@ import { Token, } from '../generated/schema' -function createRecipientRegistry( - fundingRoundFactoryAddress: Address, - recipientRegistryAddress: Address -): RecipientRegistry { - log.info('New recipientRegistry {}', [recipientRegistryAddress.toHex()]) - let recipientRegistryId = recipientRegistryAddress.toHexString() - let recipientRegistry = new RecipientRegistry(recipientRegistryId) - - recipientRegistryTemplate.create(recipientRegistryAddress) - let recipientRegistryContract = RecipientRegistryContract.bind( - recipientRegistryAddress - ) - let baseDeposit = recipientRegistryContract.try_baseDeposit() - if (baseDeposit.reverted) { - recipientRegistry.baseDeposit = BigInt.fromI32(0) - recipientRegistry.challengePeriodDuration = BigInt.fromI32(0) - } else { - recipientRegistry.baseDeposit = baseDeposit.value - let challengePeriodDuration = - recipientRegistryContract.challengePeriodDuration() - recipientRegistry.challengePeriodDuration = challengePeriodDuration - } - let controller = recipientRegistryContract.try_controller() - let maxRecipients = recipientRegistryContract.try_maxRecipients() - let owner = recipientRegistryContract.try_owner() - - if (!controller.reverted) { - recipientRegistry.controller = controller.value - } - if (!maxRecipients.reverted) { - recipientRegistry.maxRecipients = maxRecipients.value - } - if (!owner.reverted) { - recipientRegistry.owner = owner.value - } - recipientRegistry.fundingRoundFactory = - fundingRoundFactoryAddress.toHexString() - recipientRegistry.save() - - return recipientRegistry -} - function createContributorRegistry( fundingRoundFactoryAddress: Address, contributorRegistryAddress: Address @@ -191,10 +150,7 @@ function createOrUpdateFundingRoundFactory( let recipientRegistryId = recipientRegistryAddress.toHexString() let recipientRegistry = RecipientRegistry.load(recipientRegistryId) if (!recipientRegistry) { - createRecipientRegistry( - fundingRoundFactoryAddress, - recipientRegistryAddress - ) + createRecipientRegistry(fundingRoundFactoryId, recipientRegistryAddress) } let contributorRegistryAddress = fundingRoundFactoryContract.userRegistry() diff --git a/subgraph/src/OptimisticRecipientRegistryMapping.ts b/subgraph/src/OptimisticRecipientRegistryMapping.ts index 24394f231..41c83aa3f 100644 --- a/subgraph/src/OptimisticRecipientRegistryMapping.ts +++ b/subgraph/src/OptimisticRecipientRegistryMapping.ts @@ -5,7 +5,8 @@ import { RequestSubmitted, } from '../generated/OptimisticRecipientRegistry/OptimisticRecipientRegistry' -import { Recipient, RecipientRegistry } from '../generated/schema' +import { Recipient } from '../generated/schema' +import { loadRecipientRegistry } from './RecipientRegistry' // It is also possible to access smart contracts from mappings. For // example, the contract that has emitted the event can be connected to @@ -34,7 +35,7 @@ export function handleRequestResolved(event: RequestResolved): void { log.info('handleRequestResolved', []) let recipientRegistryId = event.address.toHexString() - let recipientRegistry = RecipientRegistry.load(recipientRegistryId) + let recipientRegistry = loadRecipientRegistry(event.address) if (!recipientRegistry) { log.warning( 'handleRequestResolved - ignore unknown recipient registry {} hash {}', @@ -81,8 +82,8 @@ export function handleRequestSubmitted(event: RequestSubmitted): void { log.info('handleRequestSubmitted', []) let recipientRegistryId = event.address.toHexString() - let recipientRegistery = RecipientRegistry.load(recipientRegistryId) - if (!recipientRegistery) { + let recipientRegistry = loadRecipientRegistry(event.address) + if (!recipientRegistry) { log.warning( 'handleRequestSubmitted - ignore unknown recipient registry {} hash {}', [event.address.toHexString(), event.transaction.hash.toHex()] diff --git a/subgraph/src/RecipientRegistry.ts b/subgraph/src/RecipientRegistry.ts new file mode 100644 index 000000000..f809b70f6 --- /dev/null +++ b/subgraph/src/RecipientRegistry.ts @@ -0,0 +1,78 @@ +import { Address, BigInt } from '@graphprotocol/graph-ts' +import { OptimisticRecipientRegistry as RecipientRegistryContract } from '../generated/OptimisticRecipientRegistry/OptimisticRecipientRegistry' + +import { RecipientRegistry, FundingRoundFactory } from '../generated/schema' +import { OptimisticRecipientRegistry as RecipientRegistryTemplate } from '../generated/templates' + +/* + * Create the recipient registry entity + */ +export function createRecipientRegistry( + fundingRoundFactoryId: string, + recipientRegistryAddress: Address +): RecipientRegistry { + let recipientRegistryId = recipientRegistryAddress.toHexString() + let recipientRegistry = new RecipientRegistry(recipientRegistryId) + RecipientRegistryTemplate.create(recipientRegistryAddress) + + let recipientRegistryContract = RecipientRegistryContract.bind( + recipientRegistryAddress + ) + let baseDeposit = recipientRegistryContract.try_baseDeposit() + if (baseDeposit.reverted) { + recipientRegistry.baseDeposit = BigInt.fromI32(0) + recipientRegistry.challengePeriodDuration = BigInt.fromI32(0) + } else { + recipientRegistry.baseDeposit = baseDeposit.value + let challengePeriodDuration = + recipientRegistryContract.challengePeriodDuration() + recipientRegistry.challengePeriodDuration = challengePeriodDuration + } + let controller = recipientRegistryContract.try_controller() + let maxRecipients = recipientRegistryContract.try_maxRecipients() + let owner = recipientRegistryContract.try_owner() + + if (!controller.reverted) { + recipientRegistry.controller = controller.value + } + if (!maxRecipients.reverted) { + recipientRegistry.maxRecipients = maxRecipients.value + } + if (!owner.reverted) { + recipientRegistry.owner = owner.value + } + recipientRegistry.fundingRoundFactory = fundingRoundFactoryId + recipientRegistry.save() + + return recipientRegistry +} + +/* + * Load the recipient registry entity from the subgraph with the given address + */ +export function loadRecipientRegistry( + address: Address +): RecipientRegistry | null { + let recipientRegistryId = address.toHexString() + let recipientRegistry = RecipientRegistry.load(recipientRegistryId) + if (!recipientRegistry) { + let recipientRegistryContract = RecipientRegistryContract.bind(address) + let controller = recipientRegistryContract.try_controller() + if (!controller.reverted) { + // Recipient registry's controller must be the factory + let factoryId = controller.value.toHexString() + let factory = FundingRoundFactory.load(factoryId) + if (factory) { + /* This is our registry, create it */ + recipientRegistry = createRecipientRegistry(factory.id, address) + + // update factory + factory.recipientRegistry = recipientRegistryId + factory.recipientRegistryAddress = address + factory.save() + } + } + } + + return recipientRegistry +} diff --git a/vue-app/src/api/projects.ts b/vue-app/src/api/projects.ts index 7bcc19164..c1a065bd8 100644 --- a/vue-app/src/api/projects.ts +++ b/vue-app/src/api/projects.ts @@ -61,19 +61,6 @@ export async function getRecipientRegistryAddress(roundAddress: string | null): } } -export async function getCurrentRecipientRegistryAddress(): Promise { - const data = await sdk.GetRecipientRegistryInfo({ - factoryAddress: factory.address.toLowerCase(), - }) - - const registryAddress = - data.fundingRoundFactory?.currentRound?.recipientRegistry?.id || - data.fundingRoundFactory?.recipientRegistry?.id || - '' - - return registryAddress -} - export async function getProjects(registryAddress: string, startTime?: number, endTime?: number): Promise { if (recipientRegistryType === 'simple') { return await SimpleRegistry.getProjects(registryAddress, startTime, endTime) diff --git a/vue-app/src/api/round.ts b/vue-app/src/api/round.ts index 44865c682..006a0e501 100644 --- a/vue-app/src/api/round.ts +++ b/vue-app/src/api/round.ts @@ -63,7 +63,7 @@ export async function getCurrentRound(): Promise { return isVoidedRound(fundingRoundAddress) ? null : fundingRoundAddress } -function toRoundInfo(data: any, network: string): RoundInfo { +export function toRoundInfo(data: any, network: string): RoundInfo { const nativeTokenDecimals = Number(data.nativeTokenDecimals) // leaderboard does not need coordinator key, generate a dummy number const keypair = Keypair.createFromSeed(utils.hexlify(utils.randomBytes(32))) @@ -97,7 +97,9 @@ function toRoundInfo(data: any, network: string): RoundInfo { status, startTime: DateTime.fromSeconds(data.startTime), signUpDeadline: DateTime.fromSeconds(Number(data.startTime) + Number(data.signUpDuration)), - votingDeadline: DateTime.fromSeconds(Number(data.startTime) + Number(data.votingDuration)), + votingDeadline: DateTime.fromSeconds( + Number(data.startTime) + Number(data.signUpDuration) + Number(data.votingDuration), + ), totalFunds, matchingPool, contributions, diff --git a/vue-app/src/api/rounds.ts b/vue-app/src/api/rounds.ts index 2d036752f..05f5d18c4 100644 --- a/vue-app/src/api/rounds.ts +++ b/vue-app/src/api/rounds.ts @@ -8,6 +8,7 @@ export interface Round { network?: string hasLeaderboard: boolean startTime: number + votingDeadline: number } export function isVoidedRound(address: string): boolean { @@ -29,8 +30,8 @@ export async function getRounds(): Promise { return [] } - const rounds: Round[] = extraRounds.map(({ address, network, startTime }, index): Round => { - return { index, address, network, hasLeaderboard: true, startTime } + const rounds: Round[] = extraRounds.map(({ address, network, startTime, votingDeadline }, index): Round => { + return { index, address, network, hasLeaderboard: true, startTime, votingDeadline } }) const leaderboardRounds = new Set(rounds.map(r => toRoundId({ network: r.network || '', address: r.address }))) @@ -50,6 +51,7 @@ export async function getRounds(): Promise { address, hasLeaderboard: false, startTime: Number(fundingRound.startTime), + votingDeadline: Number(fundingRound.votingDeadline), }) } } @@ -62,6 +64,7 @@ export async function getRounds(): Promise { address: r.address, hasLeaderboard: r.hasLeaderboard, startTime: r.startTime, + votingDeadline: r.votingDeadline, network: r.network, } }) diff --git a/vue-app/src/components.d.ts b/vue-app/src/components.d.ts index 7ccfb0c87..1cef45f0e 100644 --- a/vue-app/src/components.d.ts +++ b/vue-app/src/components.d.ts @@ -23,6 +23,7 @@ declare module '@vue/runtime-core' { ContributionModal: typeof import('./components/ContributionModal.vue')['default'] CopyButton: typeof import('./components/CopyButton.vue')['default'] CriteriaModal: typeof import('./components/CriteriaModal.vue')['default'] + DateRange: typeof import('./components/DateRange.vue')['default'] ErrorModal: typeof import('./components/ErrorModal.vue')['default'] FilterDropdown: typeof import('./components/FilterDropdown.vue')['default'] FormNavigation: typeof import('./components/FormNavigation.vue')['default'] diff --git a/vue-app/src/components/DateRange.vue b/vue-app/src/components/DateRange.vue new file mode 100644 index 000000000..2ad28955f --- /dev/null +++ b/vue-app/src/components/DateRange.vue @@ -0,0 +1,18 @@ + + + diff --git a/vue-app/src/components/RoundInformation.vue b/vue-app/src/components/RoundInformation.vue index ebbbbef93..9fd7fdf45 100644 --- a/vue-app/src/components/RoundInformation.vue +++ b/vue-app/src/components/RoundInformation.vue @@ -166,6 +166,12 @@ +
+
{{ $t('roundInfo.round_period') }}
+
+ +
+
@@ -280,8 +286,9 @@