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/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) + }) }) })