From 1a46db0abd0fbf6f08008fee17bfbc02727db47a Mon Sep 17 00:00:00 2001 From: yuetloo Date: Tue, 21 Nov 2023 18:35:38 -0500 Subject: [PATCH] code cleanup and update e2e testing with maci v1 api --- .github/workflows/test-scripts.yml | 25 +- contracts/.env.example | 12 +- contracts/.gitignore | 2 +- contracts/contracts/ClrFund.sol | 37 +- contracts/contracts/ClrFundDeployer.sol | 97 ++- contracts/contracts/FundingRound.sol | 3 + contracts/contracts/FundingRoundFactory.sol | 30 + contracts/contracts/MACIFactory.sol | 49 +- contracts/contracts/PollFactoryCreator.sol | 11 - contracts/contracts/TopupToken.sol | 18 + contracts/contracts/VkRegistryCreator.sol | 11 - contracts/e2e/index.ts | 138 +++-- contracts/package.json | 8 +- contracts/scripts/claim.ts | 47 -- contracts/scripts/contribute.ts | 66 --- contracts/scripts/deploy.ts | 142 ----- contracts/scripts/deployUserRegistry.ts | 52 -- contracts/scripts/finalize.ts | 53 -- contracts/scripts/newRound.ts | 90 --- contracts/scripts/tally.ts | 235 ++++++++ contracts/scripts/vote.ts | 74 --- contracts/sh/runScriptTests.sh | 91 +++ contracts/tasks/addContributors.ts | 41 ++ contracts/tasks/addRecipients.ts | 45 ++ contracts/tasks/cancelRound.ts | 12 +- contracts/tasks/claim.ts | 56 ++ contracts/tasks/contribute.ts | 76 +++ contracts/tasks/finalize.ts | 65 ++ contracts/tasks/index.ts | 53 +- contracts/tasks/newClrFund.ts | 144 +++++ contracts/tasks/newDeployer.ts | 75 +++ contracts/tasks/newMaciKey.ts | 19 + contracts/tasks/newRound.ts | 77 +++ contracts/tasks/pubkey.ts | 30 + contracts/tasks/setCoordinator.ts | 68 +++ contracts/tasks/setDurations.ts | 34 -- contracts/tasks/setMaciParameters.ts | 33 ++ contracts/tasks/setPollFactory.ts | 39 ++ contracts/tasks/setRecipientRegistry.ts | 137 +++++ contracts/tasks/setToken.ts | 51 ++ contracts/tasks/setUserRegistry.ts | 115 ++++ contracts/tasks/tally.ts | 257 -------- .../{evmIncreaseTime.ts => timeTravel.ts} | 4 +- contracts/tasks/verifyAll.ts | 196 ++++-- contracts/tasks/verifyMaciFactory.ts | 76 +-- contracts/tasks/verifyRoundFactory.ts | 20 - contracts/tasks/vote.ts | 84 +++ contracts/tests/deployer.ts | 419 +++++++------ contracts/tests/maciFactory.ts | 30 +- contracts/tests/recipientRegistry.ts | 26 +- contracts/tests/round.ts | 34 +- contracts/tsconfig.json | 2 +- contracts/utils/JSONFile.ts | 27 + contracts/utils/circuits.ts | 2 + contracts/utils/constants.ts | 1 + contracts/utils/deployment.ts | 411 +++++++++---- contracts/utils/maci.ts | 228 +++---- contracts/utils/maciParameters.ts | 146 +++++ contracts/utils/misc.ts | 11 + yarn.lock | 556 +++++++++--------- 60 files changed, 3051 insertions(+), 1940 deletions(-) create mode 100644 contracts/contracts/FundingRoundFactory.sol delete mode 100644 contracts/contracts/PollFactoryCreator.sol create mode 100644 contracts/contracts/TopupToken.sol delete mode 100644 contracts/contracts/VkRegistryCreator.sol delete mode 100644 contracts/scripts/claim.ts delete mode 100644 contracts/scripts/contribute.ts delete mode 100644 contracts/scripts/deploy.ts delete mode 100644 contracts/scripts/deployUserRegistry.ts delete mode 100644 contracts/scripts/finalize.ts delete mode 100644 contracts/scripts/newRound.ts create mode 100644 contracts/scripts/tally.ts delete mode 100644 contracts/scripts/vote.ts create mode 100755 contracts/sh/runScriptTests.sh create mode 100644 contracts/tasks/addContributors.ts create mode 100644 contracts/tasks/addRecipients.ts create mode 100644 contracts/tasks/claim.ts create mode 100644 contracts/tasks/contribute.ts create mode 100644 contracts/tasks/finalize.ts create mode 100644 contracts/tasks/newClrFund.ts create mode 100644 contracts/tasks/newDeployer.ts create mode 100644 contracts/tasks/newMaciKey.ts create mode 100644 contracts/tasks/newRound.ts create mode 100644 contracts/tasks/pubkey.ts create mode 100644 contracts/tasks/setCoordinator.ts delete mode 100644 contracts/tasks/setDurations.ts create mode 100644 contracts/tasks/setMaciParameters.ts create mode 100644 contracts/tasks/setPollFactory.ts create mode 100644 contracts/tasks/setRecipientRegistry.ts create mode 100644 contracts/tasks/setToken.ts create mode 100644 contracts/tasks/setUserRegistry.ts delete mode 100644 contracts/tasks/tally.ts rename contracts/tasks/{evmIncreaseTime.ts => timeTravel.ts} (73%) delete mode 100644 contracts/tasks/verifyRoundFactory.ts create mode 100644 contracts/tasks/vote.ts create mode 100644 contracts/utils/JSONFile.ts create mode 100644 contracts/utils/maciParameters.ts create mode 100644 contracts/utils/misc.ts diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 41937c33d..bb3b38612 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -29,13 +29,24 @@ jobs: - name: Install zkutil run: | cargo install zkutil --version 0.3.2 + - name: Checkout rapidsnark source code + uses: actions/checkout@v3 + with: + repository: iden3/rapidsnark + path: rapidsnark + - name: Install rapidsnark + npm install + git submodule init + git submodule update + npx task createFieldSources + npx task buildProver - name: Checkout source code uses: actions/checkout@v3 with: path: monorepo - name: Download batch 64 params run: | - $GITHUB_WORKSPACE/monorepo/.github/scripts/download-batch64-params.sh + $GITHUB_WORKSPACE/monorepo/.github/scripts/download-6-8-2-3.sh - name: Build CLR run: | cd monorepo @@ -48,14 +59,6 @@ jobs: yarn start:node & - name: Run script tests run: | + export RAPIDSNARK_DIRECTORY=$GITHUB_WORKSPACE/rapidsnark cd monorepo/contracts - export NODE_CONFIG=$(node -e "const snarkParamsPath=process.env.GITHUB_WORKSPACE + '/params'; console.log(JSON.stringify({ snarkParamsPath }));") - echo $NODE_CONFIG - yarn deploy:local - yarn deployTestRound:local - yarn contribute:local - yarn vote:local - yarn hardhat evm-increase-time 1200 --network localhost - yarn tally:local - yarn finalize:local - yarn claim:local + ./sh/runScriptTests.sh diff --git a/contracts/.env.example b/contracts/.env.example index c3c9355ba..5c61baf56 100644 --- a/contracts/.env.example +++ b/contracts/.env.example @@ -23,7 +23,7 @@ WALLET_PRIVATE_KEY= NATIVE_TOKEN_ADDRESS= # Required to use in the tally and finalize scripts -FACTORY_ADDRESS= +CLRFUND= ROUND_ADDRESS= COORDINATOR_PK= COORDINATOR_ETH_PK= @@ -50,7 +50,15 @@ CIRCUIT_TYPE=prod # The IPFS gateway url used by the prepare-results.ts script IPFS_GATEWAY_URL= -# circuit params and directory used by e2e script +# Parameters used in the tally script CIRCUIT_TYPE= CIRCUIT_DIRECTORY= RAPIDSNARK_DIRECTORY= +# Used in MACI queue merging operation before genProofs, default is 4 +NUM_QUEUE_OPS= +# Used in e2e testing to store intermediate states +STATE_FILE= +# MACI creation transaction hash, used to find the start block of MACI logs +MACI_TRANSACTION_HASH= +# genProofs output directory +PROOF_OUTPUT_DIR= diff --git a/contracts/.gitignore b/contracts/.gitignore index b68456558..0f8b3e642 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -7,5 +7,5 @@ proofs.json tally.json .env .DS_Store -tasks/addresses.txt +addresses.txt proof_output diff --git a/contracts/contracts/ClrFund.sol b/contracts/contracts/ClrFund.sol index 21a0a5bb2..eaf988db7 100644 --- a/contracts/contracts/ClrFund.sol +++ b/contracts/contracts/ClrFund.sol @@ -16,6 +16,8 @@ import './userRegistry/IUserRegistry.sol'; import './recipientRegistry/IRecipientRegistry.sol'; import {FundingRound} from './FundingRound.sol'; import './OwnableUpgradeable.sol'; +import {FundingRoundFactory} from './FundingRoundFactory.sol'; +import {TopupToken} from './TopupToken.sol'; contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { using EnumerableSet for EnumerableSet.AddressSet; @@ -33,6 +35,8 @@ contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { EnumerableSet.AddressSet private fundingSources; FundingRound[] private rounds; + FundingRoundFactory public roundFactory; + // Events event FundingSourceAdded(address _source); event FundingSourceRemoved(address _source); @@ -40,6 +44,10 @@ contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { event RoundFinalized(address _round); event TokenChanged(address _token); event CoordinatorChanged(address _coordinator); + event Initialized(); + event UserRegistrySet(); + event RecipientRegistrySet(); + event FundingRoundTemplateChanged(); // errors error FundingSourceAlreadyAdded(); @@ -53,14 +61,27 @@ contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { error NoRecipientRegistry(); error NoUserRegistry(); error NotOwnerOfMaciFactory(); + error InvalidFundingRoundFactory(); + error InvalidMaciFactory(); + /** + * @dev Initialize clrfund instance with MACI factory and new round templates + */ function init( - MACIFactory _maciFactory + address _maciFactory, + address _roundFactory ) external { __Ownable_init(); - maciFactory = _maciFactory; + + if (address(_maciFactory) == address(0)) revert InvalidMaciFactory(); + if (_roundFactory == address(0)) revert InvalidFundingRoundFactory(); + + maciFactory = MACIFactory(_maciFactory); + roundFactory = FundingRoundFactory(_roundFactory); + + emit Initialized(); } /** @@ -72,6 +93,8 @@ contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { onlyOwner { userRegistry = _userRegistry; + + emit UserRegistrySet(); } /** @@ -85,6 +108,8 @@ contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { recipientRegistry = _recipientRegistry; (, uint256 maxVoteOptions) = maciFactory.maxValues(); recipientRegistry.setMaxRecipients(maxVoteOptions); + + emit RecipientRegistrySet(); } /** @@ -150,18 +175,20 @@ contract ClrFund is OwnableUpgradeable, IPubKey, SnarkCommon, Params { (, uint256 maxVoteOptions) = maciFactory.maxValues(); recipientRegistry.setMaxRecipients(maxVoteOptions); // Deploy funding round and MACI contracts - FundingRound newRound = new FundingRound( + FundingRound newRound = roundFactory.deploy( nativeToken, userRegistry, recipientRegistry, - coordinator + coordinator, + address(this) ); rounds.push(newRound); + TopupToken topupToken = newRound.topupToken(); MACI maci = maciFactory.deployMaci( SignUpGatekeeper(newRound), InitialVoiceCreditProxy(newRound), - address(nativeToken), + address(topupToken), duration, coordinator, coordinatorPubKey diff --git a/contracts/contracts/ClrFundDeployer.sol b/contracts/contracts/ClrFundDeployer.sol index b7d2707a0..8db5bc7fd 100644 --- a/contracts/contracts/ClrFundDeployer.sol +++ b/contracts/contracts/ClrFundDeployer.sol @@ -2,46 +2,85 @@ pragma solidity 0.8.10; -import './MACIFactory.sol'; -import './ClrFund.sol'; +import {MACIFactory} from './MACIFactory.sol'; +import {ClrFund} from './ClrFund.sol'; import {CloneFactory} from './CloneFactory.sol'; import {SignUpGatekeeper} from "@clrfund/maci-contracts/contracts/gatekeepers/SignUpGatekeeper.sol"; import {InitialVoiceCreditProxy} from "@clrfund/maci-contracts/contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol"; +import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; -contract ClrFundDeployer is CloneFactory { +contract ClrFundDeployer is CloneFactory, Ownable { + address public clrfundTemplate; + address public maciFactory; + address public roundFactory; + mapping (address => bool) public clrfunds; - address public template; - mapping (address => bool) public clrfunds; + event NewInstance(address indexed clrfund); + event Register(address indexed clrfund, string metadata); + event NewFundingRoundTemplate(address newTemplate); + event NewClrfundTemplate(address newTemplate); - constructor(address _template) { - template = _template; - } - - event NewInstance(address indexed clrfund); - event Register(address indexed clrfund, string metadata); + // errors + error ClrFundAlreadyRegistered(); + error InvalidMaciFactory(); + error InvalidClrFundTemplate(); + error InvalidFundingRoundFactory(); - // errors - error ClrFundAlreadyRegistered(); + constructor( + address _clrfundTemplate, + address _maciFactory, + address _roundFactory + ) + { + if (_clrfundTemplate == address(0)) revert InvalidClrFundTemplate(); + if (_maciFactory == address(0)) revert InvalidMaciFactory(); + if (_roundFactory == address(0)) revert InvalidFundingRoundFactory(); - function deployClrFund(MACIFactory _maciFactory) public returns (address) { + clrfundTemplate = _clrfundTemplate; + maciFactory = _maciFactory; + roundFactory = _roundFactory; + } - ClrFund clrfund = ClrFund(createClone(template)); - clrfund.init(_maciFactory); - emit NewInstance(address(clrfund)); + /** + * @dev Set a new clrfund template + * @param _clrfundTemplate New template + */ + function setClrFundTemplate(address _clrfundTemplate) + external + onlyOwner + { + if (_clrfundTemplate == address(0)) revert InvalidClrFundTemplate(); - return address(clrfund); - } - - function registerInstance( - address _clrFundAddress, - string memory _metadata - ) public returns (bool) { + clrfundTemplate = _clrfundTemplate; + emit NewClrfundTemplate(_clrfundTemplate); + } - if (clrfunds[_clrFundAddress] == true) revert ClrFundAlreadyRegistered(); + /** + * @dev Deploy a new instance of ClrFund + */ + function deployClrFund() public returns (address) { + ClrFund clrfund = ClrFund(createClone(clrfundTemplate)); + clrfund.init(maciFactory, roundFactory); + emit NewInstance(address(clrfund)); - clrfunds[_clrFundAddress] = true; + return address(clrfund); + } - emit Register(_clrFundAddress, _metadata); - return true; - } + /** + * @dev Register the clrfund instance of subgraph event processing + * @param _clrFundAddress ClrFund address + * @param _metadata Clrfund metadata + */ + function registerInstance( + address _clrFundAddress, + string memory _metadata + ) public returns (bool) { + + if (clrfunds[_clrFundAddress] == true) revert ClrFundAlreadyRegistered(); + + clrfunds[_clrFundAddress] = true; + + emit Register(_clrFundAddress, _metadata); + return true; + } } diff --git a/contracts/contracts/FundingRound.sol b/contracts/contracts/FundingRound.sol index efc98c230..a4f465a39 100644 --- a/contracts/contracts/FundingRound.sol +++ b/contracts/contracts/FundingRound.sol @@ -10,6 +10,7 @@ import {DomainObjs} from '@clrfund/maci-contracts/contracts/DomainObjs.sol'; import {MACI} from '@clrfund/maci-contracts/contracts/MACI.sol'; import {Poll} from '@clrfund/maci-contracts/contracts/Poll.sol'; import {Tally} from '@clrfund/maci-contracts/contracts/Tally.sol'; +import {TopupToken} from './TopupToken.sol'; import {SignUpGatekeeper} from "@clrfund/maci-contracts/contracts/gatekeepers/SignUpGatekeeper.sol"; import {InitialVoiceCreditProxy} from "@clrfund/maci-contracts/contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol"; @@ -93,6 +94,7 @@ contract FundingRound is Ownable, SignUpGatekeeper, InitialVoiceCreditProxy, Dom address public coordinator; MACI public maci; ERC20 public nativeToken; + TopupToken public topupToken; IUserRegistry public userRegistry; IRecipientRegistry public recipientRegistry; string public tallyHash; @@ -143,6 +145,7 @@ contract FundingRound is Ownable, SignUpGatekeeper, InitialVoiceCreditProxy, Dom userRegistry = _userRegistry; recipientRegistry = _recipientRegistry; coordinator = _coordinator; + topupToken = new TopupToken(); } /** diff --git a/contracts/contracts/FundingRoundFactory.sol b/contracts/contracts/FundingRoundFactory.sol new file mode 100644 index 000000000..6d4d586de --- /dev/null +++ b/contracts/contracts/FundingRoundFactory.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.10; + +import {FundingRound} from './FundingRound.sol'; +import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import {IUserRegistry} from './userRegistry/IUserRegistry.sol'; +import {IRecipientRegistry} from './recipientRegistry/IRecipientRegistry.sol'; + +contract FundingRoundFactory { + function deploy( + ERC20 _nativeToken, + IUserRegistry _userRegistry, + IRecipientRegistry _recipientRegistry, + address _coordinator, + address _owner + ) + external + returns (FundingRound newRound) + { + newRound = new FundingRound( + _nativeToken, + _userRegistry, + _recipientRegistry, + _coordinator + ); + + newRound.transferOwnership(_owner); + } +} diff --git a/contracts/contracts/MACIFactory.sol b/contracts/contracts/MACIFactory.sol index 2f5e35983..e633fead4 100644 --- a/contracts/contracts/MACIFactory.sol +++ b/contracts/contracts/MACIFactory.sol @@ -11,7 +11,6 @@ import {VkRegistry} from '@clrfund/maci-contracts/contracts/VkRegistry.sol'; import {SnarkCommon} from '@clrfund/maci-contracts/contracts/crypto/SnarkCommon.sol'; import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol'; import {Params} from '@clrfund/maci-contracts/contracts/Params.sol'; -import {PollFactoryCreator} from './PollFactoryCreator.sol'; import {IPubKey} from '@clrfund/maci-contracts/contracts/DomainObjs.sol'; contract MACIFactory is Ownable, Params, SnarkCommon, IPubKey { @@ -20,6 +19,7 @@ contract MACIFactory is Ownable, Params, SnarkCommon, IPubKey { // State VkRegistry public vkRegistry; + PollFactory public pollFactory; uint8 public stateTreeDepth; TreeDepths public treeDepths; MaxValues public maxValues; @@ -31,27 +31,41 @@ contract MACIFactory is Ownable, Params, SnarkCommon, IPubKey { // errors error NotInitialized(); - error CannotDecreaseVoteOptionDepth(); error ProcessVkNotSet(); error TallyVkNotSet(); error InvalidVkRegistry(); + error InvalidPollFactory(); - constructor(VkRegistry _vkRegistry) { - _setVkRegistry(_vkRegistry); + constructor(address _vkRegistry, address _pollFactory) { + if (_vkRegistry == address(0)) revert InvalidVkRegistry(); + if (_pollFactory == address(0)) revert InvalidPollFactory(); + + vkRegistry = VkRegistry(_vkRegistry); + pollFactory = PollFactory(_pollFactory); } - function _setVkRegistry(VkRegistry _vkRegistry) internal { - if (address(_vkRegistry) == address(0)) { - revert InvalidVkRegistry(); - } + /** + * @dev set vk registry + */ + function setVkRegistry(address _vkRegistry) public onlyOwner { + if (_vkRegistry == address(0)) revert InvalidVkRegistry(); - vkRegistry = _vkRegistry; + vkRegistry = VkRegistry(_vkRegistry); } - function setVkRegistry(VkRegistry _vkRegistry) public onlyOwner { - _setVkRegistry(_vkRegistry); + /** + * @dev set poll factory in MACI factory + * @param _pollFactory poll factory + */ + function setPollFactory(address _pollFactory) public onlyOwner { + if (_pollFactory == address(0)) revert InvalidPollFactory(); + + pollFactory = PollFactory(_pollFactory); } + /** + * @dev set MACI zkeys parameters + */ function setMaciParameters( uint8 _stateTreeDepth, TreeDepths calldata _treeDepths, @@ -63,10 +77,6 @@ contract MACIFactory is Ownable, Params, SnarkCommon, IPubKey { public onlyOwner { - if (_treeDepths.voteOptionTreeDepth < treeDepths.voteOptionTreeDepth) { - revert CannotDecreaseVoteOptionDepth(); - } - if (!vkRegistry.hasProcessVk( _stateTreeDepth, _treeDepths.messageTreeDepth, @@ -128,20 +138,15 @@ contract MACIFactory is Ownable, Params, SnarkCommon, IPubKey { revert TallyVkNotSet(); } - PollFactory pollFactory = PollFactoryCreator.create(); _maci = new MACI( pollFactory, signUpGatekeeper, initialVoiceCreditProxy ); - pollFactory.transferOwnership(address(_maci)); _maci.init(vkRegistry, TopupCredit(topupCredit)); - _maci.deployPoll(duration, maxValues, treeDepths, coordinatorPubKey); - - // this is a brand new maci, get poll 0 - Poll poll = _maci.getPoll(0); - poll.transferOwnership(coordinator); + address poll = _maci.deployPoll(duration, maxValues, treeDepths, coordinatorPubKey); + Poll(poll).transferOwnership(coordinator); emit MaciDeployed(address(_maci)); } diff --git a/contracts/contracts/PollFactoryCreator.sol b/contracts/contracts/PollFactoryCreator.sol deleted file mode 100644 index 24b8b0f9d..000000000 --- a/contracts/contracts/PollFactoryCreator.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.10; - -import {PollFactory} from '@clrfund/maci-contracts/contracts/Poll.sol'; - -library PollFactoryCreator { - function create() external returns (PollFactory pollFactory) { - pollFactory = new PollFactory(); - } -} \ No newline at end of file diff --git a/contracts/contracts/TopupToken.sol b/contracts/contracts/TopupToken.sol new file mode 100644 index 000000000..c70068ae7 --- /dev/null +++ b/contracts/contracts/TopupToken.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.10; + +import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * TopupToken is used by MACI Poll contract to validate the topup credits of a user + * In clrfund, this is only used as gateway to pass the topup amount to the Poll contract + */ +contract TopupToken is ERC20, Ownable { + constructor() ERC20("TopupCredit", "TopupCredit") {} + + function airdrop(uint256 amount) public onlyOwner { + _mint(msg.sender, amount); + } +} diff --git a/contracts/contracts/VkRegistryCreator.sol b/contracts/contracts/VkRegistryCreator.sol deleted file mode 100644 index ef43411c9..000000000 --- a/contracts/contracts/VkRegistryCreator.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.10; - -import {VkRegistry} from '@clrfund/maci-contracts/contracts/VkRegistry.sol'; - -library VkRegistryCreator { - function create() public returns (VkRegistry vkRegistry) { - vkRegistry = new VkRegistry(); - } -} \ No newline at end of file diff --git a/contracts/e2e/index.ts b/contracts/e2e/index.ts index e6ead998a..4738c4755 100644 --- a/contracts/e2e/index.ts +++ b/contracts/e2e/index.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { ethers, waffle } from 'hardhat' +import { ethers, waffle, config } from 'hardhat' import { use, expect } from 'chai' import { solidity } from 'ethereum-waffle' import { BigNumber, Contract, Signer, Wallet } from 'ethers' @@ -9,23 +9,23 @@ import { genTallyResultCommitment } from '@clrfund/common' import { UNIT } from '../utils/constants' import { getEventArg } from '../utils/contracts' import { - deployMaciFactory, deployContract, - mergeMessages, - mergeSignups, - genProofs, - proveOnChain, deployPoseidonLibraries, - deployContractWithLinkedLibraries, + deployMaciFactory, + deployMessageProcesorAndTally, } from '../utils/deployment' import { getIpfsHash } from '../utils/ipfs' import { bnSqrt, - MaciParameters, + genProofs, + proveOnChain, + mergeMaciSubtrees, addTallyResultsBatch, getRecipientClaimData, getGenProofArgs, } from '../utils/maci' +import { DEFAULT_CIRCUIT } from '../utils/circuits' +import { MaciParameters } from '../utils/maciParameters' import { readFileSync, existsSync, mkdirSync } from 'fs' import path from 'path' @@ -36,10 +36,13 @@ const DEFAULT_SR_QUEUE_OPS = 4 const roundDuration = 7 * 86400 // MACI zkFiles -const circuit = process.env.CIRCUIT_TYPE || 'micro' +const circuit = process.env.CIRCUIT_TYPE || DEFAULT_CIRCUIT const circuitDirectory = process.env.CIRCUIT_DIRECTORY || '../../params' const rapidSnarkDirectory = process.env.RAPIDSNARK_DIRECTORY || '../../rapidsnark/build' +const proofOutputDirectory = process.env.PROOF_OUTPUT_DIR || './proof_output' +const tallyBatchSize = Number(process.env.TALLY_BATCH_SIZE || 8) + let maciTransactionHash: string const ALPHA_PRECISION = BigNumber.from(10).pow(18) @@ -112,7 +115,7 @@ describe('End-to-end Tests', function () { let poseidonLibraries: { [key: string]: string } let userRegistry: Contract let recipientRegistry: Contract - let fundingRoundFactory: Contract + let clrfund: Contract let token: Contract let fundingRound: Contract let maci: Contract @@ -122,7 +125,11 @@ describe('End-to-end Tests', function () { before(async () => { params = await MaciParameters.fromConfig(circuit, circuitDirectory) - poseidonLibraries = await deployPoseidonLibraries(deployer) + poseidonLibraries = await deployPoseidonLibraries({ + ethers, + artifactsPath: config.paths.artifacts, + signer: deployer, + }) }) beforeEach(async () => { @@ -138,22 +145,35 @@ describe('End-to-end Tests', function () { ] = await ethers.getSigners() // Deploy funding round factory - const maciFactory = await deployMaciFactory(deployer, poseidonLibraries) + const maciFactory = await deployMaciFactory({ + libraries: poseidonLibraries, + signer: deployer, + ethers, + }) const setMaciTx = await maciFactory.setMaciParameters( ...params.asContractParam() ) await setMaciTx.wait() - fundingRoundFactory = await deployContractWithLinkedLibraries( - deployer, - 'ClrFund', - poseidonLibraries + clrfund = await deployContract({ + name: 'ClrFund', + signer: deployer, + ethers, + }) + + const roundFactory = await deployContract({ + name: 'FundingRoundFactory', + libraries: poseidonLibraries, + signer: deployer, + ethers, + }) + + const initClrfundTx = await clrfund.init( + maciFactory.address, + roundFactory.address ) - const initClrfundTx = await fundingRoundFactory.init(maciFactory.address) await initClrfundTx.wait() - const transferTx = await maciFactory.transferOwnership( - fundingRoundFactory.address - ) + const transferTx = await maciFactory.transferOwnership(clrfund.address) await transferTx.wait() const SimpleUserRegistry = await ethers.getContractFactory( @@ -161,15 +181,13 @@ describe('End-to-end Tests', function () { deployer ) userRegistry = await SimpleUserRegistry.deploy() - await fundingRoundFactory.setUserRegistry(userRegistry.address) + await clrfund.setUserRegistry(userRegistry.address) const SimpleRecipientRegistry = await ethers.getContractFactory( 'SimpleRecipientRegistry', deployer ) - recipientRegistry = await SimpleRecipientRegistry.deploy( - fundingRoundFactory.address - ) - await fundingRoundFactory.setRecipientRegistry(recipientRegistry.address) + recipientRegistry = await SimpleRecipientRegistry.deploy(clrfund.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) // Deploy ERC20 token contract const Token = await ethers.getContractFactory('AnyOldERC20Token', deployer) @@ -182,9 +200,9 @@ describe('End-to-end Tests', function () { } // Configure factory - await fundingRoundFactory.setToken(token.address) + await clrfund.setToken(token.address) coordinatorKeypair = new Keypair() - await fundingRoundFactory.setCoordinator( + await clrfund.setCoordinator( coordinator.address, coordinatorKeypair.pubKey.asContractParam() ) @@ -193,15 +211,13 @@ describe('End-to-end Tests', function () { const poolContributionAmount = UNIT.mul(5) await token .connect(poolContributor1) - .transfer(fundingRoundFactory.address, poolContributionAmount) + .transfer(clrfund.address, poolContributionAmount) // Add additional funding source - await fundingRoundFactory.addFundingSource( - await poolContributor2.getAddress() - ) + await clrfund.addFundingSource(await poolContributor2.getAddress()) await token .connect(poolContributor2) - .approve(fundingRoundFactory.address, poolContributionAmount) + .approve(clrfund.address, poolContributionAmount) // Add recipients await recipientRegistry.addRecipient( @@ -230,9 +246,9 @@ describe('End-to-end Tests', function () { ) // Deploy new funding round and MACI - const newRoundTx = await fundingRoundFactory.deployNewRound(roundDuration) + const newRoundTx = await clrfund.deployNewRound(roundDuration) maciTransactionHash = newRoundTx.hash - const fundingRoundAddress = await fundingRoundFactory.getCurrentRound() + const fundingRoundAddress = await clrfund.getCurrentRound() fundingRound = await ethers.getContractAt( 'FundingRound', fundingRoundAddress @@ -299,20 +315,14 @@ describe('End-to-end Tests', function () { const providerUrl = (provider as any)._hardhatNetwork.config.url // Process messages and tally votes - await mergeMessages({ - contract: maci.address, - poll_id: pollId.toString(), - num_queue_ops: DEFAULT_SR_QUEUE_OPS, - }) - - await mergeSignups({ - contract: maci.address, - poll_id: pollId.toString(), - num_queue_ops: DEFAULT_SR_QUEUE_OPS, - }) + await mergeMaciSubtrees( + maci.address, + pollId.toString(), + DEFAULT_SR_QUEUE_OPS + ) const random = Math.floor(Math.random() * 10 ** 8) - const outputDir = path.join('.', 'proof_output', `${random}`) + const outputDir = path.join(proofOutputDirectory, `${random}`) if (!existsSync(outputDir)) { mkdirSync(outputDir, { recursive: true }) } @@ -320,7 +330,7 @@ describe('End-to-end Tests', function () { maciAddress: maci.address, providerUrl, pollId: pollId.toString(), - serializedCoordinatorPrivKey: coordinatorKeypair.privKey.serialize(), + coordinatorMacisk: coordinatorKeypair.privKey.serialize(), maciTxHash: maciTransactionHash, rapidSnarkDirectory, circuitType: circuit, @@ -329,23 +339,11 @@ describe('End-to-end Tests', function () { }) await genProofs(genProofArgs) - // deploy the tally contract - const verifierContract = await deployContract(coordinator, 'Verifier') - const tallyContract = await deployContractWithLinkedLibraries( - coordinator, - 'Tally', - poseidonLibraries, - [verifierContract.address] - ) - await fundingRound.connect(coordinator).setTally(tallyContract.address) - - // deploy the message processing contract - const mpContract = await deployContractWithLinkedLibraries( - coordinator, - 'MessageProcessor', - poseidonLibraries, - [verifierContract.address] - ) + const { mpContract, tallyContract } = await deployMessageProcesorAndTally({ + libraries: poseidonLibraries, + ethers, + signer: coordinator, + }) // Submit proofs to MACI contract await proveOnChain({ @@ -353,25 +351,25 @@ describe('End-to-end Tests', function () { poll_id: pollId.toString(), mp: mpContract.address, tally: tallyContract.address, - subsidy: tallyContract.address, // TODO: make subsidy optional + //subsidy: tallyContract.address, // TODO: make subsidy optional proof_dir: genProofArgs.output, }) - console.log('finished proveOnChain') + + await fundingRound.connect(coordinator).setTally(tallyContract.address) const tally = JSON.parse(readFileSync(genProofArgs.tally_file).toString()) const tallyHash = await getIpfsHash(tally) await fundingRound.connect(coordinator).publishTallyHash(tallyHash) console.log('Tally hash', tallyHash) // add tally results to funding round - const batchSize = Number(process.env.TALLY_BATCH_SIZE) || 8 const recipientTreeDepth = params.voteOptionTreeDepth - console.log('Adding tally result on chain in batches of', batchSize) + console.log('Adding tally result on chain in batches of', tallyBatchSize) await addTallyResultsBatch( fundingRound.connect(coordinator), recipientTreeDepth, tally, - batchSize + tallyBatchSize ) console.log('Finished adding tally results') @@ -388,7 +386,7 @@ describe('End-to-end Tests', function () { ) // Finalize round - await fundingRoundFactory.transferMatchingFunds( + await clrfund.transferMatchingFunds( tally.totalSpentVoiceCredits.spent, tally.totalSpentVoiceCredits.salt, newResultCommitment.toString(), diff --git a/contracts/package.json b/contracts/package.json index a30a7a920..3f94b73c9 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -22,7 +22,7 @@ "clean": "rm -rf cache && rm -rf build" }, "dependencies": { - "@clrfund/maci-contracts": "^1.1.7", + "@clrfund/maci-contracts": "^1.1.9", "@openzeppelin/contracts": "4.9.0", "dotenv": "^8.2.0", "solidity-rlp": "2.0.8" @@ -33,7 +33,7 @@ "@clrfund/maci-cli": "^1.1.7", "@ethereum-waffle/mock-contract": "^3.4.4", "@kleros/gtcr-encoder": "^1.4.0", - "@nomiclabs/hardhat-ethers": "^2.2.1", + "@nomiclabs/hardhat-ethers": "^2.2.3", "@nomiclabs/hardhat-etherscan": "^3.1.4", "@nomiclabs/hardhat-ganache": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.3", @@ -47,12 +47,12 @@ "eslint-config-prettier": "^8.5.0", "ethereum-waffle": "^3.4.4", "ethers": "^5.7.2", - "hardhat": "^2.12.5", + "hardhat": "^2.19.1", "hardhat-contract-sizer": "^2.6.1", "ipfs-only-hash": "^2.0.1", "solhint": "^3.3.2", "ts-generator": "^0.0.8", - "ts-node": "^8.8.1", + "ts-node": "^10.9.1", "typescript": "^4.9.3" } } diff --git a/contracts/scripts/claim.ts b/contracts/scripts/claim.ts deleted file mode 100644 index 1085d0bec..000000000 --- a/contracts/scripts/claim.ts +++ /dev/null @@ -1,47 +0,0 @@ -import fs from 'fs' -import { ethers } from 'hardhat' - -import { getEventArg } from '../utils/contracts' -import { getRecipientClaimData } from '../utils/maci' - -async function main() { - const [, , , recipient1, recipient2] = await ethers.getSigners() - const state = JSON.parse(fs.readFileSync('state.json').toString()) - const tally = JSON.parse(fs.readFileSync('tally.json').toString()) - - const fundingRound = await ethers.getContractAt( - 'FundingRound', - state.fundingRound - ) - const maciAddress = await fundingRound.maci() - const maci = await ethers.getContractAt('MACI', maciAddress) - const recipientTreeDepth = (await maci.treeDepths()).voteOptionTreeDepth - - // Claim funds - for (const recipientIndex of [1, 2]) { - const recipient = recipientIndex === 1 ? recipient1 : recipient2 - const recipientClaimData = getRecipientClaimData( - recipientIndex, - recipientTreeDepth, - tally - ) - const fundingRoundAsRecipient = fundingRound.connect(recipient) - const claimTx = await fundingRoundAsRecipient.claimFunds( - ...recipientClaimData - ) - const claimedAmount = await getEventArg( - claimTx, - fundingRound, - 'FundsClaimed', - '_amount' - ) - console.log(`Recipient ${recipientIndex} claimed ${claimedAmount} tokens.`) - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/scripts/contribute.ts b/contracts/scripts/contribute.ts deleted file mode 100644 index 2aef9b2a6..000000000 --- a/contracts/scripts/contribute.ts +++ /dev/null @@ -1,66 +0,0 @@ -import fs from 'fs' -import { ethers } from 'hardhat' -import { Keypair } from '@clrfund/common' - -import { UNIT } from '../utils/constants' -import { getEventArg } from '../utils/contracts' - -async function main() { - const [, , , , , , , , , , , , contributor1, contributor2] = - await ethers.getSigners() - const state = JSON.parse(fs.readFileSync('state.json').toString()) - const fundingRound = await ethers.getContractAt( - 'FundingRound', - state.fundingRound - ) - const tokenAddress = await fundingRound.nativeToken() - const token = await ethers.getContractAt('AnyOldERC20Token', tokenAddress) - const maciAddress = await fundingRound.maci() - const maci = await ethers.getContractAt('MACI', maciAddress) - - const contributionAmount = UNIT.mul(16).div(10) - state.contributors = {} - - for (const contributor of [contributor1, contributor2]) { - const contributorAddress = await contributor.getAddress() - const contributorKeypair = new Keypair() - const tokenAsContributor = token.connect(contributor) - await tokenAsContributor.approve(fundingRound.address, contributionAmount) - const fundingRoundAsContributor = fundingRound.connect(contributor) - const contributionTx = await fundingRoundAsContributor.contribute( - contributorKeypair.pubKey.asContractParam(), - contributionAmount - ) - const stateIndex = await getEventArg( - contributionTx, - maci, - 'SignUp', - '_stateIndex' - ) - const voiceCredits = await getEventArg( - contributionTx, - maci, - 'SignUp', - '_voiceCreditBalance' - ) - state.contributors[contributorAddress] = { - privKey: contributorKeypair.privKey.serialize(), - pubKey: contributorKeypair.pubKey.serialize(), - stateIndex: parseInt(stateIndex), - voiceCredits: voiceCredits.toString(), - } - console.log( - `Contributor ${contributorAddress} registered. State index: ${stateIndex}. Voice credits: ${voiceCredits.toString()}.` - ) - } - - // Update state file - fs.writeFileSync('state.json', JSON.stringify(state)) -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/scripts/deploy.ts b/contracts/scripts/deploy.ts deleted file mode 100644 index e036e62fe..000000000 --- a/contracts/scripts/deploy.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { ethers } from 'hardhat' -import { Contract, Wallet } from 'ethers' - -import { UNIT } from '../utils/constants' -import { - deployMaciFactory, - deployUserRegistry, - getBrightIdParams, -} from '../utils/deployment' -import { Keypair, PrivKey } from '@clrfund/common' - -// Number.MAX_SAFE_INTEGER - 1 -const challengePeriodSeconds = 9007199254740990 - -/** - * Set the coordinator address and maci public key in the funding round factory - * - * @param fundingRoundFactory funding round factory contract - * @param coordinatorAddress - * @param MaciPrivateKey - */ -async function setCoordinator( - fundingRoundFactory: Contract, - coordinatorAddress: string, - coordinatorKey?: string -) { - // Generate or use the passed in coordinator key - const privKey = coordinatorKey - ? PrivKey.unserialize(coordinatorKey) - : undefined - const keypair = new Keypair(privKey) - const coordinatorPubKey = keypair.pubKey - const serializedCoordinatorPrivKey = keypair.privKey.serialize() - const serializedCoordinatorPubKey = keypair.pubKey.serialize() - const setCoordinatorTx = await fundingRoundFactory.setCoordinator( - coordinatorAddress, - coordinatorPubKey.asContractParam() - ) - await setCoordinatorTx.wait() - console.log('coordinator address:', coordinatorAddress) - console.log('serializedCoordinatorPrivKey: ', serializedCoordinatorPrivKey) - console.log('serializedCoordinatorPubKey: ', serializedCoordinatorPubKey) -} - -async function main() { - const [deployer] = await ethers.getSigners() - console.log(`Deploying from address: ${deployer.address}`) - - const circuit = 'prod' - const maciFactory = await deployMaciFactory(deployer, circuit) - await maciFactory.deployTransaction.wait() - console.log(`MACIFactory deployed: ${maciFactory.address}`) - - const FundingRoundFactory = await ethers.getContractFactory( - 'FundingRoundFactory', - deployer - ) - const fundingRoundFactory = await FundingRoundFactory.deploy( - maciFactory.address - ) - await fundingRoundFactory.deployTransaction.wait() - console.log(`FundingRoundFactory deployed: ${fundingRoundFactory.address}`) - - const transferOwnershipTx = await maciFactory.transferOwnership( - fundingRoundFactory.address - ) - await transferOwnershipTx.wait() - - const userRegistryType = process.env.USER_REGISTRY_TYPE || 'simple' - const brightidParams = getBrightIdParams(userRegistryType) - const userRegistry: Contract = await deployUserRegistry( - userRegistryType, - deployer, - brightidParams - ) - console.log( - `User registry (${userRegistryType}) deployed: ${userRegistry.address}` - ) - - const setUserRegistryTx = await fundingRoundFactory.setUserRegistry( - userRegistry.address - ) - await setUserRegistryTx.wait() - - const recipientRegistryType = process.env.RECIPIENT_REGISTRY_TYPE || 'simple' - let recipientRegistry: Contract - if (recipientRegistryType === 'simple') { - const SimpleRecipientRegistry = await ethers.getContractFactory( - 'SimpleRecipientRegistry', - deployer - ) - recipientRegistry = await SimpleRecipientRegistry.deploy( - fundingRoundFactory.address - ) - } else if (recipientRegistryType === 'optimistic') { - const OptimisticRecipientRegistry = await ethers.getContractFactory( - 'OptimisticRecipientRegistry', - deployer - ) - recipientRegistry = await OptimisticRecipientRegistry.deploy( - UNIT.div(1000), - challengePeriodSeconds, - fundingRoundFactory.address - ) - } else { - throw new Error('unsupported recipient registry type') - } - await recipientRegistry.deployTransaction.wait() - console.log(`Recipient registry deployed: ${recipientRegistry.address}`) - - const setRecipientRegistryTx = await fundingRoundFactory.setRecipientRegistry( - recipientRegistry.address - ) - await setRecipientRegistryTx.wait() - - if (process.env.NATIVE_TOKEN_ADDRESS) { - const setTokenTx = await fundingRoundFactory.setToken( - process.env.NATIVE_TOKEN_ADDRESS - ) - await setTokenTx.wait() - console.log('Set token', process.env.NATIVE_TOKEN_ADDRESS) - } - - const coordinatorAddress = process.env.COORDINATOR_ETH_PK - ? new Wallet(process.env.COORDINATOR_ETH_PK).address - : await deployer.getAddress() - - await setCoordinator( - fundingRoundFactory, - coordinatorAddress, - process.env.COORDINATOR_PK - ) - - console.log(`Deployment complete!`) -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/scripts/deployUserRegistry.ts b/contracts/scripts/deployUserRegistry.ts deleted file mode 100644 index 2373d5451..000000000 --- a/contracts/scripts/deployUserRegistry.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ethers } from 'hardhat' -import { deployUserRegistry, getBrightIdParams } from '../utils/deployment' - -async function main() { - console.log('*******************') - console.log('Deploying a user registry!') - console.log('*******************') - const [deployer] = await ethers.getSigners() - console.log('deployer.address: ', deployer.address) - - const fundingRoundFactoryAddress = process.env.FACTORY_ADDRESS - - if (!fundingRoundFactoryAddress) { - throw new Error('Environment variable FACTORY_ADDRESS is not setup') - } - const fundingRoundFactory = await ethers.getContractAt( - 'FundingRoundFactory', - fundingRoundFactoryAddress - ) - console.log('funding round factory address ', fundingRoundFactory.address) - - const userRegistryType = process.env.USER_REGISTRY_TYPE || 'simple' - const brightidParams = getBrightIdParams(userRegistryType) - const userRegistry = await deployUserRegistry( - userRegistryType, - deployer, - brightidParams - ) - console.log( - `deployed ${userRegistryType} user registry at ${userRegistry.address}` - ) - - const setUserRegistryTx = await fundingRoundFactory.setUserRegistry( - userRegistry.address - ) - await setUserRegistryTx.wait() - console.log( - 'set user registry in funding round factory at tx hash', - setUserRegistryTx.hash - ) - - console.log('*******************') - console.log('Deploy complete!') - console.log('*******************') -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/scripts/finalize.ts b/contracts/scripts/finalize.ts deleted file mode 100644 index 12f544aad..000000000 --- a/contracts/scripts/finalize.ts +++ /dev/null @@ -1,53 +0,0 @@ -import fs from 'fs' -import { Wallet } from 'ethers' -import { ethers, network } from 'hardhat' - -async function main() { - let factoryAddress, coordinator - if (network.name === 'localhost') { - const state = JSON.parse(fs.readFileSync('state.json').toString()) - factoryAddress = state.factory - - const signers = await ethers.getSigners() - coordinator = signers[0] - } else { - factoryAddress = process.env.FACTORY_ADDRESS || '' - // default to the first account - const coordinatorEthPrivKey = - process.env.COORDINATOR_ETH_PK || - '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' - coordinator = new Wallet(coordinatorEthPrivKey, ethers.provider) - } - - const tally = JSON.parse(fs.readFileSync('tally.json').toString()) - const factory = await ethers.getContractAt( - 'FundingRoundFactory', - factoryAddress, - coordinator - ) - console.log('Funding round factory address', factory.address) - - const currentRoundAddress = await factory.getCurrentRound() - const fundingRound = await ethers.getContractAt( - 'FundingRound', - currentRoundAddress, - coordinator - ) - console.log('Current round', fundingRound.address) - - const totalSpent = parseInt(tally.totalVoiceCredits.spent) - const totalSpentSalt = tally.totalVoiceCredits.salt - const tx = await factory.transferMatchingFunds(totalSpent, totalSpentSalt) - const receipt = await tx.wait() - console.log( - 'Round finalized, totals verified. Gas used:', - receipt.gasUsed.toString() - ) -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/scripts/newRound.ts b/contracts/scripts/newRound.ts deleted file mode 100644 index 3cf0d6a43..000000000 --- a/contracts/scripts/newRound.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { ethers } from 'hardhat' -import { utils, constants } from 'ethers' - -async function main() { - console.log('*******************') - console.log('Start a new funding round!') - console.log('*******************') - const [deployer] = await ethers.getSigners() - console.log('deployer.address: ', deployer.address) - - const fundingRoundFactoryAddress = process.env.FACTORY_ADDRESS - const userRegistryType = process.env.USER_REGISTRY_TYPE - const brightIdSponsor = process.env.BRIGHTID_SPONSOR - const brightIdVerifier = process.env.BRIGHTID_VERIFIER_ADDR - - if (!fundingRoundFactoryAddress) { - throw new Error('Environment variable FACTORY_ADDRESS is not setup') - } - - if (userRegistryType === 'brightid') { - if (!brightIdSponsor) { - throw new Error('Environment variable BRIGHTID_SPONSOR is not setup') - } - if (!brightIdVerifier) { - throw new Error( - 'Environment variable BRIGHTID_VERIFIER_ADDR is not setup' - ) - } - } - - const factory = await ethers.getContractAt( - 'FundingRoundFactory', - fundingRoundFactoryAddress - ) - console.log('funding round factory address ', factory.address) - - // check if the current round is finalized before starting a new round to avoid revert - const currentRoundAddress = await factory.getCurrentRound() - if (currentRoundAddress !== constants.AddressZero) { - const currentRound = await ethers.getContractAt( - 'FundingRound', - currentRoundAddress - ) - const isFinalized = await currentRound.isFinalized() - if (!isFinalized) { - throw new Error( - 'Cannot start a new round as the current round is not finalized' - ) - } - } - - // deploy a new BrightId user registry for each new round - // to force users to link with BrightId every round - if (userRegistryType === 'brightid') { - const BrightIdUserRegistry = await ethers.getContractFactory( - 'BrightIdUserRegistry', - deployer - ) - - const userRegistry = await BrightIdUserRegistry.deploy( - utils.formatBytes32String(process.env.BRIGHTID_CONTEXT || 'clr.fund'), - brightIdVerifier, - brightIdSponsor - ) - console.log('BrightId user registry address: ', userRegistry.address) - await userRegistry.deployTransaction.wait() - - const setUserRegistryTx = await factory.setUserRegistry( - userRegistry.address - ) - await setUserRegistryTx.wait() - console.log('Set user registry in factory', setUserRegistryTx.hash) - } - - const tx = await factory.deployNewRound() - console.log('Deployed new round, tx hash: ', tx.hash) - await tx.wait() - console.log('New funding round address: ', await factory.getCurrentRound()) - - console.log('*******************') - console.log('Script complete!') - console.log('*******************') -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/scripts/tally.ts b/contracts/scripts/tally.ts new file mode 100644 index 000000000..d94868c12 --- /dev/null +++ b/contracts/scripts/tally.ts @@ -0,0 +1,235 @@ +/** + * Tally votes for the specified funding round. This task can be rerun by + * passing in additional parameters: --maci-logs, --maci-state-file + * + * Make sure to set the following environment variables in the .env file + * if not running test using the localhost network + * 1) COORDINATOR_ETH_PK - coordinator's wallet private key to interact with contracts + * 2) COORDINATOR_PK - coordinator's MACI private key to decrypt messages + * + * Sample usage: + * + * yarn hardhat tally --round-address
--start-block --network + * + * To rerun: + * + * yarn hardhat tally --round-address
--network \ + * --maci-logs --maci-state-file + */ +import { ethers, network, config } from 'hardhat' +import { Contract, Signer } from 'ethers' + +import { DEFAULT_SR_QUEUE_OPS } from '../utils/constants' +import { getIpfsHash } from '../utils/ipfs' +import { JSONFile } from '../utils/JSONFile' +import { + deployContract, + deployPoseidonLibraries, + deployMessageProcesorAndTally, +} from '../utils/deployment' +import { + getGenProofArgs, + genProofs, + proveOnChain, + addTallyResultsBatch, + mergeMaciSubtrees, +} from '../utils/maci' +import { getTalyFilePath } from '../utils/misc' + +/** + * Read variables from the environment file needed + * to run the tally script + * + * @returns data used to run the tally script + */ +function readFromEnvironment(): { + clrfund: string + batchSize: number + circuit: string + circuitDirectory: string + maciTransactionHash?: string + rapidSnarkDirectory?: string + outputDir: string + stateFile?: string + coordinatorMacisk: string + numQueueOps: number +} { + if (!process.env.CLRFUND) { + console.log('process env', process.env) + throw Error('Env. variable CLRFUND not set') + } + + if (!process.env.CIRCUIT_DIRECTORY) { + throw Error('Env. variable CIRCUIT_DIRECTORY not set') + } + + if (!process.env.COORDINATOR_MACISK) { + throw Error('Env. variable COORDINATOR_MACISK not set') + } + + return { + clrfund: process.env.CLRFUND || '', + batchSize: Number(process.env.BATCH_SIZE || '20'), + circuit: process.env.CIRCUIT_TYPE || 'micro', + circuitDirectory: process.env.CIRCUIT_DIRECTORY || '', + maciTransactionHash: process.env.MACI_TRANSACTION_HASH, + rapidSnarkDirectory: process.env.RAPIDSNARK_DIRECTORY, + outputDir: process.env.OUTPUT_DIR || './output', + stateFile: process.env.STATE_FILE, + coordinatorMacisk: process.env.COORDINATOR_MACISK || '', + numQueueOps: Number(process.env.NUM_QUEUE_OPS || DEFAULT_SR_QUEUE_OPS), + } +} + +/** + * Main tally logic + */ +async function main() { + const { + clrfund, + batchSize, + stateFile, + outputDir, + circuit, + circuitDirectory, + rapidSnarkDirectory, + maciTransactionHash, + coordinatorMacisk, + numQueueOps, + } = readFromEnvironment() + + const [coordinator] = await ethers.getSigners() + console.log('Coordinator address: ', coordinator.address) + + const providerUrl = (network.config as any).url + console.log('providerUrl', providerUrl) + + let clrfundContract: Contract + try { + clrfundContract = await ethers.getContractAt( + 'ClrFund', + clrfund, + coordinator + ) + } catch (e) { + console.error('Error accessing ClrFund Contract at', clrfund) + throw e + } + + const fundingRound = await clrfundContract.getCurrentRound() + const fundingRoundContract = await ethers.getContractAt( + 'FundingRound', + fundingRound, + coordinator + ) + console.log('Funding round contract', fundingRoundContract.address) + + const publishedTallyHash = await fundingRoundContract.tallyHash() + console.log('publishedTallyHash', publishedTallyHash) + + let tally + if (!publishedTallyHash) { + const pollIdBN = await fundingRoundContract.pollId() + const pollId = pollIdBN.toString() + console.log('PollId', pollId) + + const maciAddress = await fundingRoundContract.maci() + console.log('MACI address', maciAddress) + + // Generate proof and tally file + const genProofArgs = getGenProofArgs({ + maciAddress, + providerUrl, + pollId, + coordinatorMacisk, + maciTxHash: maciTransactionHash, + rapidSnarkDirectory, + circuitType: circuit, + circuitDirectory, + outputDir, + }) + console.log('genProofsArg', genProofArgs) + + await mergeMaciSubtrees(maciAddress, pollId, numQueueOps) + console.log('Completed tree merge') + + await genProofs(genProofArgs) + console.log('Completed genProofs') + + tally = JSONFile.read(genProofArgs.tally_file) + if (stateFile) { + // Save tally file in the state + JSONFile.update(stateFile, { tallyFile: genProofArgs.tally_file }) + } + + // deploy the MessageProcessor and Tally contracts used by proveOnChain + const { mpContract, tallyContract } = await deployMessageProcesorAndTally({ + artifactsPath: config.paths.artifacts, + ethers, + signer: coordinator, + }) + console.log('MessageProcessor', mpContract.address) + console.log('Tally Contract', tallyContract.address) + + try { + // Submit proofs to MACI contract + await proveOnChain({ + contract: maciAddress, + poll_id: pollId, + mp: mpContract.address, + tally: tallyContract.address, + //subsidy: tallyContractAddress, // TODO: make subsidy optional + proof_dir: outputDir, + }) + } catch (e) { + console.error('proveOnChain failed') + throw e + } + + // set the Tally contract address for verifying tally result on chain + const setTallyTx = await fundingRoundContract.setTally( + tallyContract.address + ) + await setTallyTx.wait() + console.log('Tally contract set in funding round') + + // Publish tally hash + const tallyHash = await getIpfsHash(tally) + await fundingRoundContract.publishTallyHash(tallyHash) + console.log(`Tally hash is ${tallyHash}`) + } else { + // read the tally.json file + console.log(`Tally hash is ${publishedTallyHash}`) + try { + console.log(`Reading tally.json file...`) + const tallyFile = getTalyFilePath(outputDir) + tally = JSONFile.read(tallyFile) + } catch (err) { + console.log('Failed to get tally file', publishedTallyHash) + throw err + } + } + + // Submit results to the funding round contract + const startIndex = await fundingRoundContract.totalTallyResults() + const total = tally.results.tally.length + console.log('Uploading tally results in batches of', batchSize) + const addTallyGas = await addTallyResultsBatch( + fundingRoundContract, + 3, + tally, + batchSize, + startIndex.toNumber(), + (processed: number) => { + console.log(`Processed ${processed} / ${total}`) + } + ) + console.log('Tally results uploaded. Gas used:', addTallyGas.toString()) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/contracts/scripts/vote.ts b/contracts/scripts/vote.ts deleted file mode 100644 index 11d51c272..000000000 --- a/contracts/scripts/vote.ts +++ /dev/null @@ -1,74 +0,0 @@ -import fs from 'fs' -import { ethers } from 'hardhat' -import { BigNumber } from 'ethers' -import { PrivKey, Keypair } from '@clrfund/common' - -import { createMessage } from '../utils/maci' - -async function main() { - const [, , , , , , , , , , , , contributor1, contributor2] = - await ethers.getSigners() - const state = JSON.parse(fs.readFileSync('state.json').toString()) - const coordinatorKeyPair = new Keypair( - PrivKey.unserialize(state.coordinatorPrivKey) - ) - - for (const contributor of [contributor1, contributor2]) { - const contributorAddress = await contributor.getAddress() - const contributorData = state.contributors[contributorAddress] - const contributorKeyPair = new Keypair( - PrivKey.unserialize(contributorData.privKey) - ) - const messages = [] - const encPubKeys = [] - let nonce = 1 - // Change key - const newContributorKeypair = new Keypair() - const [message, encPubKey] = createMessage( - contributorData.stateIndex, - contributorKeyPair, - newContributorKeypair, - coordinatorKeyPair.pubKey, - null, - null, - nonce - ) - messages.push(message.asContractParam()) - encPubKeys.push(encPubKey.asContractParam()) - nonce += 1 - // Vote - for (const recipientIndex of [1, 2]) { - const votes = BigNumber.from(contributorData.voiceCredits).div(4) - const [message, encPubKey] = createMessage( - contributorData.stateIndex, - newContributorKeypair, - null, - coordinatorKeyPair.pubKey, - recipientIndex, - votes, - nonce - ) - messages.push(message.asContractParam()) - encPubKeys.push(encPubKey.asContractParam()) - nonce += 1 - } - - const fundingRoundAsContributor = await ethers.getContractAt( - 'FundingRound', - state.fundingRound, - contributor - ) - await fundingRoundAsContributor.submitMessageBatch( - messages.reverse(), - encPubKeys.reverse() - ) - console.log(`Contributor ${contributorAddress} voted.`) - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/contracts/sh/runScriptTests.sh b/contracts/sh/runScriptTests.sh new file mode 100755 index 000000000..c3dfccd42 --- /dev/null +++ b/contracts/sh/runScriptTests.sh @@ -0,0 +1,91 @@ +#!/bin/bash +set -e + +# +# Run the hardhat scripts/tasks to simulate e2e testing +# + +# Test settings +NOW=$(date +%s) +OUTPUT_DIR="./proof_output/${NOW}" +CIRCUIT=micro +NETWORK=localhost +CIRCUIT_DIRECTORY=${CIRCUIT_DIRECTORY:-"./snark-params"} +STATE_FILE=${OUTPUT_DIR}/state.json + +# 20 mins +ROUND_DURATION=1800 + +mkdir -p ${OUTPUT_DIR} + +# A helper to extract field value from the JSON state file +# The pattern "field": "value" must be on 1 line +# Usage: extract 'clrfund' +function extract() { + val=$(cat "${STATE_FILE}" | grep "${1}" | grep -o "[^:]*$" | grep -o '[^",]*') + echo ${val} +} + +# create a ClrFund deployer +yarn hardhat new-deployer \ + --directory "${CIRCUIT_DIRECTORY}" \ + --state-file "${STATE_FILE}" \ + --network "${NETWORK}" +DEPLOYER=$(extract 'deployer') + +# create a new maci key for the coordinator +MACI_KEYPAIR=$(yarn hardhat new-maci-key) +MACI_SECRET_KEY=$(echo "${MACI_KEYPAIR}" | grep -o "macisk.*$") + +# create a new instance of ClrFund +yarn hardhat new-clrfund --deployer ${DEPLOYER} \ + --user-type simple \ + --recipient-type simple \ + --coordinator-macisk "${MACI_SECRET_KEY}" \ + --state-file "${STATE_FILE}" \ + --network "${NETWORK}" +CLRFUND=$(extract 'clrfund') + +# deploy a new funding round +yarn hardhat new-round \ + --clrfund ${CLRFUND} \ + --state-file "${STATE_FILE}" \ + --duration "${ROUND_DURATION}" \ + --network "${NETWORK}" +FUNDING_ROUND=$(extract 'fundingRound') + +yarn hardhat add-contributors --clrfund ${CLRFUND} --network "${NETWORK}" +yarn hardhat add-recipients --clrfund ${CLRFUND} --network "${NETWORK}" + +yarn hardhat contribute --state-file "${STATE_FILE}" --network "${NETWORK}" +yarn hardhat vote \ + --coordinator-macisk "${MACI_SECRET_KEY}" \ + --state-file "${STATE_FILE}" \ + --network "${NETWORK}" +yarn hardhat time-travel ${ROUND_DURATION} --network "${NETWORK}" + +# run the tally script +export CIRCUIT=micro +export CIRCUIT_DIRECTORY="${CIRCUIT_DIRECTORY}" +export CLRFUND="${CLRFUND}" +export STATE_FILE="${STATE_FILE}" +export TALLY_BATCH_SIZE=10 +export PROOF_OUTPUT_DIR="${PROOF_OUTPUT_DIR}" +export COORDINATOR_MACISK="${MACI_SECRET_KEY}" +export MACI_TRANSACTION_HASH=$(extract 'maciTxHash') +export OUTPUT_DIR="${OUTPUT_DIR}" +export NODE_OPTIONS=--max-old-space-size=4096 +yarn hardhat run scripts/tally.ts --network "${NETWORK}" + +# finalize the round +TALLY_FILE=$(extract 'tallyFile') +yarn hardhat finalize \ + --clrfund "${CLRFUND}" \ + --tally-file "${TALLY_FILE}" \ + --network "${NETWORK}" + +# claim funds +yarn hardhat claim \ + --funding-round ${FUNDING_ROUND} \ + --network "${NETWORK}" + diff --git a/contracts/tasks/addContributors.ts b/contracts/tasks/addContributors.ts new file mode 100644 index 000000000..2e4bee422 --- /dev/null +++ b/contracts/tasks/addContributors.ts @@ -0,0 +1,41 @@ +/** + * Add contributors for testing purposes + * + * Sample usage: + * + * yarn hardhat add-contributors --network \ + * --state-file + * + */ + +import { task } from 'hardhat/config' + +task('add-contributors', 'Add test contributors') + .addParam('clrfund', 'The ClrFund contract address') + .setAction(async ({ clrfund }, { ethers }) => { + const [signer, , , , , , , , , , , , contributor1, contributor2] = + await ethers.getSigners() + console.log('Adding contributors by', signer.address) + + const clrfundContract = await ethers.getContractAt( + 'ClrFund', + clrfund, + signer + ) + const userRegistryAddress = await clrfundContract.userRegistry() + console.log('User registry address', userRegistryAddress) + + const userRegistry = await ethers.getContractAt( + 'SimpleUserRegistry', + userRegistryAddress, + signer + ) + const users = [contributor1, contributor2] + let addUserTx + for (const account of users) { + addUserTx = await userRegistry.addUser(account.getAddress()) + addUserTx.wait() + } + + console.log(`Added ${users.length} contributors`) + }) diff --git a/contracts/tasks/addRecipients.ts b/contracts/tasks/addRecipients.ts new file mode 100644 index 000000000..0a42110ae --- /dev/null +++ b/contracts/tasks/addRecipients.ts @@ -0,0 +1,45 @@ +/** + * Add recipients for testing purposes + * + * Sample usage: + * + * yarn hardhat add-recipients --network --clrfund + * + */ + +import { task } from 'hardhat/config' + +task('add-recipients', 'Add test recipients') + .addParam('clrfund', 'The ClrFund contract address') + .setAction(async ({ clrfund }, { ethers }) => { + const [signer, ...recipients] = await ethers.getSigners() + console.log('Add recipients by', signer.address) + + const clrfundContract = await ethers.getContractAt( + 'ClrFund', + clrfund, + signer + ) + const recipientRegistryAddress = await clrfundContract.recipientRegistry() + console.log('Recipient registry', recipientRegistryAddress) + + const recipientRegistry = await ethers.getContractAt( + 'SimpleRecipientRegistry', + recipientRegistryAddress, + signer + ) + + for (let i = 6; i < 10; i++) { + const recipient = recipients[i] + const addRecipientTx = await recipientRegistry.addRecipient( + recipient.address, + JSON.stringify({ + name: `recipient ${i}`, + description: `recipient ${i}`, + }) + ) + addRecipientTx.wait() + } + + console.log('Added test recipients') + }) diff --git a/contracts/tasks/cancelRound.ts b/contracts/tasks/cancelRound.ts index f32aeabbe..f1cb010d5 100644 --- a/contracts/tasks/cancelRound.ts +++ b/contracts/tasks/cancelRound.ts @@ -1,17 +1,17 @@ import { task } from 'hardhat/config' task('cancel-round', 'Cancel the current round') - .addParam('factory', 'The funding round factory contract address') - .setAction(async ({ factory }, { ethers }) => { + .addParam('clrfund', 'The ClrFund contract address') + .setAction(async ({ clrfund }, { ethers }) => { const [deployer] = await ethers.getSigners() console.log('deployer', deployer.address) - const fundingRoundFactory = await ethers.getContractAt( - 'FundingRoundFactory', - factory, + const clrfundContract = await ethers.getContractAt( + 'ClrFund', + clrfund, deployer ) - const cancelTx = await fundingRoundFactory.cancelCurrentRound() + const cancelTx = await clrfundContract.cancelCurrentRound() await cancelTx.wait() console.log('Cancel transaction hash: ', cancelTx.hash) }) diff --git a/contracts/tasks/claim.ts b/contracts/tasks/claim.ts new file mode 100644 index 000000000..258e6d46b --- /dev/null +++ b/contracts/tasks/claim.ts @@ -0,0 +1,56 @@ +/** + * Claim funds. This script is mainly used by e2e testing + * + * Sample usage: + * yarn hardhat claim --funding-round --network + */ + +import { task } from 'hardhat/config' +import { getEventArg } from '../utils/contracts' +import { getRecipientClaimData } from '@clrfund/common' +import { JSONFile } from '../utils/JSONFile' +import { getTalyFilePath } from '../utils/misc' + +task('claim', 'Claim funnds for test recipients') + .addParam('fundingRound', 'The funding round contract address') + .addParam('tallyDirectory', 'The tally file directory') + .setAction(async ({ fundingRound, tallyDirectory }, { ethers }) => { + const [, , recipient0, recipient1, recipient2] = await ethers.getSigners() + const tallyFile = getTalyFilePath(tallyDirectory) + const tally = JSONFile.read(tallyFile) + + const fundingRoundContract = await ethers.getContractAt( + 'FundingRound', + fundingRound + ) + const pollAddress = await fundingRoundContract.poll() + console.log('pollAddress', pollAddress) + + const poll = await ethers.getContractAt('Poll', pollAddress) + const recipientTreeDepth = (await poll.treeDepths()).voteOptionTreeDepth + + // Claim funds + const recipients = [recipient0, recipient1, recipient2] + for (const recipientIndex of [1, 2]) { + const recipientClaimData = getRecipientClaimData( + recipientIndex, + recipientTreeDepth, + tally + ) + const fundingRoundAsRecipient = fundingRoundContract.connect( + recipients[recipientIndex] + ) + const claimTx = await fundingRoundAsRecipient.claimFunds( + ...recipientClaimData + ) + const claimedAmount = await getEventArg( + claimTx, + fundingRoundAsRecipient, + 'FundsClaimed', + '_amount' + ) + console.log( + `Recipient ${recipientIndex} claimed ${claimedAmount} tokens.` + ) + } + }) diff --git a/contracts/tasks/contribute.ts b/contracts/tasks/contribute.ts new file mode 100644 index 000000000..7417ecc78 --- /dev/null +++ b/contracts/tasks/contribute.ts @@ -0,0 +1,76 @@ +/** + * Contribute to a funding round. This script is mainly used by e2e testing + * All the input used by the script comes from the state.json file + * + * Sample usage: + * yarn hardhat contribute --state-file --network + */ + +import { task } from 'hardhat/config' +import { JSONFile } from '../utils/JSONFile' +import { Keypair } from '@clrfund/common' + +import { UNIT } from '../utils/constants' +import { getEventArg } from '../utils/contracts' + +task('contribute', 'Contribute to a funding round') + .addParam('stateFile', 'The file to store the state information') + .setAction(async ({ stateFile }, { ethers }) => { + const [, , , , , , , , , , , , contributor1, contributor2] = + await ethers.getSigners() + + const state = JSONFile.read(stateFile) + const fundingRound = await ethers.getContractAt( + 'FundingRound', + state.fundingRound + ) + const tokenAddress = await fundingRound.nativeToken() + const token = await ethers.getContractAt('AnyOldERC20Token', tokenAddress) + const maciAddress = await fundingRound.maci() + const maci = await ethers.getContractAt('MACI', maciAddress) + + const contributionAmount = UNIT.mul(16).div(10) + + state.contributors = {} + for (const contributor of [contributor1, contributor2]) { + const contributorAddress = await contributor.getAddress() + + // transfer token to contributor first + await token.transfer(contributorAddress, contributionAmount) + + const contributorKeypair = new Keypair() + const tokenAsContributor = token.connect(contributor) + await tokenAsContributor.approve(fundingRound.address, contributionAmount) + + const fundingRoundAsContributor = fundingRound.connect(contributor) + const contributionTx = await fundingRoundAsContributor.contribute( + contributorKeypair.pubKey.asContractParam(), + contributionAmount + ) + const stateIndex = await getEventArg( + contributionTx, + maci, + 'SignUp', + '_stateIndex' + ) + const voiceCredits = await getEventArg( + contributionTx, + maci, + 'SignUp', + '_voiceCreditBalance' + ) + console.log('saving states') + state.contributors[contributorAddress] = { + privKey: contributorKeypair.privKey.serialize(), + pubKey: contributorKeypair.pubKey.serialize(), + stateIndex: parseInt(stateIndex), + voiceCredits: voiceCredits.toString(), + } + console.log( + `Contributor ${contributorAddress} registered. State index: ${stateIndex}. Voice credits: ${voiceCredits.toString()}.` + ) + } + + // Update state file + JSONFile.update(stateFile, state) + }) diff --git a/contracts/tasks/finalize.ts b/contracts/tasks/finalize.ts new file mode 100644 index 000000000..62a061093 --- /dev/null +++ b/contracts/tasks/finalize.ts @@ -0,0 +1,65 @@ +/** + * Finalize a funding round + * + * Sample usage: + * yarn hardhat finalize \ + * --funding-round --network + */ + +import { task } from 'hardhat/config' +import { JSONFile } from '../utils/JSONFile' +import { genTallyResultCommitment } from '@clrfund/common' + +task('finalize', 'Finalize a funding round') + .addParam('clrfund', 'The ClrFund contract address') + .addParam('tallyFile', 'The tally file path') + .setAction(async ({ clrfund, tallyFile }, { ethers }) => { + const tally = JSONFile.read(tallyFile) + if (!tally.maci) { + throw Error('Bad tally file ' + tallyFile) + } + + const clrfundContract = await ethers.getContractAt('ClrFund', clrfund) + console.log('ClrFund address', clrfund) + + const currentRoundAddress = await clrfundContract.getCurrentRound() + const fundingRound = await ethers.getContractAt( + 'FundingRound', + currentRoundAddress + ) + console.log('Current round', fundingRound.address) + + const pollAddress = await fundingRound.poll() + const pollContract = await ethers.getContractAt('Poll', pollAddress) + console.log('Poll', pollAddress) + + const treeDepths = await pollContract.treeDepths() + console.log('voteOptionTreeDepth', treeDepths.voteOptionTreeDepth) + + const totalSpent = parseInt(tally.totalSpentVoiceCredits.spent) + const totalSpentSalt = tally.totalSpentVoiceCredits.salt + + const resultsCommitment = genTallyResultCommitment( + tally.results.tally.map((x: string) => BigInt(x)), + tally.results.salt, + treeDepths.voteOptionTreeDepth + ) + + const perVOVoiceCreditCommitment = genTallyResultCommitment( + tally.perVOSpentVoiceCredits.tally.map((x: string) => BigInt(x)), + tally.perVOSpentVoiceCredits.salt, + treeDepths.voteOptionTreeDepth + ) + + const tx = await clrfundContract.transferMatchingFunds( + totalSpent, + totalSpentSalt, + resultsCommitment, + perVOVoiceCreditCommitment + ) + const receipt = await tx.wait() + console.log( + 'Round finalized, totals verified. Gas used:', + receipt.gasUsed.toString() + ) + }) diff --git a/contracts/tasks/index.ts b/contracts/tasks/index.ts index 1988492ab..12beb4e12 100644 --- a/contracts/tasks/index.ts +++ b/contracts/tasks/index.ts @@ -1,20 +1,35 @@ +import './newDeployer' +import './newClrFund' +import './newMaciKey' +import './newRound' +import './setToken' +import './setCoordinator' +import './setUserRegistry' +import './setRecipientRegistry' +import './setMaciParameters' +import './setPollFactory' +import './cancelRound' +import './addContributors' +import './addRecipients' +import './contribute' +import './vote' +import './timeTravel' +import './finalize' +import './claim' + +import './verifyAll' import './verifyMaciFactory' -import './verifyRoundFactory' -//import './verifyRound' -//import './verifyMaci' -//import './verifyRecipientRegistry' -//import './verifyUserRegistry' -//import './verifyAll' -//import './cancelRound' -//import './evmIncreaseTime' -//import './auditTally' -//import './exportRound' -//import './mergeAllocations' -//import './setDurations' -//import './deploySponsor' -//import './loadUsers' -// TODO: make tally script work with MACI v1 -//import './tally' -//import './findStorageSlot' -//import './setStorageRoot' -//import './loadMerkleUsers' +import './verifyRound' +import './verifyMaci' +import './verifyRecipientRegistry' +import './verifyUserRegistry' +import './auditTally' +import './exportRound' +import './mergeAllocations' +import './deploySponsor' +import './loadUsers' +import './findStorageSlot' +import './setStorageRoot' +import './loadMerkleUsers' + +import './pubkey' diff --git a/contracts/tasks/newClrFund.ts b/contracts/tasks/newClrFund.ts new file mode 100644 index 000000000..54a5c18ca --- /dev/null +++ b/contracts/tasks/newClrFund.ts @@ -0,0 +1,144 @@ +/** + * Create a new instance of the ClrFund contract. + * If the coordinator ETH address is not provided, use the signer address + * If the coordinator MACI secret key is not provided, create a random one + * + * Sample usage: + * + * yarn hardhat new-clrfund --network \ + * --deployer \ + * --token \ + * [--coordinator ] \ + * [--coordinator-macisk ] \ + * [--user-type ] \ + * [--recipient-type ] + * + * + * If user registry address and recipient registry address are not provided, + * the registry types become mandatory as well as the other parameters needed + * to deploy the registries + * + * If token is not provided, a new ERC20 token will be created + */ + +import { task } from 'hardhat/config' +import { getEventArg } from '../utils/contracts' +import { challengePeriodSeconds } from '../utils/deployment' +import { JSONFile } from '../utils/JSONFile' + +task('new-clrfund', 'Deploy a new ClrFund instance') + .addParam('deployer', 'ClrFund deployer contract address') + .addOptionalParam('token', 'The token address') + .addOptionalParam('coordinator', 'The coordinator ETH address') + .addOptionalParam( + 'coordinatorMacisk', + 'The coordinator MACI serialized secret key' + ) + .addOptionalParam( + 'userType', + 'The user registry type, e.g brightid, simple, merkle, snapshot' + ) + .addOptionalParam('userRegistry', 'The user registry contract address') + .addOptionalParam('context', 'The BrightId context') + .addOptionalParam('verifier', 'The BrightId verifier address') + .addOptionalParam('sponsor', 'The BrightId sponsor contract address') + .addOptionalParam('recipientType', 'The recipient registry type') + .addOptionalParam('recipientRegistry', 'The recipient registry address') + .addOptionalParam( + 'deposit', + 'The deposit for optimistic recipient registry', + '0.01' + ) + .addOptionalParam( + 'challengePeriod', + 'The challenge period for optimistic recipient registry', + challengePeriodSeconds + ) + .addOptionalParam('stateFile', 'The state file to save the clrfund address') + .setAction( + async ( + { + deployer, + token, + coordinator, + coordinatorMacisk, + userType, + userRegistry, + context, + verifier, + sponsor, + recipientType, + recipientRegistry, + deposit, + challengePeriod, + stateFile, + }, + { run, ethers } + ) => { + const [signer] = await ethers.getSigners() + console.log(`Deploying from address: ${signer.address}`) + + const clrfundDeployer = await ethers.getContractAt( + 'ClrFundDeployer', + deployer + ) + console.log('ClrFundDeployer:', clrfundDeployer.address) + + const tx = await clrfundDeployer.deployClrFund() + const receipt = await tx.wait() + + let clrfund: string + try { + clrfund = await getEventArg( + tx, + clrfundDeployer, + 'NewInstance', + 'clrfund' + ) + console.log('ClrFund: ', clrfund) + } catch (e) { + console.log('receipt', receipt) + throw new Error( + 'Unable to get clrfund address after deployment. ' + + (e as Error).message + ) + } + + // set coordinator, use the coordinator address if available, + // otherwise use the signer address + // If the maci secret key is not provided, it will create a new key + const coordinatorAddress = coordinator ?? signer.address + await run('set-coordinator', { + clrfund, + coordinator: coordinatorAddress, + coordinatorMacisk, + stateFile, + }) + + // set token + await run('set-token', { clrfund, token }) + + // set user registry + await run('set-user-registry', { + clrfund, + type: userType, + registry: userRegistry, + context, + verifier, + sponsor, + }) + + // set recipient registry + await run('set-recipient-registry', { + clrfund, + type: recipientType, + registry: recipientRegistry, + deposit, + challengePeriod, + }) + + if (stateFile) { + JSONFile.update(stateFile, { clrfund }) + } + } + ) diff --git a/contracts/tasks/newDeployer.ts b/contracts/tasks/newDeployer.ts new file mode 100644 index 000000000..d14f499e2 --- /dev/null +++ b/contracts/tasks/newDeployer.ts @@ -0,0 +1,75 @@ +/** + * Create a new instance of the ClrFundDeployer + * + * Sample usage: + * + * yarn hardhat new-deployer --network + * + */ + +import { task } from 'hardhat/config' +import { + deployContract, + deployPoseidonLibraries, + deployMaciFactory, +} from '../utils/deployment' +import { DEFAULT_CIRCUIT } from '../utils/circuits' +import { JSONFile } from '../utils/JSONFile' + +task('new-deployer', 'Create the ClrFund deployer and its dependent contracts') + .addParam('circuit', 'The circuit type', DEFAULT_CIRCUIT) + .addParam('directory', 'The zkeys directory') + .addParam('stateFile', 'The file to save the deployer contract address') + .setAction( + async ({ circuit, directory, stateFile }, { ethers, config, run }) => { + const [signer] = await ethers.getSigners() + console.log(`Deploying from address: ${signer.address}`) + + const libraries = await deployPoseidonLibraries({ + artifactsPath: config.paths.artifacts, + signer, + ethers, + }) + console.log('Deployed Poseidons', libraries) + + const maciFactory = await deployMaciFactory({ libraries, ethers }) + console.log('Deployed MaciFactory at', maciFactory.address) + + await run('set-maci-parameters', { + maciFactory: maciFactory.address, + circuit, + directory, + }) + + const clrfundTemplate = await deployContract({ + name: 'ClrFund', + ethers, + }) + console.log('Deployed clrfundTemplate at', clrfundTemplate.address) + + const fundingRoundFactory = await deployContract({ + name: 'FundingRoundFactory', + libraries, + ethers, + }) + console.log( + 'Deployed FundingRoundFactory at', + fundingRoundFactory.address + ) + + const clrfundDeployer = await deployContract({ + name: 'ClrFundDeployer', + ethers, + contractArgs: [ + clrfundTemplate.address, + maciFactory.address, + fundingRoundFactory.address, + ], + }) + console.log('Deployed ClrfundDeployer at', clrfundDeployer.address) + + if (stateFile) { + JSONFile.update(stateFile, { deployer: clrfundDeployer.address }) + } + } + ) diff --git a/contracts/tasks/newMaciKey.ts b/contracts/tasks/newMaciKey.ts new file mode 100644 index 000000000..ff8152e25 --- /dev/null +++ b/contracts/tasks/newMaciKey.ts @@ -0,0 +1,19 @@ +/** + * Create a new MACI key pair + * + * Sample usage: + * + * yarn hardhat new-maci-key + */ + +import { task } from 'hardhat/config' +import { Keypair } from '@clrfund/maci-domainobjs' + +task('new-maci-key', 'Create a random maci key pair').setAction(async () => { + const keypair = new Keypair() + const SecretKey = keypair.privKey.serialize() + const PublicKey = keypair.pubKey.serialize() + + console.log(`SecretKey: ${SecretKey}`) + console.log(`PublicKey: ${PublicKey}`) +}) diff --git a/contracts/tasks/newRound.ts b/contracts/tasks/newRound.ts new file mode 100644 index 000000000..c58ff2b50 --- /dev/null +++ b/contracts/tasks/newRound.ts @@ -0,0 +1,77 @@ +/** + * Create a new instance of the ClrFundDeployer + * + * Sample usage: + * + * yarn hardhat new-round \ + * --network \ + * --clrfund \ + * --duration + * + */ +import { task, types } from 'hardhat/config' +import { JSONFile } from '../utils/JSONFile' + +task('new-round', 'Deploy a new funding round contract') + .addParam('clrfund', 'ClrFund contract address') + .addParam('duration', 'The funding round duration in seconds') + .addOptionalParam( + 'newBrightid', + 'Create a new BrightId user registry', + false, + types.boolean + ) + .addOptionalParam('context', 'BrightId context') + .addOptionalParam('verifier', 'BrightId verifier') + .addOptionalParam('sponsor', 'BrightId sponsor') + .addOptionalParam('stateFile', 'Save the state information in state file') + .setAction( + async ( + { clrfund, duration, newBrightid, context, verifier, sponsor, stateFile }, + { ethers, run } + ) => { + const [signer] = await ethers.getSigners() + console.log(`Deploying from address: ${signer.address}`) + + const clrfundContract = await ethers.getContractAt('ClrFund', clrfund) + + // check if the current round is finalized before starting a new round to avoid revert + const currentRoundAddress = await clrfundContract.getCurrentRound() + if (currentRoundAddress !== ethers.constants.AddressZero) { + const currentRound = await ethers.getContractAt( + 'FundingRound', + currentRoundAddress + ) + const isFinalized = await currentRound.isFinalized() + if (!isFinalized) { + throw new Error( + 'Cannot start a new round as the current round is not finalized' + ) + } + } + + if (newBrightid) { + await run('set-user-registry', { + clrfund, + type: 'brightid', + sponsor, + verifier, + context, + }) + } + + const tx = await clrfundContract.deployNewRound(duration) + await tx.wait() + const fundingRound = await clrfundContract.getCurrentRound() + console.log('New funding round address: ', fundingRound) + + if (stateFile) { + const pollId = 0 + const state = { fundingRound, pollId, maciTxHash: tx.hash } + JSONFile.update(stateFile, state) + } + console.log('*******************') + console.log('Script complete!') + console.log('*******************') + } + ) diff --git a/contracts/tasks/pubkey.ts b/contracts/tasks/pubkey.ts new file mode 100644 index 000000000..592bfd0f4 --- /dev/null +++ b/contracts/tasks/pubkey.ts @@ -0,0 +1,30 @@ +/** + * Print the serialized MACI public key given either the secret key or + * the x and y values of the public key + * + * Usage: hardhat pubkey --macisk + */ +import { utils } from 'ethers' +import { task } from 'hardhat/config' +import { PubKey, PrivKey, Keypair } from '@clrfund/maci-domainobjs' + +task('pubkey', 'Get the serialized MACI public key') + .addOptionalParam('x', 'MACI public key x') + .addOptionalParam('y', 'MACI public key y') + .addOptionalParam('macisk', 'MACI secret key') + .setAction(async ({ x, y, macisk }) => { + if (macisk) { + const keypair = new Keypair(PrivKey.unserialize(macisk)) + console.log(`Public Key: ${keypair.pubKey.serialize()}`) + } else { + if (!x || !y) { + console.error('Must provide either macisk or x y values') + return + } + const pubKey = new PubKey([BigInt(x), BigInt(y)]) + console.log(`Public Key: ${pubKey.serialize()}`) + + const id = utils.id(x + '.' + y) + console.log(`Subgraph id: ${id}`) + } + }) diff --git a/contracts/tasks/setCoordinator.ts b/contracts/tasks/setCoordinator.ts new file mode 100644 index 000000000..6d259b535 --- /dev/null +++ b/contracts/tasks/setCoordinator.ts @@ -0,0 +1,68 @@ +/** + * Set the coordinator in ClrFund, create the MACI key if not provided + * + * Sample usage: + * + * yarn hardhat set-coordinator --network \ + * --clrfund \ + * --coordinator \ + * [--coordinator-macisk ] + */ + +import { task } from 'hardhat/config' +import { PrivKey, Keypair } from '@clrfund/maci-domainobjs' +import { Contract } from 'ethers' + +/** + * Set the coordinator address and maci public key in the funding round factory + * + * @param fundingRoundFactory funding round factory contract + * @param coordinatorAddress + * @param MaciPrivateKey + */ +async function setCoordinator({ + clrfundContract, + coordinatorAddress, + coordinatorMacisk, +}: { + clrfundContract: Contract + coordinatorAddress: string + coordinatorMacisk?: string + stateFile?: string +}) { + // Generate or use the passed in coordinator key + const privKey = coordinatorMacisk + ? PrivKey.unserialize(coordinatorMacisk) + : undefined + + const keypair = new Keypair(privKey) + const coordinatorPubKey = keypair.pubKey + const SecretKey = keypair.privKey.serialize() + const PublicKey = keypair.pubKey.serialize() + + const setCoordinatorTx = await clrfundContract.setCoordinator( + coordinatorAddress, + coordinatorPubKey.asContractParam() + ) + await setCoordinatorTx.wait() + + console.log(`Coordinator address: ${coordinatorAddress}`) + console.log(`SecretKey: ${SecretKey}`) + console.log(`PublicKey: ${PublicKey}`) +} + +task('set-coordinator', 'Set the coordinator address and maci key') + .addParam('clrfund', 'The funding round factory contract address') + .addParam('coordinator', 'The coordinator ETH address') + .addOptionalParam('coordinatorMacisk', 'The coordinator maci secret key') + .setAction( + async ({ clrfund, coordinator, coordinatorMacisk }, { ethers }) => { + const clrfundContract = await ethers.getContractAt('ClrFund', clrfund) + + await setCoordinator({ + clrfundContract, + coordinatorAddress: coordinator, + coordinatorMacisk, + }) + } + ) diff --git a/contracts/tasks/setDurations.ts b/contracts/tasks/setDurations.ts deleted file mode 100644 index 29567f812..000000000 --- a/contracts/tasks/setDurations.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { task, types } from 'hardhat/config' -import { MaciParameters } from '../utils/maci' - -task('set-durations', 'Set the signup and voting durations for future rounds') - .addParam('factory', 'The funding round factory contract address') - .addParam('signup', 'Sign up duration in minutes', 60, types.int) - .addParam('voting', 'Voting duration in minutes', 10, types.int) - .setAction(async ({ factory, signup, voting }, { ethers }) => { - const signUpDuration = signup * 60 - const votingDuration = voting * 60 - - const fundingRoundFactory = await ethers.getContractAt( - 'FundingRoundFactory', - factory - ) - - const maciFactoryAddress = await fundingRoundFactory.maciFactory() - const maciFactory = await ethers.getContractAt( - 'MACIFactory', - maciFactoryAddress - ) - const maciParameters = await MaciParameters.read(maciFactory) - maciParameters.update({ - signUpDuration, - votingDuration, - }) - const setMaciParametersTx = await fundingRoundFactory.setMaciParameters( - ...maciParameters.values() - ) - await setMaciParametersTx.wait() - - const newParams = await MaciParameters.read(maciFactory) - console.log('New durations set', newParams) - }) diff --git a/contracts/tasks/setMaciParameters.ts b/contracts/tasks/setMaciParameters.ts new file mode 100644 index 000000000..ad4a3678a --- /dev/null +++ b/contracts/tasks/setMaciParameters.ts @@ -0,0 +1,33 @@ +/** + * Set the zkeys parameters in MACI factory + * Sample usage: + * + * yarn hardhat set-maci-parameters \ + * --circuit \ + * --maci-factory \ + * --network + * + * See utils/circuits.ts for the circuit type value + */ + +import { task } from 'hardhat/config' +import { DEFAULT_CIRCUIT } from '../utils/circuits' +import { MaciParameters } from '../utils/maciParameters' + +task('set-maci-parameters', 'Set the token in ClrFund') + .addParam('maciFactory', 'The MACIFactory contract address') + .addParam('circuit', 'The circuit type', DEFAULT_CIRCUIT) + .addParam('directory', 'The zkeys directory') + .setAction(async ({ maciFactory, circuit, directory }, { ethers }) => { + const factory = await ethers.getContractAt('MACIFactory', maciFactory) + + const maciParameters = await MaciParameters.fromConfig(circuit, directory) + const setMaciTx = await factory.setMaciParameters( + ...maciParameters.asContractParam() + ) + console.log('Set MACI parameters at ', setMaciTx.hash) + await setMaciTx.wait() + + const newParameters = await MaciParameters.fromContract(factory) + console.log(newParameters) + }) diff --git a/contracts/tasks/setPollFactory.ts b/contracts/tasks/setPollFactory.ts new file mode 100644 index 000000000..921872f6a --- /dev/null +++ b/contracts/tasks/setPollFactory.ts @@ -0,0 +1,39 @@ +/** + * Set the Poll factory in the MACI factory + * Usage: + * hardhat set-poll-factory \ + * --maci-factory \ + * [--poll-factory ] \ + * --network + */ +import { task } from 'hardhat/config' +import { deployPollFactory } from '../utils/deployment' + +task( + 'set-poll-factory', + 'Set (create if non-existent) the Poll factory address in the MACI factory' +) + .addParam('maciFactory', 'The MACI factory contract address') + .addOptionalParam('pollFactory', 'The poll factory contract address') + .setAction(async ({ maciFactory, pollFactory }, { ethers, config }) => { + const maciFactoryContract = await ethers.getContractAt( + 'MACIFactory', + maciFactory + ) + + let pollFactoryAddress = pollFactory + if (!pollFactoryAddress) { + const [signer] = await ethers.getSigners() + const pollFactoryContract = await deployPollFactory({ + signer, + ethers, + artifactPath: config.paths.artifacts, + }) + pollFactoryAddress = pollFactoryContract.address + } + + const tx = await maciFactoryContract.setPollFactory(pollFactoryAddress) + await tx.wait() + + console.log('Set poll factory at tx', tx.hash) + }) diff --git a/contracts/tasks/setRecipientRegistry.ts b/contracts/tasks/setRecipientRegistry.ts new file mode 100644 index 000000000..e92553933 --- /dev/null +++ b/contracts/tasks/setRecipientRegistry.ts @@ -0,0 +1,137 @@ +/** + * Set the recipient registry in the ClrFund contract. + * + * Sample usage: + * + * yarn hardhat set-recipient-registry --network \ + * --clrfund \ + * [--type ] \ + * [--registry ] \ + * [--context ] \ + * [--verifier ] \ + * [--sponsor ] + * + * Valid user registry types are simple, brightid, merkle, storage + * + * Verifier is the brightid node verifier address. + * Clrfund's brightId node is in the ethSigningAddress field from https://brightid.clr.fund + * + */ + +import { task } from 'hardhat/config' +import { BigNumber, Contract, utils } from 'ethers' +import { HardhatEthersHelpers } from '@nomiclabs/hardhat-ethers/types' +import { + deployRecipientRegistry, + challengePeriodSeconds, +} from '../utils/deployment' + +async function getDepositInUnits( + clrfundContract: Contract, + ethers: HardhatEthersHelpers, + deposit: string +): Promise { + let depositInUnits = BigNumber.from(0) + try { + const token = await clrfundContract.nativeToken() + const tokenContract = await ethers.getContractAt('ERC20', token) + const decimals = await tokenContract.decimals() + depositInUnits = utils.parseUnits(deposit, decimals) + } catch (e) { + console.log('Error formatting deposit amount ' + (e as Error).message) + console.log('Set deposit to 0') + } + + return depositInUnits +} + +/** + * Set the token address in the ClrFund contract + * + * @param clrfundContract ClrFund contract + * @param registryType The user registry type, e.g brightid, simple, merkle, snapshot + * @param registryAddress The user registry address to set in ClrFund + * @param ethers the hardhat ethers handle + */ +async function setRecipientRegistry({ + clrfundContract, + registryType, + registryAddress, + deposit, + challengePeriod, + ethers, +}: { + clrfundContract: Contract + registryType?: string + registryAddress?: string + deposit: string + challengePeriod: string + ethers: HardhatEthersHelpers +}) { + let recipientRegistryAddress = registryAddress + if (!recipientRegistryAddress) { + const recipientRegistryType = registryType || '' + const [signer] = await ethers.getSigners() + console.log(`Deploying recipient registry by: ${signer.address}`) + + const controller = clrfundContract.address + const depositInUnits = await getDepositInUnits( + clrfundContract, + ethers, + deposit + ) + const registry = await deployRecipientRegistry({ + type: recipientRegistryType, + controller, + deposit: depositInUnits, + challengePeriod, + ethers, + }) + recipientRegistryAddress = registry.address + } + + const tx = await clrfundContract.setRecipientRegistry( + recipientRegistryAddress + ) + await tx.wait() + + console.log( + `Recipient registry (${registryType}): ${recipientRegistryAddress}` + ) + console.log(`Recipient registry set at tx: ${tx.hash}`) +} + +task('set-recipient-registry', 'Set the recipient registry in ClrFund') + .addParam('clrfund', 'The ClrFund contract address') + .addOptionalParam( + 'type', + 'The recipient registry type, e.g simple, optimistic' + ) + .addOptionalParam('registry', 'The user registry contract address') + .addOptionalParam( + 'deposit', + 'The base deposit for the optimistic registry', + '0.001' + ) + .addOptionalParam( + 'challengePeriod', + 'The challenge period in seconds', + challengePeriodSeconds + ) + .setAction( + async ( + { clrfund, type, registry, deposit, challengePeriod }, + { ethers } + ) => { + const clrfundContract = await ethers.getContractAt('ClrFund', clrfund) + + await setRecipientRegistry({ + clrfundContract: clrfundContract, + registryType: type, + registryAddress: registry, + deposit, + challengePeriod, + ethers, + }) + } + ) diff --git a/contracts/tasks/setToken.ts b/contracts/tasks/setToken.ts new file mode 100644 index 000000000..f4096d175 --- /dev/null +++ b/contracts/tasks/setToken.ts @@ -0,0 +1,51 @@ +/** + * Set the native token in the ClrFund contract + * Sample usage: + * + * yarn hardhat set-token --token --clrfund --network arbitrum-goerli + */ + +import { task } from 'hardhat/config' +import { Contract, BigNumber } from 'ethers' +import { deployContract } from '../utils/deployment' +import { UNIT } from '../utils/constants' + +/** + * Set the token address in the ClrFund contract + * + * @param clrfundContract ClrFund contract + * @param tokenAddress The token address to set in ClrFund + */ +async function setToken(clrfundContract: Contract, tokenAddress: string) { + const tx = await clrfundContract.setToken(tokenAddress) + await tx.wait() + + console.log(`Token set at tx: ${tx.hash}`) +} + +task('set-token', 'Set the token in ClrFund') + .addParam('clrfund', 'The ClrFund contract address') + .addOptionalParam('token', 'The token address') + .addOptionalParam('tokenAmount', 'Initial token amount', '1000') + .setAction(async ({ clrfund, token, tokenAmount }, { ethers }) => { + const [signer] = await ethers.getSigners() + const clrfundContract = await ethers.getContractAt( + 'ClrFund', + clrfund, + signer + ) + console.log('Setting token by', signer.address) + + let tokenAddress: string = token || '' + if (!tokenAddress) { + const initialTokenSupply = BigNumber.from(tokenAmount).mul(UNIT) + const tokenContract = await deployContract({ + name: 'AnyOldERC20Token', + contractArgs: [initialTokenSupply], + ethers, + }) + tokenAddress = tokenContract.address + console.log('New token address', tokenAddress) + } + await setToken(clrfundContract, tokenAddress) + }) diff --git a/contracts/tasks/setUserRegistry.ts b/contracts/tasks/setUserRegistry.ts new file mode 100644 index 000000000..cc6db87b7 --- /dev/null +++ b/contracts/tasks/setUserRegistry.ts @@ -0,0 +1,115 @@ +/** + * Set the user registry in the ClrFund contract. + * + * Sample usage: + * + * yarn hardhat set-user-registry --network \ + * --clrfund \ + * [--type ] \ + * [--registry ] \ + * [--context ] \ + * [--verifier ] \ + * [--sponsor ] + * + * Valid user registry types are simple, brightid, merkle, storage + * + * Verifier is the brightid node verifier address. + * Clrfund's brightId node is in the ethSigningAddress field from https://brightid.clr.fund + * + * Context is the bright app id + * The context value can be found here: https://apps.brightid.org/#nodes + */ + +import { task } from 'hardhat/config' +import { Contract } from 'ethers' +import { HardhatEthersHelpers } from '@nomiclabs/hardhat-ethers/types' +import { BrightIdParams, deployUserRegistry } from '../utils/deployment' +/** + * Set the token address in the ClrFund contract + * + * @param clrfundContract ClrFund contract + * @param registryType The user registry type, e.g brightid, simple, merkle, snapshot + * @param registryAddress The user registry address to set in ClrFund + */ +async function setUserRegistry({ + clrfundContract, + registryType, + registryAddress, + brightIdParams, + ethers, +}: { + clrfundContract: Contract + registryType?: string + registryAddress?: string + brightIdParams?: BrightIdParams + ethers: HardhatEthersHelpers +}) { + let userRegistryAddress = registryAddress + if (!userRegistryAddress) { + const userRegistryType = registryType || '' + const [signer] = await ethers.getSigners() + console.log(`Deploying a user registry by: ${signer.address}`) + + const registry = await deployUserRegistry( + userRegistryType, + ethers, + brightIdParams + ) + userRegistryAddress = registry.address + } + + const tx = await clrfundContract.setUserRegistry(userRegistryAddress) + await tx.wait() + + console.log(`User registry (${registryType}): ${userRegistryAddress}`) + console.log(`User registry set at tx ${tx.hash}`) +} + +task('set-user-registry', 'Set the user registry in ClrFund') + .addParam('clrfund', 'The ClrFund contract address') + .addOptionalParam( + 'type', + 'The user registry type, e.g brightid, simple, merkle, snapshot' + ) + .addOptionalParam('registry', 'The user registry contract address') + .addOptionalParam('context', 'The BrightId context') + .addOptionalParam('verifier', 'The BrightId verifier address') + .addOptionalParam('sponsor', 'The BrightId sponsor contract address') + .setAction( + async ( + { clrfund, type, registry, context, verifier, sponsor }, + { ethers } + ) => { + const clrfundContract = await ethers.getContractAt('ClrFund', clrfund) + + let brightIdParams: BrightIdParams | undefined = undefined + + if (type === 'brightid') { + if (!context) { + throw Error('BrightId context is required') + } + + if (!verifier) { + throw Error('BrightId node verifier address is required') + } + + if (!sponsor) { + throw Error('BrightId sponsor contract address is required') + } + + brightIdParams = { + context, + verifierAddress: verifier, + sponsor, + } + } + + await setUserRegistry({ + clrfundContract: clrfundContract, + registryType: type, + registryAddress: registry, + brightIdParams, + ethers, + }) + } + ) diff --git a/contracts/tasks/tally.ts b/contracts/tasks/tally.ts deleted file mode 100644 index a058b853e..000000000 --- a/contracts/tasks/tally.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { task, types } from 'hardhat/config' -import fs from 'fs' -import { Contract, Wallet } from 'ethers' -import { genProofs, proveOnChain } from 'maci-cli' - -import { getIpfsHash } from '../utils/ipfs' -import { addTallyResultsBatch } from '../utils/maci' - -/** - * Tally votes for the specified funding round. This task can be rerun by - * passing in additional parameters: --maci-logs, --maci-state-file - * - * Make sure to set the following environment variables in the .env file - * if not running test using the localhost network - * 1) COORDINATOR_ETH_PK - coordinator's wallet private key to interact with contracts - * 2) COORDINATOR_PK - coordinator's MACI private key to decrypt messages - * - * Sample usage: - * - * yarn hardhat tally --round-address
--start-block --network - * - * To rerun: - * - * yarn hardhat tally --round-address
--network \ - * --maci-logs --maci-state-file - */ - -type TallyArgs = { - fundingRound: Contract - coordinatorMaciPrivKey: string - coordinator: Wallet - startBlock: number - numBlocksPerRequest: number - batchSize: number - logsFile: string - maciStateFile: string - providerUrl: string - voteOptionTreeDepth: number - shouldFetchLogs: boolean -} - -async function main(args: TallyArgs) { - const { - fundingRound, - coordinatorMaciPrivKey, - coordinator, - batchSize, - logsFile, - maciStateFile, - providerUrl, - voteOptionTreeDepth, - shouldFetchLogs, - startBlock, - numBlocksPerRequest, - } = args - - console.log('funding round address', fundingRound.address) - const maciAddress = await fundingRound.maci() - console.log('maci address', maciAddress) - - const publishedTallyHash = await fundingRound.tallyHash() - console.log('publishedTallyHash', publishedTallyHash) - - let tally - if (!publishedTallyHash) { - const maciAddress = await fundingRound.maci() - console.log('maci address', maciAddress) - - /* TODO: fix this, fetchLog is not available in v1 - if (shouldFetchLogs) { - // Fetch Maci logs - console.log('Fetching MACI logs from block', startBlock) - try { - await fetchLogs({ - contract: maciAddress, - eth_provider: providerUrl, - privkey: coordinatorMaciPrivKey, - start_block: startBlock, - num_blocks_per_request: numBlocksPerRequest, - output: logsFile, - }) - console.log('MACI logs generated at', logsFile) - } catch (err) { - console.log('Failed to fetchLogs', err) - throw err - } - }*/ - - // Process messages and tally votes - const results = await genProofs({ - contract: maciAddress, - eth_provider: providerUrl, - privkey: coordinatorMaciPrivKey, - tally_file: 'tally.json', - output: 'proofs.json', - logs_file: logsFile, - macistate: maciStateFile, - }) - if (!results) { - throw new Error('generation of proofs failed') - } - const { proofs } = results - tally = results.tally - - // Submit proofs to MACI contract - await proveOnChain({ - contract: maciAddress, - eth_privkey: coordinator.privateKey, - eth_provider: providerUrl, - privkey: coordinatorMaciPrivKey, - proof_file: proofs, - }) - - // Publish tally hash - const tallyHash = await getIpfsHash(tally) - await fundingRound.publishTallyHash(tallyHash) - console.log(`Tally hash is ${tallyHash}`) - } else { - // read the tally.json file - console.log(`Tally hash is ${publishedTallyHash}`) - try { - console.log(`Reading tally.json file...`) - const tallyStr = fs.readFileSync('tally.json').toString() - tally = JSON.parse(tallyStr) - } catch (err) { - console.log('Failed to get tally file', publishedTallyHash, err) - throw err - } - } - - // Submit results to the funding round contract - const startIndex = await fundingRound.totalTallyResults() - const total = tally.results.tally.length - console.log('Uploading tally results in batches of', batchSize) - const addTallyGas = await addTallyResultsBatch( - fundingRound, - voteOptionTreeDepth, - tally, - batchSize, - startIndex.toNumber(), - (processed: number) => { - console.log(`Processed ${processed} / ${total}`) - } - ) - console.log('Tally results uploaded. Gas used:', addTallyGas.toString()) -} - -task('tally', 'Tally votes for the current round') - .addParam( - 'roundAddress', - 'The funding round contract address', - '', - types.string - ) - .addParam( - 'batchSize', - 'Number of tally result to submit on chain per batch', - 20, - types.int - ) - .addParam( - 'numBlocksPerRequest', - 'The number of blocks to fetch for each get log request', - 200000, - types.int - ) - .addParam( - 'startBlock', - 'The first block containing the MACI events', - 0, - types.int - ) - .addOptionalParam('maciLogs', 'The file path containing the MACI logs') - .addOptionalParam( - 'maciStateFile', - 'The MACI state file, genProof will continue from it last run' - ) - .setAction( - async ( - { - roundAddress, - maciLogs, - maciStateFile, - batchSize, - startBlock, - numBlocksPerRequest, - }, - { ethers, network } - ) => { - let fundingRoundAddress = roundAddress - let coordinatorMaciPrivKey = process.env.COORDINATOR_PK || '' - let coordinatorEthPrivKey = - process.env.COORDINATOR_ETH_PK || process.env.WALLET_PRIVATE_KEY || '' - const providerUrl = (network.config as any).url - - if (network.name === 'localhost') { - const stateStr = fs.readFileSync('state.json').toString() - const state = JSON.parse(stateStr) - fundingRoundAddress = state.fundingRound - coordinatorMaciPrivKey = state.coordinatorPrivKey - // default to the first account - coordinatorEthPrivKey = coordinatorEthPrivKey - ? coordinatorEthPrivKey - : '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' - } else { - if (!coordinatorEthPrivKey) { - throw Error( - `Please set the environment variable COORDINATOR_ETH_PK, the coordinator's wallet private key` - ) - } - - if (!coordinatorMaciPrivKey) { - throw Error( - `Please set the environment variable COORDINATOR_PK, the coordinator's MACI private key` - ) - } - } - - if (!fundingRoundAddress) { - throw Error(`The '--round-address' parameter is required`) - } - - console.log('Funding round address: ', fundingRoundAddress) - const coordinator = new Wallet(coordinatorEthPrivKey, ethers.provider) - console.log('Coordinator address: ', coordinator.address) - - const fundingRound = await ethers.getContractAt( - 'FundingRound', - fundingRoundAddress, - coordinator - ) - - const maciAddress = await fundingRound.maci() - const maci = await ethers.getContractAt('MACI', maciAddress, coordinator) - const [, , voteOptionTreeDepth] = await maci.treeDepths() - console.log('Vote option tree depth', voteOptionTreeDepth) - - const timeMs = new Date().getTime() - const logsFile = maciLogs ? maciLogs : `maci_logs_${timeMs}.json` - - await main({ - fundingRound, - coordinatorMaciPrivKey, - coordinator, - startBlock, - numBlocksPerRequest, - batchSize, - voteOptionTreeDepth: Number(voteOptionTreeDepth), - logsFile, - providerUrl, - shouldFetchLogs: !maciLogs, - maciStateFile: maciStateFile - ? maciStateFile - : `maci_state_${timeMs}.json`, - }) - } - ) diff --git a/contracts/tasks/evmIncreaseTime.ts b/contracts/tasks/timeTravel.ts similarity index 73% rename from contracts/tasks/evmIncreaseTime.ts rename to contracts/tasks/timeTravel.ts index 7ee995fcf..6854dade3 100644 --- a/contracts/tasks/evmIncreaseTime.ts +++ b/contracts/tasks/timeTravel.ts @@ -1,9 +1,9 @@ import { task, types } from 'hardhat/config' -task('evm-increase-time', 'Increase block timestamp by seconds') +task('time-travel', 'Travel to block timestamp in seconds') .addPositionalParam( 'seconds', - 'The number of seconds to increase', + 'The number of seconds to travel to', undefined, types.int, false diff --git a/contracts/tasks/verifyAll.ts b/contracts/tasks/verifyAll.ts index bef27e87c..2b9da8ac3 100644 --- a/contracts/tasks/verifyAll.ts +++ b/contracts/tasks/verifyAll.ts @@ -8,22 +8,41 @@ type Result = { status: string } -async function verifyMaciFactory(factory: Contract, run: any): Promise { +async function verifyDeployer(deployer: Contract, run: any): Promise { try { - const address = await factory.maciFactory() + const { address } = deployer + const constructorArguments = await Promise.all([ + deployer.clrfundTemplate(), + deployer.maciFactory(), + deployer.roundFactory(), + ]) + + await run('verify:verify', { address, constructorArguments }) + return SUCCESS + } catch (error) { + return (error as Error).message + } +} + +async function verifyMaciFactory( + deployer: Contract, + run: any +): Promise { + try { + const address = await deployer.maciFactory() await run('verify-maci-factory', { address }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } -async function verifyRoundFactory(address: string, run: any): Promise { +async function verifyClrFund(clrfund: Contract, run: any): Promise { try { - await run('verify-round-factory', { address }) + await run('verify', { address: clrfund.address }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } @@ -36,7 +55,7 @@ async function verifyRecipientRegistry( await run('verify-recipient-registry', { address }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } @@ -49,7 +68,7 @@ async function verifyUserRegistry( await run('verify-user-registry', { address }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } @@ -58,7 +77,7 @@ async function verifyRound(address: string, run: any): Promise { await run('verify-round', { address }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } @@ -67,54 +86,58 @@ async function verifyMaci(maciAddress: string, run: any): Promise { await run('verify-maci', { maciAddress }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } -async function verifyStateTreeVerifier( - maciAddress: string, - run: any, - ethers: any -): Promise { +async function verifyTally(tally: Contract, run: any): Promise { try { - const rawAddress = await ethers.provider.getStorageAt(maciAddress, 1) - const address = ethers.utils.hexDataSlice(rawAddress, 12) - await run('verify:verify', { address }) + const constructorArguments = await Promise.all([tally.verifier()]) + await run('verify:verify', { address: tally.address, constructorArguments }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } -async function verifyTallyVerifier( - maciAddress: string, - run: any, - ethers: any -): Promise { +async function verifyPoll(pollContract: Contract, run: any): Promise { try { - const rawAddress = await ethers.provider.getStorageAt(maciAddress, 2) - const address = ethers.utils.hexDataSlice(rawAddress, 12) - await run('verify:verify', { address }) + const constructorArguments = await Promise.all([ + pollContract.duration(), + pollContract.maxValues(), + pollContract.treeDepths(), + pollContract.batchSizes(), + pollContract.coordinatorPubKey(), + pollContract.extContracts(), + ]) + const { address } = pollContract + await run('verify:verify', { address, constructorArguments }) return SUCCESS } catch (error) { - return error.message + return (error as Error).message } } -async function verifySponsor(address: string, run: any): Promise { +async function verifyContract( + name: string, + address: string, + run: any, + results: Result[] +) { + let result = SUCCESS try { await run('verify:verify', { address }) - return SUCCESS } catch (error) { - return error.message + result = (error as Error).message } + results.push({ name, status: result }) } async function getBrightIdSponsor( - factory: Contract, + clrfund: Contract, ethers: any ): Promise { - const userRegistryAddress = await factory.userRegistry() + const userRegistryAddress = await clrfund.userRegistry() const userRegistry = await ethers.getContractAt( 'BrightIdUserRegistry', userRegistryAddress @@ -131,42 +154,89 @@ async function getBrightIdSponsor( * Verifies all the contracts created for clrfund app */ task('verify-all', 'Verify all clrfund contracts') - .addPositionalParam('address', 'Funding round factory contract address') - .setAction(async ({ address }, { run, ethers }) => { - const factory = await ethers.getContractAt('FundingRoundFactory', address) - const roundAddress = await factory.getCurrentRound() + .addParam('deployer', 'ClrFundDeployer contract address') + .addOptionalParam('clrfund', 'ClrFund contract address') + .setAction(async ({ deployer, clrfund }, { run, ethers }) => { + const deployerContract = await ethers.getContractAt( + 'ClrFundDeployer', + deployer + ) + const maciFactoryAddress = await deployerContract.maciFactory() + const maciFactory = await ethers.getContractAt( + 'MACIFactory', + maciFactoryAddress + ) const results: Result[] = [] - let status = await verifyMaciFactory(factory, run) + let status = await verifyDeployer(deployerContract, run) + results.push({ name: 'ClrFund Deployer', status }) + status = await verifyMaciFactory(deployerContract, run) results.push({ name: 'Maci facotry', status }) - status = await verifyRoundFactory(address, run) - results.push({ name: 'Funding round factory', status }) - status = await verifyRecipientRegistry(factory, run) - results.push({ name: 'Recipient registry', status }) - status = await verifyUserRegistry(factory, run) - results.push({ name: 'User factory', status }) - - const sponsor = await getBrightIdSponsor(factory, ethers) - if (sponsor) { - status = await verifySponsor(sponsor, run) - results.push({ name: 'BrightId sponsor', status }) - } - - if (roundAddress !== ethers.constants.AddressZero) { - const round = await ethers.getContractAt('FundingRound', roundAddress) - const maciAddress = await round.maci() - status = await verifyRound(roundAddress, run) - results.push({ name: 'Funding round', status }) - status = await verifyMaci(maciAddress, run) - results.push({ name: 'MACI', status }) - status = await verifyStateTreeVerifier(maciAddress, run, ethers) - results.push({ name: 'BatchUpdateStateTreeVerifier', status }) - - status = await verifyTallyVerifier(maciAddress, run, ethers) - results.push({ name: 'QuadVoteTallyVerifier', status }) + if (clrfund) { + const clrfundContract = await ethers.getContractAt('ClrFund', clrfund) + status = await verifyClrFund(clrfundContract, run) + results.push({ name: 'ClrFund', status }) + status = await verifyRecipientRegistry(clrfundContract, run) + results.push({ name: 'Recipient registry', status }) + status = await verifyUserRegistry(clrfundContract, run) + results.push({ name: 'User factory', status }) + const sponsor = await getBrightIdSponsor(clrfundContract, ethers) + if (sponsor) { + await verifyContract('Sponsor', sponsor, run, results) + } + + const roundAddress = await clrfundContract.getCurrentRound() + if (roundAddress !== ethers.constants.AddressZero) { + const round = await ethers.getContractAt('FundingRound', roundAddress) + const maciAddress = await round.maci() + status = await verifyRound(roundAddress, run) + results.push({ name: 'Funding round', status }) + status = await verifyMaci(maciAddress, run) + results.push({ name: 'MACI', status }) + + const poll = await round.poll() + if (poll !== ethers.constants.AddressZero) { + const pollContract = await ethers.getContractAt('Poll', poll) + status = await verifyPoll(pollContract, run) + results.push({ name: 'Poll', status }) + } + + const tally = await round.tally() + if (tally !== ethers.constants.AddressZero) { + const tallyContract = await ethers.getContractAt('Tally', tally) + status = await verifyTally(tallyContract, run) + results.push({ name: 'Tally', status }) + } + + await verifyContract( + 'TopupToken', + await round.topupToken(), + run, + results + ) + } } + await verifyContract( + 'clrfundTemplate', + await deployerContract.clrfundTemplate(), + run, + results + ) + await verifyContract( + 'VkRegistry', + await maciFactory.vkRegistry(), + run, + results + ) + await verifyContract( + 'PollFactory', + await maciFactory.pollFactory(), + run, + results + ) + results.forEach(({ name, status }, i) => { const color = status === SUCCESS ? '32' : '31' console.log(`${i} ${name}: \x1b[%sm%s\x1b[0m`, color, status) diff --git a/contracts/tasks/verifyMaciFactory.ts b/contracts/tasks/verifyMaciFactory.ts index 31761119f..54fdcf86f 100644 --- a/contracts/tasks/verifyMaciFactory.ts +++ b/contracts/tasks/verifyMaciFactory.ts @@ -1,53 +1,12 @@ import { task } from 'hardhat/config' import { Contract } from 'ethers' -type ProvidedArgs = { - signupDuration?: string - votingDuration?: string -} - -async function getConstructorArguments( - maciFactory: Contract, - provided: ProvidedArgs = {} -): Promise { - const signupPromise = provided.signupDuration - ? Promise.resolve(provided.signupDuration) - : maciFactory.signUpDuration() - - const votingPromise = provided.votingDuration - ? Promise.resolve(provided.votingDuration) - : maciFactory.votingDuration() - - const [ - treeDepths, - batchSizes, - batchUstVerifier, - qvtVerifier, - signUpDuration, - votingDuration, - ] = await Promise.all([ - maciFactory.treeDepths(), - maciFactory.batchSizes(), - maciFactory.batchUstVerifier(), - maciFactory.qvtVerifier(), - signupPromise, - votingPromise, +async function getConstructorArguments(maciFactory: Contract): Promise { + const result = await Promise.all([ + maciFactory.vkRegistry(), + maciFactory.pollFactory(), ]) - - const [stateTreeDepth, messageTreeDepth, voteOptionTreeDepth] = treeDepths - const [tallyBatchSize, messageBatchSize] = batchSizes - - return [ - stateTreeDepth, - messageTreeDepth, - voteOptionTreeDepth, - tallyBatchSize, - messageBatchSize, - batchUstVerifier, - qvtVerifier, - signUpDuration, - votingDuration, - ] + return result } /** @@ -57,21 +16,14 @@ async function getConstructorArguments( */ task('verify-maci-factory', 'Verify a MACI factory contract') .addParam('address', 'MACI factory contract address') - .addOptionalParam('signupDuration', 'Signup duration in seconds') - .addOptionalParam('votingDuration', 'Voting duration in seconds') - .setAction( - async ({ address, signupDuration, votingDuration }, { run, ethers }) => { - const maciFactory = await ethers.getContractAt('MACIFactory', address) + .setAction(async ({ address }, { run, ethers }) => { + const maciFactory = await ethers.getContractAt('MACIFactory', address) - const constructorArguments = await getConstructorArguments(maciFactory, { - signupDuration, - votingDuration, - }) - console.log('Constructor arguments', constructorArguments) + const constructorArguments = await getConstructorArguments(maciFactory) + console.log('Constructor arguments', constructorArguments) - await run('verify:verify', { - address, - constructorArguments, - }) - } - ) + await run('verify:verify', { + address, + constructorArguments, + }) + }) diff --git a/contracts/tasks/verifyRoundFactory.ts b/contracts/tasks/verifyRoundFactory.ts deleted file mode 100644 index ccc768361..000000000 --- a/contracts/tasks/verifyRoundFactory.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { task } from 'hardhat/config' - -task('verify-round-factory', 'Verify a funding round factory contract') - .addPositionalParam('address', 'Funding round factory contract address') - .setAction(async ({ address }, { run, ethers }) => { - const fundingRoundFactory = await ethers.getContractAt( - 'FundingRoundFactory', - address - ) - const maciFactoryAddress = await fundingRoundFactory.maciFactory() - - const constructorArguments = [maciFactoryAddress] - - console.log('Constructor arguments', constructorArguments) - - await run('verify:verify', { - address, - constructorArguments, - }) - }) diff --git a/contracts/tasks/vote.ts b/contracts/tasks/vote.ts new file mode 100644 index 000000000..0e423b032 --- /dev/null +++ b/contracts/tasks/vote.ts @@ -0,0 +1,84 @@ +/** + * Contribute to a funding round. This script is mainly used by e2e testing + * All the input used by the script comes from the state.json file + * + * Sample usage: + * yarn hardhat contribute \ + * --coordinator-macisk \ + * --state-file + * --network + */ + +import { task } from 'hardhat/config' +import { JSONFile } from '../utils/JSONFile' +import { PrivKey, Keypair, createMessage } from '@clrfund/common' +import { BigNumber } from 'ethers' + +task('vote', 'Cast votes for test users') + .addParam('coordinatorMacisk', 'The coordinator MACI secret key') + .addParam('stateFile', 'The file to store the state information') + .setAction(async ({ coordinatorMacisk, stateFile }, { ethers }) => { + const [, , , , , , , , , , , , contributor1, contributor2] = + await ethers.getSigners() + + const state = JSONFile.read(stateFile) + const coordinatorKeyPair = new Keypair( + PrivKey.unserialize(coordinatorMacisk) + ) + + const pollId = state.pollId + for (const contributor of [contributor1, contributor2]) { + const contributorAddress = await contributor.getAddress() + const contributorData = state.contributors[contributorAddress] + const contributorKeyPair = new Keypair( + PrivKey.unserialize(contributorData.privKey) + ) + + const messages: { msgType: any; data: string[] }[] = [] + const encPubKeys: any[] = [] + let nonce = 1 + // Change key + const newContributorKeypair = new Keypair() + const [message, encPubKey] = createMessage( + contributorData.stateIndex, + contributorKeyPair, + newContributorKeypair, + coordinatorKeyPair.pubKey, + null, + null, + nonce, + pollId + ) + messages.push(message.asContractParam()) + encPubKeys.push(encPubKey.asContractParam()) + nonce += 1 + // Vote + for (const recipientIndex of [1, 2]) { + const votes = BigNumber.from(contributorData.voiceCredits).div(4) + const [message, encPubKey] = createMessage( + contributorData.stateIndex, + newContributorKeypair, + null, + coordinatorKeyPair.pubKey, + recipientIndex, + votes, + nonce, + pollId + ) + messages.push(message.asContractParam()) + encPubKeys.push(encPubKey.asContractParam()) + nonce += 1 + } + + const fundingRoundAsContributor = await ethers.getContractAt( + 'FundingRound', + state.fundingRound, + contributor + ) + await fundingRoundAsContributor.submitMessageBatch( + messages.reverse(), + encPubKeys.reverse() + ) + console.log(`Contributor ${contributorAddress} voted.`) + } + }) diff --git a/contracts/tests/deployer.ts b/contracts/tests/deployer.ts index e86b06356..1f708fade 100644 --- a/contracts/tests/deployer.ts +++ b/contracts/tests/deployer.ts @@ -1,7 +1,7 @@ -import { ethers, waffle } from 'hardhat' +import { ethers, waffle, config } from 'hardhat' import { use, expect } from 'chai' import { solidity } from 'ethereum-waffle' -import { Signer, Contract, ContractTransaction } from 'ethers' +import { Signer, Contract, ContractTransaction, constants } from 'ethers' import { genRandomSalt } from 'maci-crypto' import { Keypair } from '@clrfund/common' @@ -9,29 +9,37 @@ import { ZERO_ADDRESS, UNIT } from '../utils/constants' import { getGasUsage, getEventArg } from '../utils/contracts' import { deployContract, - deployContractWithLinkedLibraries, - deployMaciFactory, deployPoseidonLibraries, + deployMaciFactory, } from '../utils/deployment' -import { MaciParameters } from '../utils/maci' +import { MaciParameters } from '../utils/maciParameters' +import { DEFAULT_CIRCUIT } from '../utils/circuits' use(solidity) const roundDuration = 10000 -const circuit = 'micro' +const circuit = DEFAULT_CIRCUIT async function setRoundTally( clrfund: Contract, coordinator: Signer ): Promise { - const libraries = await deployPoseidonLibraries(coordinator) - const verifier = await deployContract(coordinator, 'MockVerifier') - const tally = await deployContractWithLinkedLibraries( - coordinator, - 'Tally', + const libraries = await deployPoseidonLibraries({ + artifactsPath: config.paths.artifacts, + ethers, + }) + const verifier = await deployContract({ + name: 'MockVerifier', + ethers, + signer: coordinator, + }) + const tally = await deployContract({ + name: 'Tally', libraries, - [verifier.address] - ) + contractArgs: [verifier.address], + signer: coordinator, + ethers, + }) const roundAddress = await clrfund.getCurrentRound() const round = await ethers.getContractAt( 'FundingRound', @@ -49,7 +57,7 @@ describe('Clr fund deployer', () => { let userRegistry: Contract let recipientRegistry: Contract let factoryTemplate: Contract - let factory: Contract + let clrfund: Contract let clrFundDeployer: Contract let token: Contract const coordinatorPubKey = new Keypair().pubKey.asContractParam() @@ -57,35 +65,55 @@ describe('Clr fund deployer', () => { beforeEach(async () => { if (!poseidonContracts) { - poseidonContracts = await deployPoseidonLibraries(deployer) + poseidonContracts = await deployPoseidonLibraries({ + artifactsPath: config.paths.artifacts, + ethers, + }) } - maciFactory = await deployMaciFactory(deployer, poseidonContracts) + + maciFactory = await deployMaciFactory({ + libraries: poseidonContracts, + signer: deployer, + ethers, + }) const params = MaciParameters.mock(circuit) await maciFactory.setMaciParameters(...params.asContractParam()) - factoryTemplate = await deployContractWithLinkedLibraries( - deployer, - 'ClrFund', - poseidonContracts - ) + factoryTemplate = await deployContract({ + name: 'ClrFund', + ethers, + signer: deployer, + }) expect(factoryTemplate.address).to.properAddress expect(await getGasUsage(factoryTemplate.deployTransaction)).lessThan( 5400000 ) - clrFundDeployer = await deployContract(deployer, 'ClrFundDeployer', [ - factoryTemplate.address, - ]) + const roundFactory = await deployContract({ + name: 'FundingRoundFactory', + libraries: poseidonContracts, + ethers, + }) + expect(await getGasUsage(roundFactory.deployTransaction)).lessThan(4000000) + + clrFundDeployer = await deployContract({ + name: 'ClrFundDeployer', + contractArgs: [ + factoryTemplate.address, + maciFactory.address, + roundFactory.address, + ], + ethers, + signer: deployer, + }) expect(clrFundDeployer.address).to.properAddress expect(await getGasUsage(clrFundDeployer.deployTransaction)).lessThan( 5400000 ) - const newInstanceTx = await clrFundDeployer.deployClrFund( - maciFactory.address - ) + const newInstanceTx = await clrFundDeployer.deployClrFund() const instanceAddress = await getEventArg( newInstanceTx, clrFundDeployer, @@ -93,7 +121,7 @@ describe('Clr fund deployer', () => { 'clrfund' ) - factory = await ethers.getContractAt('ClrFund', instanceAddress, deployer) + clrfund = await ethers.getContractAt('ClrFund', instanceAddress, deployer) const SimpleUserRegistry = await ethers.getContractFactory( 'SimpleUserRegistry', @@ -104,7 +132,7 @@ describe('Clr fund deployer', () => { 'SimpleRecipientRegistry', deployer ) - recipientRegistry = await SimpleRecipientRegistry.deploy(factory.address) + recipientRegistry = await SimpleRecipientRegistry.deploy(clrfund.address) // Deploy token contract and transfer tokens to contributor @@ -116,85 +144,86 @@ describe('Clr fund deployer', () => { }) it('can only be initialized once', async () => { - await expect(factory.init(maciFactory.address)).to.be.revertedWith( - 'Initializable: contract is already initialized' - ) + const dummyRoundFactory = constants.AddressZero + await expect( + clrfund.init(maciFactory.address, dummyRoundFactory) + ).to.be.revertedWith('Initializable: contract is already initialized') }) it('can register with the subgraph', async () => { await expect( clrFundDeployer.registerInstance( - factory.address, + clrfund.address, '{name:dead,title:beef}' ) ) .to.emit(clrFundDeployer, 'Register') - .withArgs(factory.address, '{name:dead,title:beef}') + .withArgs(clrfund.address, '{name:dead,title:beef}') }) it('cannot register with the subgraph twice', async () => { await expect( clrFundDeployer.registerInstance( - factory.address, + clrfund.address, '{name:dead,title:beef}' ) ) .to.emit(clrFundDeployer, 'Register') - .withArgs(factory.address, '{name:dead,title:beef}') + .withArgs(clrfund.address, '{name:dead,title:beef}') await expect( clrFundDeployer.registerInstance( - factory.address, + clrfund.address, '{name:dead,title:beef}' ) ).to.be.revertedWith('ClrFundAlreadyRegistered') }) - it('initializes factory', async () => { - expect(await factory.coordinator()).to.equal(ZERO_ADDRESS) - expect(await factory.nativeToken()).to.equal(ZERO_ADDRESS) - expect(await factory.maciFactory()).to.equal(maciFactory.address) - expect(await factory.userRegistry()).to.equal(ZERO_ADDRESS) - expect(await factory.recipientRegistry()).to.equal(ZERO_ADDRESS) + it('initializes clrfund', async () => { + expect(await clrfund.coordinator()).to.equal(ZERO_ADDRESS) + expect(await clrfund.nativeToken()).to.equal(ZERO_ADDRESS) + expect(await clrfund.maciFactory()).to.equal(maciFactory.address) + expect(await clrfund.userRegistry()).to.equal(ZERO_ADDRESS) + expect(await clrfund.recipientRegistry()).to.equal(ZERO_ADDRESS) }) it('transfers ownership to another address', async () => { - await expect(factory.transferOwnership(coordinator.address)) - .to.emit(factory, 'OwnershipTransferred') + await expect(clrfund.transferOwnership(coordinator.address)) + .to.emit(clrfund, 'OwnershipTransferred') .withArgs(deployer.address, coordinator.address) }) describe('changing user registry', () => { it('allows owner to set user registry', async () => { - await factory.setUserRegistry(userRegistry.address) - expect(await factory.userRegistry()).to.equal(userRegistry.address) + await clrfund.setUserRegistry(userRegistry.address) + expect(await clrfund.userRegistry()).to.equal(userRegistry.address) }) it('allows only owner to set user registry', async () => { await expect( - factory.connect(contributor).setUserRegistry(userRegistry.address) + clrfund.connect(contributor).setUserRegistry(userRegistry.address) ).to.be.revertedWith('Ownable: caller is not the owner') }) it('allows owner to change recipient registry', async () => { - await factory.setRecipientRegistry(recipientRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) const SimpleUserRegistry = await ethers.getContractFactory( 'SimpleUserRegistry', deployer ) const anotherUserRegistry = await SimpleUserRegistry.deploy() - await factory.setUserRegistry(anotherUserRegistry.address) - expect(await factory.userRegistry()).to.equal(anotherUserRegistry.address) + await clrfund.setUserRegistry(anotherUserRegistry.address) + expect(await clrfund.userRegistry()).to.equal(anotherUserRegistry.address) }) }) describe('changing recipient registry', () => { it('allows owner to set recipient registry', async () => { - await factory.setCoordinator(coordinator.address, coordinatorPubKey) - await factory.setRecipientRegistry(recipientRegistry.address) - expect(await factory.recipientRegistry()).to.equal( + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setRecipientRegistry(recipientRegistry.address) + expect(await clrfund.recipientRegistry()).to.equal( recipientRegistry.address ) - expect(await recipientRegistry.controller()).to.equal(factory.address) + expect(await recipientRegistry.controller()).to.equal(clrfund.address) const params = MaciParameters.mock(circuit) expect(await recipientRegistry.maxRecipients()).to.equal( 5 ** params.voteOptionTreeDepth @@ -203,23 +232,23 @@ describe('Clr fund deployer', () => { it('allows only owner to set recipient registry', async () => { await expect( - factory + clrfund .connect(contributor) .setRecipientRegistry(recipientRegistry.address) ).to.be.revertedWith('Ownable: caller is not the owner') }) it('allows owner to change recipient registry', async () => { - await factory.setRecipientRegistry(recipientRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) const SimpleRecipientRegistry = await ethers.getContractFactory( 'SimpleRecipientRegistry', deployer ) const anotherRecipientRegistry = await SimpleRecipientRegistry.deploy( - factory.address + clrfund.address ) - await factory.setRecipientRegistry(anotherRecipientRegistry.address) - expect(await factory.recipientRegistry()).to.equal( + await clrfund.setRecipientRegistry(anotherRecipientRegistry.address) + expect(await clrfund.recipientRegistry()).to.equal( anotherRecipientRegistry.address ) }) @@ -227,71 +256,71 @@ describe('Clr fund deployer', () => { describe('managing funding sources', () => { it('allows owner to add funding source', async () => { - await expect(factory.addFundingSource(contributor.address)) - .to.emit(factory, 'FundingSourceAdded') + await expect(clrfund.addFundingSource(contributor.address)) + .to.emit(clrfund, 'FundingSourceAdded') .withArgs(contributor.address) }) it('allows only owner to add funding source', async () => { await expect( - factory.connect(contributor).addFundingSource(contributor.address) + clrfund.connect(contributor).addFundingSource(contributor.address) ).to.be.revertedWith('Ownable: caller is not the owner') }) it('reverts if funding source is already added', async () => { - await factory.addFundingSource(contributor.address) + await clrfund.addFundingSource(contributor.address) await expect( - factory.addFundingSource(contributor.address) + clrfund.addFundingSource(contributor.address) ).to.be.revertedWith('FundingSourceAlreadyAdded') }) it('allows owner to remove funding source', async () => { - await factory.addFundingSource(contributor.address) - await expect(factory.removeFundingSource(contributor.address)) - .to.emit(factory, 'FundingSourceRemoved') + await clrfund.addFundingSource(contributor.address) + await expect(clrfund.removeFundingSource(contributor.address)) + .to.emit(clrfund, 'FundingSourceRemoved') .withArgs(contributor.address) }) it('allows only owner to remove funding source', async () => { - await factory.addFundingSource(contributor.address) + await clrfund.addFundingSource(contributor.address) await expect( - factory.connect(contributor).removeFundingSource(contributor.address) + clrfund.connect(contributor).removeFundingSource(contributor.address) ).to.be.revertedWith('Ownable: caller is not the owner') }) it('reverts if funding source is already removed', async () => { - await factory.addFundingSource(contributor.address) - await factory.removeFundingSource(contributor.address) + await clrfund.addFundingSource(contributor.address) + await clrfund.removeFundingSource(contributor.address) await expect( - factory.removeFundingSource(contributor.address) + clrfund.removeFundingSource(contributor.address) ).to.be.revertedWith('FundingSourceNotFound') }) }) it('allows direct contributions to the matching pool', async () => { const contributionAmount = UNIT.mul(10) - await factory.setToken(token.address) + await clrfund.setToken(token.address) await expect( - token.connect(contributor).transfer(factory.address, contributionAmount) + token.connect(contributor).transfer(clrfund.address, contributionAmount) ) .to.emit(token, 'Transfer') - .withArgs(contributor.address, factory.address, contributionAmount) - expect(await token.balanceOf(factory.address)).to.equal(contributionAmount) + .withArgs(contributor.address, clrfund.address, contributionAmount) + expect(await token.balanceOf(clrfund.address)).to.equal(contributionAmount) }) describe('deploying funding round', () => { it('deploys funding round', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) - const deployed = factory.deployNewRound(roundDuration) - await expect(deployed).to.emit(factory, 'RoundStarted') + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) + const deployed = clrfund.deployNewRound(roundDuration) + await expect(deployed).to.emit(clrfund, 'RoundStarted') const deployTx = await deployed // TODO: fix gas usage for deployNewRound() expect(await getGasUsage(deployTx)).lessThan(20000000) - const fundingRoundAddress = await factory.getCurrentRound() + const fundingRoundAddress = await clrfund.getCurrentRound() expect(fundingRoundAddress).to.properAddress expect(fundingRoundAddress).to.not.equal(ZERO_ADDRESS) @@ -299,7 +328,7 @@ describe('Clr fund deployer', () => { 'FundingRound', fundingRoundAddress ) - expect(await fundingRound.owner()).to.equal(factory.address) + expect(await fundingRound.owner()).to.equal(clrfund.address) expect(await fundingRound.nativeToken()).to.equal(token.address) const maciAddress = await getEventArg( @@ -324,79 +353,79 @@ describe('Clr fund deployer', () => { }) it('reverts if user registry is not set', async () => { - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) - await expect(factory.deployNewRound(roundDuration)).to.be.revertedWith( + await expect(clrfund.deployNewRound(roundDuration)).to.be.revertedWith( 'NoUserRegistry' ) }) it('reverts if recipient registry is not set', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) - await expect(factory.deployNewRound(roundDuration)).to.be.revertedWith( + await expect(clrfund.deployNewRound(roundDuration)).to.be.revertedWith( 'NoRecipientRegistry' ) }) it('reverts if native token is not set', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) - await expect(factory.deployNewRound(roundDuration)).to.be.revertedWith( + await expect(clrfund.deployNewRound(roundDuration)).to.be.revertedWith( 'NoToken' ) }) it('reverts if coordinator is not set', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await expect(factory.deployNewRound(roundDuration)).to.be.revertedWith( + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await expect(clrfund.deployNewRound(roundDuration)).to.be.revertedWith( 'NoCoordinator' ) }) it('reverts if current round is not finalized', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) - await factory.deployNewRound(roundDuration) - await expect(factory.deployNewRound(roundDuration)).to.be.revertedWith( + await clrfund.deployNewRound(roundDuration) + await expect(clrfund.deployNewRound(roundDuration)).to.be.revertedWith( 'NotFinalized' ) }) it('deploys new funding round after previous round has been finalized', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) - - await factory.deployNewRound(roundDuration) - await factory.cancelCurrentRound() - await expect(factory.deployNewRound(roundDuration)).to.emit( - factory, + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) + + await clrfund.deployNewRound(roundDuration) + await clrfund.cancelCurrentRound() + await expect(clrfund.deployNewRound(roundDuration)).to.emit( + clrfund, 'RoundStarted' ) }) it('only owner can deploy funding round', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) - const factoryAsContributor = factory.connect(contributor) + const clrfundAsContributor = clrfund.connect(contributor) await expect( - factoryAsContributor.deployNewRound(roundDuration) + clrfundAsContributor.deployNewRound(roundDuration) ).to.be.revertedWith('Ownable: caller is not the owner') }) }) @@ -409,28 +438,28 @@ describe('Clr fund deployer', () => { const perVOVoiceCreditCommitment = genRandomSalt().toString() beforeEach(async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) }) it('returns the amount of available matching funding', async () => { - await factory.addFundingSource(deployer.address) - await factory.addFundingSource(contributor.address) + await clrfund.addFundingSource(deployer.address) + await clrfund.addFundingSource(contributor.address) // Allowance is more than available balance - await token.connect(deployer).approve(factory.address, contributionAmount) + await token.connect(deployer).approve(clrfund.address, contributionAmount) // Allowance is less than available balance await token .connect(contributor) - .approve(factory.address, contributionAmount) + .approve(clrfund.address, contributionAmount) // Direct contribution await token .connect(contributor) - .transfer(factory.address, contributionAmount) + .transfer(clrfund.address, contributionAmount) - await factory.deployNewRound(roundDuration) - expect(await factory.getMatchingFunds(token.address)).to.equal( + await clrfund.deployNewRound(roundDuration) + expect(await clrfund.getMatchingFunds(token.address)).to.equal( contributionAmount.mul(2) ) }) @@ -438,12 +467,12 @@ describe('Clr fund deployer', () => { it('allows owner to finalize round', async () => { await token .connect(contributor) - .transfer(factory.address, contributionAmount) - await factory.deployNewRound(roundDuration) + .transfer(clrfund.address, contributionAmount) + await clrfund.deployNewRound(roundDuration) await provider.send('evm_increaseTime', [roundDuration]) - await setRoundTally(factory, coordinator) + await setRoundTally(clrfund, coordinator) await expect( - factory.transferMatchingFunds( + clrfund.transferMatchingFunds( totalSpent, totalSpentSalt, resultsCommitment, @@ -453,11 +482,11 @@ describe('Clr fund deployer', () => { }) it('allows owner to finalize round even without matching funds', async () => { - await factory.deployNewRound(roundDuration) + await clrfund.deployNewRound(roundDuration) await provider.send('evm_increaseTime', [roundDuration]) - await setRoundTally(factory, coordinator) + await setRoundTally(clrfund, coordinator) await expect( - factory.transferMatchingFunds( + clrfund.transferMatchingFunds( totalSpent, totalSpentSalt, resultsCommitment, @@ -467,14 +496,14 @@ describe('Clr fund deployer', () => { }) it('pulls funds from funding source', async () => { - await factory.addFundingSource(contributor.address) - token.connect(contributor).approve(factory.address, contributionAmount) - await factory.addFundingSource(deployer.address) // Doesn't have tokens - await factory.deployNewRound(roundDuration) + await clrfund.addFundingSource(contributor.address) + token.connect(contributor).approve(clrfund.address, contributionAmount) + await clrfund.addFundingSource(deployer.address) // Doesn't have tokens + await clrfund.deployNewRound(roundDuration) await provider.send('evm_increaseTime', [roundDuration]) - await setRoundTally(factory, coordinator) + await setRoundTally(clrfund, coordinator) await expect( - factory.transferMatchingFunds( + clrfund.transferMatchingFunds( totalSpent, totalSpentSalt, resultsCommitment, @@ -484,15 +513,15 @@ describe('Clr fund deployer', () => { }) it('pulls funds from funding source if allowance is greater than balance', async () => { - await factory.addFundingSource(contributor.address) + await clrfund.addFundingSource(contributor.address) token .connect(contributor) - .approve(factory.address, contributionAmount.mul(2)) - await factory.deployNewRound(roundDuration) + .approve(clrfund.address, contributionAmount.mul(2)) + await clrfund.deployNewRound(roundDuration) await provider.send('evm_increaseTime', [roundDuration]) - await setRoundTally(factory, coordinator) + await setRoundTally(clrfund, coordinator) await expect( - factory.transferMatchingFunds( + clrfund.transferMatchingFunds( totalSpent, totalSpentSalt, resultsCommitment, @@ -502,11 +531,11 @@ describe('Clr fund deployer', () => { }) it('allows only owner to finalize round', async () => { - await factory.deployNewRound(roundDuration) + await clrfund.deployNewRound(roundDuration) await provider.send('evm_increaseTime', [roundDuration]) - await setRoundTally(factory, coordinator) + await setRoundTally(clrfund, coordinator) await expect( - factory + clrfund .connect(contributor) .transferMatchingFunds( totalSpent, @@ -519,7 +548,7 @@ describe('Clr fund deployer', () => { it('reverts if round has not been deployed', async () => { await expect( - factory.transferMatchingFunds( + clrfund.transferMatchingFunds( totalSpent, totalSpentSalt, resultsCommitment, @@ -531,72 +560,72 @@ describe('Clr fund deployer', () => { describe('cancelling round', () => { beforeEach(async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) }) it('allows owner to cancel round', async () => { - await factory.deployNewRound(roundDuration) - const fundingRoundAddress = await factory.getCurrentRound() + await clrfund.deployNewRound(roundDuration) + const fundingRoundAddress = await clrfund.getCurrentRound() const fundingRound = await ethers.getContractAt( 'FundingRound', fundingRoundAddress ) - await expect(factory.cancelCurrentRound()) - .to.emit(factory, 'RoundFinalized') + await expect(clrfund.cancelCurrentRound()) + .to.emit(clrfund, 'RoundFinalized') .withArgs(fundingRoundAddress) expect(await fundingRound.isCancelled()).to.equal(true) }) it('allows only owner to cancel round', async () => { - await factory.deployNewRound(roundDuration) + await clrfund.deployNewRound(roundDuration) await expect( - factory.connect(contributor).cancelCurrentRound() + clrfund.connect(contributor).cancelCurrentRound() ).to.be.revertedWith('Ownable: caller is not the owner') }) it('reverts if round has not been deployed', async () => { - await expect(factory.cancelCurrentRound()).to.be.revertedWith( + await expect(clrfund.cancelCurrentRound()).to.be.revertedWith( 'NoCurrentRound' ) }) it('reverts if round is finalized', async () => { - await factory.deployNewRound(roundDuration) - await factory.cancelCurrentRound() - await expect(factory.cancelCurrentRound()).to.be.revertedWith( + await clrfund.deployNewRound(roundDuration) + await clrfund.cancelCurrentRound() + await expect(clrfund.cancelCurrentRound()).to.be.revertedWith( 'AlreadyFinalized' ) }) }) it('allows owner to set native token', async () => { - await expect(factory.setToken(token.address)) - .to.emit(factory, 'TokenChanged') + await expect(clrfund.setToken(token.address)) + .to.emit(clrfund, 'TokenChanged') .withArgs(token.address) - expect(await factory.nativeToken()).to.equal(token.address) + expect(await clrfund.nativeToken()).to.equal(token.address) }) it('only owner can set native token', async () => { - const factoryAsContributor = factory.connect(contributor) + const clrfundAsContributor = clrfund.connect(contributor) await expect( - factoryAsContributor.setToken(token.address) + clrfundAsContributor.setToken(token.address) ).to.be.revertedWith('Ownable: caller is not the owner') }) it('allows owner to change coordinator', async () => { - await expect(factory.setCoordinator(coordinator.address, coordinatorPubKey)) - .to.emit(factory, 'CoordinatorChanged') + await expect(clrfund.setCoordinator(coordinator.address, coordinatorPubKey)) + .to.emit(clrfund, 'CoordinatorChanged') .withArgs(coordinator.address) - expect(await factory.coordinator()).to.eq(coordinator.address) + expect(await clrfund.coordinator()).to.eq(coordinator.address) }) it('allows only the owner to set a new coordinator', async () => { - const factoryAsContributor = factory.connect(contributor) + const clrfundAsContributor = clrfund.connect(contributor) await expect( - factoryAsContributor.setCoordinator( + clrfundAsContributor.setCoordinator( coordinator.address, coordinatorPubKey ) @@ -604,34 +633,34 @@ describe('Clr fund deployer', () => { }) it('allows coordinator to call coordinatorQuit and sets coordinator to null', async () => { - await factory.setCoordinator(coordinator.address, coordinatorPubKey) - const factoryAsCoordinator = factory.connect(coordinator) - await expect(factoryAsCoordinator.coordinatorQuit()) - .to.emit(factory, 'CoordinatorChanged') + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) + const clrfundAsCoordinator = clrfund.connect(coordinator) + await expect(clrfundAsCoordinator.coordinatorQuit()) + .to.emit(clrfund, 'CoordinatorChanged') .withArgs(ZERO_ADDRESS) - expect(await factory.coordinator()).to.equal(ZERO_ADDRESS) + expect(await clrfund.coordinator()).to.equal(ZERO_ADDRESS) }) it('only coordinator can call coordinatorQuit', async () => { - await factory.setCoordinator(coordinator.address, coordinatorPubKey) - await expect(factory.coordinatorQuit()).to.be.revertedWith('NotAuthorized') + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) + await expect(clrfund.coordinatorQuit()).to.be.revertedWith('NotAuthorized') }) it('should cancel current round when coordinator quits', async () => { - await factory.setUserRegistry(userRegistry.address) - await factory.setRecipientRegistry(recipientRegistry.address) - await factory.setToken(token.address) - await factory.setCoordinator(coordinator.address, coordinatorPubKey) - await factory.deployNewRound(roundDuration) - const fundingRoundAddress = await factory.getCurrentRound() + await clrfund.setUserRegistry(userRegistry.address) + await clrfund.setRecipientRegistry(recipientRegistry.address) + await clrfund.setToken(token.address) + await clrfund.setCoordinator(coordinator.address, coordinatorPubKey) + await clrfund.deployNewRound(roundDuration) + const fundingRoundAddress = await clrfund.getCurrentRound() const fundingRound = await ethers.getContractAt( 'FundingRound', fundingRoundAddress ) - const factoryAsCoordinator = factory.connect(coordinator) - await expect(factoryAsCoordinator.coordinatorQuit()) - .to.emit(factory, 'RoundFinalized') + const clrfundAsCoordinator = clrfund.connect(coordinator) + await expect(clrfundAsCoordinator.coordinatorQuit()) + .to.emit(clrfund, 'RoundFinalized') .withArgs(fundingRoundAddress) expect(await fundingRound.isCancelled()).to.equal(true) }) diff --git a/contracts/tests/maciFactory.ts b/contracts/tests/maciFactory.ts index b43a589bc..f3baa4c2d 100644 --- a/contracts/tests/maciFactory.ts +++ b/contracts/tests/maciFactory.ts @@ -1,4 +1,4 @@ -import { waffle, artifacts, ethers } from 'hardhat' +import { waffle, artifacts, ethers, config } from 'hardhat' import { Contract } from 'ethers' import { use, expect } from 'chai' import { solidity } from 'ethereum-waffle' @@ -7,7 +7,8 @@ import { Keypair } from '@clrfund/common' import { getEventArg, getGasUsage } from '../utils/contracts' import { deployMaciFactory, deployPoseidonLibraries } from '../utils/deployment' -import { MaciParameters } from '../utils/maci' +import { MaciParameters } from '../utils/maciParameters' +import { DEFAULT_CIRCUIT } from '../utils/circuits' use(solidity) @@ -26,12 +27,20 @@ describe('MACI factory', () => { beforeEach(async () => { if (!poseidonContracts) { - poseidonContracts = await deployPoseidonLibraries(deployer) + poseidonContracts = await deployPoseidonLibraries({ + artifactsPath: config.paths.artifacts, + ethers, + signer: deployer, + }) } - maciFactory = await deployMaciFactory(deployer, poseidonContracts) + maciFactory = await deployMaciFactory({ + ethers, + signer: deployer, + libraries: poseidonContracts, + }) expect(await getGasUsage(maciFactory.deployTransaction)).lessThan(5600000) - maciParameters = MaciParameters.mock('micro') + maciParameters = MaciParameters.mock(DEFAULT_CIRCUIT) const SignUpGatekeeperArtifact = await artifacts.readArtifact('SignUpGatekeeper') @@ -68,17 +77,6 @@ describe('MACI factory', () => { expect(messageTreeDepth).to.equal(maciParameters.messageTreeDepth) }) - it('does not allow to decrease the vote option tree depth', async () => { - await expect( - maciFactory.setMaciParameters(...maciParameters.asContractParam()) - ).to.emit(maciFactory, 'MaciParametersChanged') - - maciParameters.voteOptionTreeDepth = 1 - await expect( - maciFactory.setMaciParameters(...maciParameters.asContractParam()) - ).to.be.revertedWith('CannotDecreaseVoteOptionDepth') - }) - it('allows only owner to set MACI parameters', async () => { const coordinatorMaciFactory = maciFactory.connect(coordinator) await expect( diff --git a/contracts/tests/recipientRegistry.ts b/contracts/tests/recipientRegistry.ts index 180a8bca6..398d747c1 100644 --- a/contracts/tests/recipientRegistry.ts +++ b/contracts/tests/recipientRegistry.ts @@ -551,11 +551,12 @@ describe('Optimistic recipient registry', () => { } beforeEach(async () => { - registry = await deployContract(deployer, 'OptimisticRecipientRegistry', [ - baseDeposit, - challengePeriodDuration, - controller.address, - ]) + registry = await deployContract({ + name: 'OptimisticRecipientRegistry', + contractArgs: [baseDeposit, challengePeriodDuration, controller.address], + ethers, + signer: deployer, + }) }) it('initializes correctly', async () => { @@ -981,11 +982,16 @@ describe('Optimistic recipient registry', () => { '_recipientId' ) - const anotherRegistry = await deployContract( - deployer, - 'OptimisticRecipientRegistry', - [baseDeposit, challengePeriodDuration, controller.address] - ) + const anotherRegistry = await deployContract({ + name: 'OptimisticRecipientRegistry', + contractArgs: [ + baseDeposit, + challengePeriodDuration, + controller.address, + ], + ethers, + signer: deployer, + }) const txTwo = await anotherRegistry.addRecipient( recipientAddress, metadata, diff --git a/contracts/tests/round.ts b/contracts/tests/round.ts index ed61cbc2c..ff822b871 100644 --- a/contracts/tests/round.ts +++ b/contracts/tests/round.ts @@ -1,4 +1,4 @@ -import { ethers, waffle, artifacts } from 'hardhat' +import { ethers, waffle, artifacts, config } from 'hardhat' import { use, expect } from 'chai' import { solidity } from 'ethereum-waffle' import { deployMockContract } from '@ethereum-waffle/mock-contract' @@ -15,9 +15,9 @@ import { } from '../utils/constants' import { getEventArg, getGasUsage } from '../utils/contracts' import { + deployContract, deployPoseidonLibraries, deployMaciFactory, - deployContractWithLinkedLibraries, } from '../utils/deployment' import { bnSqrt, @@ -25,9 +25,10 @@ import { addTallyResultsBatch, getRecipientClaimData, getRecipientTallyResultsBatch, - MaciParameters, } from '../utils/maci' import { sha256 } from 'ethers/lib/utils' +import { MaciParameters } from '../utils/maciParameters' +import { DEFAULT_CIRCUIT } from '../utils/circuits' use(solidity) @@ -134,21 +135,30 @@ describe('Funding Round', () => { IRecipientRegistryArtifact.abi ) - const libraries = await deployPoseidonLibraries(deployer) - fundingRound = await deployContractWithLinkedLibraries( - deployer, - 'FundingRound', + const libraries = await deployPoseidonLibraries({ + artifactsPath: config.paths.artifacts, + ethers, + signer: deployer, + }) + fundingRound = await deployContract({ + name: 'FundingRound', libraries, - [ + contractArgs: [ token.address, userRegistry.address, recipientRegistry.address, coordinator.address, - ] - ) - const maciFactory = await deployMaciFactory(deployer, libraries) + ], + ethers, + signer: deployer, + }) + const maciFactory = await deployMaciFactory({ + ethers, + signer: deployer, + libraries, + }) - const maciParams = MaciParameters.mock('micro') + const maciParams = MaciParameters.mock(DEFAULT_CIRCUIT) await maciFactory.setMaciParameters(...maciParams.asContractParam()) const maciDeployed = await maciFactory.deployMaci( diff --git a/contracts/tsconfig.json b/contracts/tsconfig.json index f5597e526..b178c1bd5 100644 --- a/contracts/tsconfig.json +++ b/contracts/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "module": "commonjs", "strict": true, "esModuleInterop": true, diff --git a/contracts/utils/JSONFile.ts b/contracts/utils/JSONFile.ts new file mode 100644 index 000000000..cf1a1eb95 --- /dev/null +++ b/contracts/utils/JSONFile.ts @@ -0,0 +1,27 @@ +import fs from 'fs' + +export class JSONFile { + /** + * Read the content of the JSON file + * + * @param path The path of the JSON file + * @returns + */ + static read(path: string) { + try { + return JSON.parse(fs.readFileSync(path).toString()) + } catch { + return {} + } + } + + /** + * Update the JSON file with the data + * @param path The path of the file + * @param data The new data to add to the JSON content + */ + static update(path: string, data: any) { + const state = JSONFile.read(path) + fs.writeFileSync(path, JSON.stringify({ ...state, ...data }, null, 2)) + } +} diff --git a/contracts/utils/circuits.ts b/contracts/utils/circuits.ts index c12108426..2d1a56691 100644 --- a/contracts/utils/circuits.ts +++ b/contracts/utils/circuits.ts @@ -4,6 +4,8 @@ const TREE_ARITY = 5 +export const DEFAULT_CIRCUIT = 'micro' + export const CIRCUITS: { [name: string]: any } = { micro: { processMessagesZkey: 'processmessages_6-8-2-3_final.zkey', diff --git a/contracts/utils/constants.ts b/contracts/utils/constants.ts index c7777989e..ed588f0e7 100644 --- a/contracts/utils/constants.ts +++ b/contracts/utils/constants.ts @@ -5,6 +5,7 @@ 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) export const ALPHA_PRECISION = BigNumber.from(10).pow(18) +export const DEFAULT_SR_QUEUE_OPS = 4 export enum RecipientState { Registered = 'Registered', diff --git a/contracts/utils/deployment.ts b/contracts/utils/deployment.ts index 8c5ee48d8..611013a11 100644 --- a/contracts/utils/deployment.ts +++ b/contracts/utils/deployment.ts @@ -1,17 +1,16 @@ -import { ethers, config } from 'hardhat' -import { Signer, Contract, utils } from 'ethers' +import { Signer, Contract, utils, BigNumber } from 'ethers' import { link } from 'ethereum-waffle' import path from 'path' -import { - mergeMessages, - mergeSignups, - genProofs, - proveOnChain, -} from '@clrfund/maci-cli' import { readFileSync } from 'fs' -import { MaciParameters } from './maci' +import { HardhatEthersHelpers } from '@nomiclabs/hardhat-ethers/types' +// Number.MAX_SAFE_INTEGER - 1 +export const challengePeriodSeconds = '9007199254740990' + +export type Libraries = { [name: string]: string } + +// Mapping of the user registry type and the contract name const userRegistryNames: Record = { simple: 'SimpleUserRegistry', brightid: 'BrightIdUserRegistry', @@ -19,44 +18,20 @@ const userRegistryNames: Record = { merkle: 'MerkleUserRegistry', } +// Mapping of recipient registry type to the contract name +const recipientRegistries: Record = { + simple: 'SimpleRecipientRegistry', + optimistic: 'OptimisticRecipientRegistry', +} + +// BrightId contract deployment parameters export interface BrightIdParams { context: string verifierAddress: string sponsor: string } -/** - * Return the brightid user registry contructor parameter values - * @param userRegistryType user registry type - * @returns BrightIdParams or null - */ -export function getBrightIdParams( - userRegistryType: string -): BrightIdParams | null { - if (userRegistryType === 'brightid') { - const verifierAddress = process.env.BRIGHTID_VERIFIER_ADDR - const sponsor = process.env.BRIGHTID_SPONSOR - if (!verifierAddress) { - throw new Error('Missing environment variable BRIGHTID_VERIFIER_ADDR') - } - if (!sponsor) { - throw new Error('Missing environment variable BRIGHTID_SPONSOR') - } - - return { - context: process.env.BRIGHTID_CONTEXT || 'clr.fund', - verifierAddress, - sponsor, - } - } else { - return null - } -} - -export function linkBytecode( - bytecode: string, - libraries: { [name: string]: string } -): string { +export function linkBytecode(bytecode: string, libraries: Libraries): string { // Workarounds for https://github.com/nomiclabs/buidler/issues/611 const linkable = { evm: { bytecode: { object: bytecode } } } for (const [libraryName, libraryAddress] of Object.entries(libraries)) { @@ -68,7 +43,7 @@ export function linkBytecode( type PoseidonName = 'PoseidonT3' | 'PoseidonT4' | 'PoseidonT5' | 'PoseidonT6' /** - * Deploy the PoseidonT3 or PoseidonT6 contracts. These 2 contracts + * Deploy the Poseidon contracts. These contracts * have a custom artifact location that the hardhat library cannot * retrieve using the standard getContractFactory() function, so, we manually * read the artifact content and pass to the getContractFactory function @@ -78,95 +53,85 @@ type PoseidonName = 'PoseidonT3' | 'PoseidonT4' | 'PoseidonT5' | 'PoseidonT6' * only has the library interface. If the wrong bytecode is used to deploy the contract, * the hash functions will always return 0. * - * @param account the account that deploys the contract - * @param contractName PoseidonT3 or PoseidonT6 + * @param name PoseidonT3, PoseidonT4, PoseidonT5, PoseidonT6 + * @param ethers + * @param signer the account that deploys the contract * @returns contract object */ -export async function deployPoseidon( - account: Signer, - contractName: PoseidonName -): Promise { +export async function deployPoseidon({ + name, + artifactsPath, + ethers, + signer, +}: { + name: PoseidonName + artifactsPath: string + ethers: HardhatEthersHelpers + signer?: Signer +}): Promise { const artifact = JSON.parse( - readFileSync( - path.join(config.paths.artifacts, `${contractName}.json`) - ).toString() + readFileSync(path.join(artifactsPath, `${name}.json`)).toString() ) const Poseidon = await ethers.getContractFactory( artifact.abi, artifact.bytecode, - account + signer ) return Poseidon.deploy() } -export async function deployContractWithLinkedLibraries( - signer: Signer, - contractName: string, - libraries: { [name: string]: string }, - contractArgs: any[] = [] -): Promise { - const contractFactory = await ethers.getContractFactory(contractName, { +export type deployContractOptions = { + name: string + libraries?: Libraries + contractArgs?: any[] + // hardhat ethers handle + ethers: HardhatEthersHelpers + // if signer is not provided, use the default signer from ethers + signer?: Signer +} + +export async function deployContract({ + name, + libraries, + contractArgs = [], + ethers, + signer, +}: deployContractOptions): Promise { + const contractFactory = await ethers.getContractFactory(name, { signer, libraries, }) - const contract = await contractFactory.deploy(...contractArgs) - return await contract.deployed() -} -export async function deployContract( - account: Signer, - contractName: string, - contractArgs: any[] = [] -): Promise { - const contractFactory = await ethers.getContractFactory(contractName, account) const contract = await contractFactory.deploy(...contractArgs) return await contract.deployed() } -export async function deployMaciFactory( - account: Signer, - poseidonContracts: { [name: string]: string } -): Promise { - const pollFactoryCreator = await deployContractWithLinkedLibraries( - account, - 'PollFactoryCreator', - { ...poseidonContracts } - ) - - const vkRegistry = await deployContract(account, 'VkRegistry') - const MACIFactory = await ethers.getContractFactory('MACIFactory', { - signer: account, - libraries: { - ...poseidonContracts, - PollFactoryCreator: pollFactoryCreator.address, - }, - }) - - const maciFactory = await MACIFactory.deploy(vkRegistry.address) - await maciFactory.deployTransaction.wait() - - const transferTx = await vkRegistry.transferOwnership(maciFactory.address) - await transferTx.wait() - - return maciFactory -} - +/** + * Deploy a user registry + * @param userRegistryType user registry type, e.g. brightid, simple, etc + * @param ethers Hardhat ethers handle + * @param brightid Brightid parameters for the BrightID user registry + * @returns the newly deployed user registry contract + */ export async function deployUserRegistry( userRegistryType: string, - deployer: Signer, - brightid: BrightIdParams | null + ethers: HardhatEthersHelpers, + brightid?: BrightIdParams ): Promise { let userRegistry: Contract - if (userRegistryType === 'brightid') { + const [signer] = await ethers.getSigners() + + const lowercaseType = (userRegistryType || '').toLowerCase() + if (lowercaseType === 'brightid') { if (!brightid) { throw new Error('Missing BrightId parameter') } const BrightIdUserRegistry = await ethers.getContractFactory( 'BrightIdUserRegistry', - deployer + signer ) userRegistry = await BrightIdUserRegistry.deploy( @@ -175,14 +140,14 @@ export async function deployUserRegistry( brightid.sponsor ) } else { - const userRegistryName = userRegistryNames[userRegistryType] + const userRegistryName = userRegistryNames[lowercaseType] if (!userRegistryName) { - throw new Error('unsupported user registry type: ' + userRegistryType) + throw new Error('unsupported user registry type: ' + lowercaseType) } const UserRegistry = await ethers.getContractFactory( userRegistryName, - deployer + signer ) userRegistry = await UserRegistry.deploy() } @@ -191,19 +156,98 @@ export async function deployUserRegistry( return userRegistry } +/** + * Deploy a recipient registry + * @param type recipient registry type, e.g. simple, optimistic, etc + * @param controller the controller address of the registry + * @param ethers Hardhat ethers handle + * @returns the newly deployed registry contract + */ +export async function deployRecipientRegistry({ + type, + controller, + deposit, + challengePeriod, + ethers, + signer, +}: { + type: string + controller: string + deposit?: BigNumber + challengePeriod?: string + ethers: HardhatEthersHelpers + signer?: Signer +}): Promise { + const lowercaseType = (type || '').toLowerCase() + const registryName = recipientRegistries[lowercaseType] + if (!registryName) { + throw new Error('Unsupported recipient registry type: ' + type) + } + + if (lowercaseType === 'optimistic') { + if (!deposit) { + throw new Error('Missing base deposit amount') + } + if (!challengePeriod) { + throw new Error('Missing challenge period') + } + } + + const args = + lowercaseType === 'simple' + ? [controller] + : [deposit, challengePeriod, controller] + + const factory = await ethers.getContractFactory(registryName, signer) + const recipientRegistry = await factory.deploy(...args) + + return await recipientRegistry.deployed() +} + /** * Deploy all the poseidon contracts * * @param signer The signer for the deployment transaction + * @param ethers Hardhat ethers handle + * @param artifactsPath Contract artifacts path * @returns the deployed poseidon contracts */ -export async function deployPoseidonLibraries( - signer: Signer -): Promise<{ [name: string]: string }> { - const PoseidonT3Contract = await deployPoseidon(signer, 'PoseidonT3') - const PoseidonT4Contract = await deployPoseidon(signer, 'PoseidonT4') - const PoseidonT5Contract = await deployPoseidon(signer, 'PoseidonT5') - const PoseidonT6Contract = await deployPoseidon(signer, 'PoseidonT6') +export async function deployPoseidonLibraries({ + signer, + ethers, + artifactsPath, +}: { + signer?: Signer + ethers: HardhatEthersHelpers + artifactsPath: string +}): Promise<{ [name: string]: string }> { + const PoseidonT3Contract = await deployPoseidon({ + name: 'PoseidonT3', + artifactsPath, + ethers, + signer, + }) + + const PoseidonT4Contract = await deployPoseidon({ + name: 'PoseidonT4', + artifactsPath, + ethers, + signer, + }) + + const PoseidonT5Contract = await deployPoseidon({ + name: 'PoseidonT5', + artifactsPath, + signer, + ethers, + }) + + const PoseidonT6Contract = await deployPoseidon({ + name: 'PoseidonT6', + artifactsPath, + ethers, + signer, + }) const libraries = { PoseidonT3: PoseidonT3Contract.address, @@ -214,4 +258,149 @@ export async function deployPoseidonLibraries( return libraries } -export { mergeMessages, mergeSignups, proveOnChain, genProofs } +/** + * Deploy the poll factory + * @param signer Contract creator + * @param ethers Hardhat ethers handle + * @param libraries Poseidon libraries + * @param artifactPath Poseidon contract artifacts path + * + */ +export async function deployPollFactory({ + signer, + ethers, + libraries, + artifactsPath, +}: { + signer: Signer + ethers: HardhatEthersHelpers + libraries?: Libraries + artifactsPath?: string +}): Promise { + let poseidonLibraries = libraries + if (!libraries) { + if (!artifactsPath) { + throw Error('Failed to dpeloy PollFactory, artifact path is missing') + } + poseidonLibraries = await deployPoseidonLibraries({ + artifactsPath: artifactsPath || '', + ethers, + signer, + }) + } + + return deployContract({ + name: 'PollFactory', + libraries: poseidonLibraries, + signer, + ethers, + }) +} + +/** + * Deploy the contracts needed to run the proveOnChain script. + * If the poseidon contracts are not provided, it will create them + * using the byte codes in the artifactsPath + * + * libraries - poseidon libraries + * artifactsPath - path that contacts the poseidon abi and bytecode + * + * @returns the MessageProcessor and Tally contracts + */ +export async function deployMessageProcesorAndTally({ + artifactsPath, + libraries, + ethers, + signer, +}: { + libraries?: Libraries + artifactsPath?: string + signer?: Signer + ethers: HardhatEthersHelpers +}): Promise<{ + mpContract: Contract + tallyContract: Contract +}> { + if (!libraries) { + if (!artifactsPath) { + throw Error('Need the artifacts path to create the poseidon contracts') + } + libraries = await deployPoseidonLibraries({ + artifactsPath, + ethers, + signer, + }) + } + + const verifierContract = await deployContract({ + name: 'Verifier', + signer, + ethers, + }) + const tallyContract = await deployContract({ + name: 'Tally', + contractArgs: [verifierContract.address], + libraries, + ethers, + signer, + }) + + // deploy the message processing contract + const mpContract = await deployContract({ + name: 'MessageProcessor', + contractArgs: [verifierContract.address], + signer, + libraries, + ethers, + }) + + return { + mpContract, + tallyContract, + } +} + +/** + * Deploy an instance of MACI factory + * + * libraries - poseidon contracts + * ethers - hardhat ethers handle + * signer - if signer is not provided, use default signer in ethers + * + * @returns MACI factory contract + */ +export async function deployMaciFactory({ + libraries, + ethers, + signer, +}: { + libraries: Libraries + ethers: HardhatEthersHelpers + signer?: Signer +}): Promise { + const pollFactory = await deployContract({ + name: 'PollFactory', + libraries, + ethers, + signer, + }) + + const vkRegistry = await deployContract({ + name: 'VkRegistry', + ethers, + signer, + }) + + const maciFactory = await deployContract({ + name: 'MACIFactory', + libraries, + contractArgs: [vkRegistry.address, pollFactory.address], + ethers, + signer, + }) + + const transferTx = await vkRegistry.transferOwnership(maciFactory.address) + await transferTx.wait() + + return maciFactory +} diff --git a/contracts/utils/maci.ts b/contracts/utils/maci.ts index d5bdc359a..2447ec55f 100644 --- a/contracts/utils/maci.ts +++ b/contracts/utils/maci.ts @@ -8,15 +8,26 @@ import { hash3, hashLeftRight, LEAVES_PER_NODE, + genTallyResultCommitment, } from '@clrfund/common' import * as os from 'os' +import { + mergeMessages, + mergeSignups, + genProofs, + proveOnChain, +} from '@clrfund/maci-cli' -import { genTallyResultCommitment } from '@clrfund/common' -import { VerifyingKey } from 'maci-domainobjs' -import { extractVk } from '@clrfund/maci-circuits' +import { getTalyFilePath } from './misc' import { CIRCUITS } from './circuits' import path from 'path' +interface TallyResult { + recipientIndex: number + result: string + proof: any[] +} + export interface ZkFiles { processZkFile: string processWitness: string @@ -28,158 +39,11 @@ export interface ZkFiles { export const isOsArm = os.arch().includes('arm') -/** - * Get the zkey file path - * @param name zkey file name - * @returns zkey file path - */ -export function getCircuitFiles(circuit: string, directory: string): ZkFiles { - const params = CIRCUITS[circuit] - return { - processZkFile: path.join(directory, params.processMessagesZkey), - processWitness: path.join(directory, params.processWitness), - processWasm: path.join(directory, params.processWasm), - tallyZkFile: path.join(directory, params.tallyVotesZkey), - tallyWitness: path.join(directory, params.tallyWitness), - tallyWasm: path.join(directory, params.tallyWasm), - } -} - -export class MaciParameters { - stateTreeDepth: number - intStateTreeDepth: number - messageTreeSubDepth: number - messageTreeDepth: number - voteOptionTreeDepth: number - maxMessages: number - maxVoteOptions: number - messageBatchSize: number - processVk: VerifyingKey - tallyVk: VerifyingKey - - constructor(parameters: { [name: string]: any } = {}) { - this.stateTreeDepth = parameters.stateTreeDepth - this.intStateTreeDepth = parameters.intStateTreeDepth - this.messageTreeSubDepth = parameters.messageTreeSubDepth - this.messageTreeDepth = parameters.messageTreeDepth - this.voteOptionTreeDepth = parameters.voteOptionTreeDepth - this.maxMessages = parameters.maxMessages - this.maxVoteOptions = parameters.maxVoteOptions - this.messageBatchSize = parameters.messageBatchSize - this.processVk = parameters.processVk - this.tallyVk = parameters.tallyVk - } - - asContractParam(): any[] { - return [ - this.stateTreeDepth, - { - intStateTreeDepth: this.intStateTreeDepth, - messageTreeSubDepth: this.messageTreeSubDepth, - messageTreeDepth: this.messageTreeDepth, - voteOptionTreeDepth: this.voteOptionTreeDepth, - }, - { maxMessages: this.maxMessages, maxVoteOptions: this.maxVoteOptions }, - this.messageBatchSize, - this.processVk.asContractParam(), - this.tallyVk.asContractParam(), - ] - } - - static async fromConfig( - circuit: string, - directory: string - ): Promise { - const params = CIRCUITS[circuit] - const { processZkFile, tallyZkFile } = getCircuitFiles(circuit, directory) - const processVk: VerifyingKey = VerifyingKey.fromObj( - await extractVk(processZkFile) - ) - const tallyVk: VerifyingKey = VerifyingKey.fromObj( - await extractVk(tallyZkFile) - ) - - return new MaciParameters({ - ...params.maxValues, - ...params.treeDepths, - ...params.batchSizes, - processVk, - tallyVk, - }) - } - - static async fromContract(maciFactory: Contract): Promise { - const stateTreeDepth = await maciFactory.stateTreeDepth() - const { - intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, - voteOptionTreeDepth, - } = await maciFactory.treeDepths() - const { maxMessages, maxVoteOptions } = await maciFactory.maxValues() - const messageBatchSize = await maciFactory.messageBatchSize() - const vkRegistry = await maciFactory.vkRegistry() - - const processVk = await vkRegistry.getProcessVk( - stateTreeDepth, - messageTreeDepth, - voteOptionTreeDepth, - messageBatchSize - ) - - const tallyVk = await vkRegistry.getTallyVk( - stateTreeDepth, - intStateTreeDepth, - voteOptionTreeDepth - ) - - return new MaciParameters({ - stateTreeDepth, - intStateTreeDepth, - messageTreeSubDepth, - messageTreeDepth, - voteOptionTreeDepth, - maxMessages, - maxVoteOptions, - messageBatchSize, - processVk: VerifyingKey.fromContract(processVk), - tallyVk: VerifyingKey.fromContract(tallyVk), - }) - } - - static mock(circuit: string): MaciParameters { - const processVk = VerifyingKey.fromObj({ - vk_alpha_1: [1, 2], - vk_beta_2: [ - [1, 2], - [1, 2], - ], - vk_gamma_2: [ - [1, 2], - [1, 2], - ], - vk_delta_2: [ - [1, 2], - [1, 2], - ], - IC: [[1, 2]], - }) - const params = CIRCUITS[circuit] - return new MaciParameters({ - ...params.maxValues, - ...params.treeDepths, - ...params.batchSizes, - processVk, - tallyVk: processVk.copy(), - }) - } -} - export function getRecipientTallyResult( recipientIndex: number, recipientTreeDepth: number, tally: any -): { recipientIndex: number; result: string; proof: any[] } { +): TallyResult { // Create proof for tally result const result = tally.results.tally[recipientIndex] const resultTree = new IncrementalQuinTree( @@ -210,7 +74,7 @@ export function getRecipientTallyResultsBatch( throw new Error('Recipient index out of bound') } - const tallyData = [] + const tallyData: TallyResult[] = [] const lastIndex = recipientStartIndex + batchSize > tallyCount ? tallyCount @@ -295,18 +159,36 @@ export async function addTallyResultsBatch( return totalGasUsed } +/** + * Get the zkey file path + * @param name zkey file name + * @returns zkey file path + */ +export function getCircuitFiles(circuit: string, directory: string): ZkFiles { + const params = CIRCUITS[circuit] + return { + processZkFile: path.join(directory, params.processMessagesZkey), + processWitness: path.join(directory, params.processWitness), + processWasm: path.join(directory, params.processWasm), + tallyZkFile: path.join(directory, params.tallyVotesZkey), + tallyWitness: path.join(directory, params.tallyWitness), + tallyWasm: path.join(directory, params.tallyWasm), + } +} + /* Input to getGenProofArgs() */ type getGenProofArgsInput = { maciAddress: string providerUrl: string pollId: string - serializedCoordinatorPrivKey: string + // coordinator's MACI serialized secret key + coordinatorMacisk: string // the transaction hash of the creation of the MACI contract maciTxHash?: string // the key get zkeys file mapping, see utils/circuits.ts circuitType: string circuitDirectory: string - rapidSnarkDirectory: string + rapidSnarkDirectory?: string // where the proof will be produced outputDir: string } @@ -339,16 +221,16 @@ export function getGenProofArgs( maciAddress, providerUrl, pollId, - serializedCoordinatorPrivKey, + coordinatorMacisk, maciTxHash, circuitType, circuitDirectory, rapidSnarkDirectory, outputDir, } = args - const tallyFile = path.join(outputDir, `tally.json`) + const tallyFile = getTalyFilePath(outputDir) const maciStateFile = path.join(outputDir, `macistate`) - const rapidSnarkExe = path.join(rapidSnarkDirectory, 'prover') + const rapidSnarkExe = path.join(rapidSnarkDirectory || '', 'prover') const { processZkFile, @@ -371,7 +253,7 @@ export function getGenProofArgs( tally_wasm: tallyWasm, transaction_hash: maciTxHash, output: outputDir, - privkey: serializedCoordinatorPrivKey, + privkey: coordinatorMacisk, macistate: maciStateFile, } : { @@ -386,9 +268,35 @@ export function getGenProofArgs( tally_zkey: tallyZkFile, transaction_hash: maciTxHash, output: outputDir, - privkey: serializedCoordinatorPrivKey, + privkey: coordinatorMacisk, macistate: maciStateFile, } } -export { createMessage, getRecipientClaimData, bnSqrt } +/** + * Merge MACI message and signups subtrees + * Must merge the subtrees before calling genProofs + * + * @param maciAddress MACI contract address + * @param pollId Poll id + * @param numOperations Number of operations to perform for the merge + */ +export async function mergeMaciSubtrees( + maciAddress: string, + pollId: string, + numOperations: number +) { + await mergeMessages({ + contract: maciAddress, + poll_id: pollId, + num_queue_ops: numOperations, + }) + + await mergeSignups({ + contract: maciAddress, + poll_id: pollId, + num_queue_ops: numOperations, + }) +} + +export { createMessage, getRecipientClaimData, bnSqrt, proveOnChain, genProofs } diff --git a/contracts/utils/maciParameters.ts b/contracts/utils/maciParameters.ts new file mode 100644 index 000000000..6f169b10b --- /dev/null +++ b/contracts/utils/maciParameters.ts @@ -0,0 +1,146 @@ +import { Contract } from 'ethers' + +import { VerifyingKey } from 'maci-domainobjs' +import { extractVk } from '@clrfund/maci-circuits' +import { CIRCUITS } from './circuits' +import path from 'path' + +export interface ZkFiles { + processZkFile: string + processWitness: string + processWasm: string + tallyZkFile: string + tallyWitness: string + tallyWasm: string +} + +/** + * Get the zkey file path + * @param name zkey file name + * @returns zkey file path + */ +export function getCircuitFiles(circuit: string, directory: string): ZkFiles { + const params = CIRCUITS[circuit] + return { + processZkFile: path.join(directory, params.processMessagesZkey), + processWitness: path.join(directory, params.processWitness), + processWasm: path.join(directory, params.processWasm), + tallyZkFile: path.join(directory, params.tallyVotesZkey), + tallyWitness: path.join(directory, params.tallyWitness), + tallyWasm: path.join(directory, params.tallyWasm), + } +} + +export class MaciParameters { + stateTreeDepth: number + intStateTreeDepth: number + messageTreeSubDepth: number + messageTreeDepth: number + voteOptionTreeDepth: number + maxMessages: number + maxVoteOptions: number + messageBatchSize: number + processVk: VerifyingKey + tallyVk: VerifyingKey + + constructor(parameters: { [name: string]: any } = {}) { + this.stateTreeDepth = parameters.stateTreeDepth + this.intStateTreeDepth = parameters.intStateTreeDepth + this.messageTreeSubDepth = parameters.messageTreeSubDepth + this.messageTreeDepth = parameters.messageTreeDepth + this.voteOptionTreeDepth = parameters.voteOptionTreeDepth + this.maxMessages = parameters.maxMessages + this.maxVoteOptions = parameters.maxVoteOptions + this.messageBatchSize = parameters.messageBatchSize + this.processVk = parameters.processVk + this.tallyVk = parameters.tallyVk + } + + asContractParam(): any[] { + return [ + this.stateTreeDepth, + { + intStateTreeDepth: this.intStateTreeDepth, + messageTreeSubDepth: this.messageTreeSubDepth, + messageTreeDepth: this.messageTreeDepth, + voteOptionTreeDepth: this.voteOptionTreeDepth, + }, + { maxMessages: this.maxMessages, maxVoteOptions: this.maxVoteOptions }, + this.messageBatchSize, + this.processVk.asContractParam(), + this.tallyVk.asContractParam(), + ] + } + + static async fromConfig( + circuit: string, + directory: string + ): Promise { + const params = CIRCUITS[circuit] + const { processZkFile, tallyZkFile } = getCircuitFiles(circuit, directory) + const processVk: VerifyingKey = VerifyingKey.fromObj( + await extractVk(processZkFile) + ) + const tallyVk: VerifyingKey = VerifyingKey.fromObj( + await extractVk(tallyZkFile) + ) + + return new MaciParameters({ + ...params.maxValues, + ...params.treeDepths, + ...params.batchSizes, + processVk, + tallyVk, + }) + } + + static async fromContract(maciFactory: Contract): Promise { + const stateTreeDepth = await maciFactory.stateTreeDepth() + const { + intStateTreeDepth, + messageTreeSubDepth, + messageTreeDepth, + voteOptionTreeDepth, + } = await maciFactory.treeDepths() + const { maxMessages, maxVoteOptions } = await maciFactory.maxValues() + const messageBatchSize = await maciFactory.messageBatchSize() + + return new MaciParameters({ + stateTreeDepth, + intStateTreeDepth, + messageTreeSubDepth, + messageTreeDepth, + voteOptionTreeDepth, + maxMessages, + maxVoteOptions, + messageBatchSize, + }) + } + + static mock(circuit: string): MaciParameters { + const processVk = VerifyingKey.fromObj({ + vk_alpha_1: [1, 2], + vk_beta_2: [ + [1, 2], + [1, 2], + ], + vk_gamma_2: [ + [1, 2], + [1, 2], + ], + vk_delta_2: [ + [1, 2], + [1, 2], + ], + IC: [[1, 2]], + }) + const params = CIRCUITS[circuit] + return new MaciParameters({ + ...params.maxValues, + ...params.treeDepths, + ...params.batchSizes, + processVk, + tallyVk: processVk.copy(), + }) + } +} diff --git a/contracts/utils/misc.ts b/contracts/utils/misc.ts new file mode 100644 index 000000000..6eafc1586 --- /dev/null +++ b/contracts/utils/misc.ts @@ -0,0 +1,11 @@ +import path from 'path' + +/** + * Get the tally file path + * + * @param outputDir The output directory + * @returns The tally file path + */ +export function getTalyFilePath(outputDir: string) { + return path.join(outputDir, 'tally.json') +} diff --git a/yarn.lock b/yarn.lock index d825f089b..6e53fe0c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -55,12 +55,12 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.4.tgz#03ae5af150be94392cb5c7ccd97db5a19a5da6aa" + integrity sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA== dependencies: - "@babel/highlight" "^7.22.13" + "@babel/highlight" "^7.23.4" chalk "^2.4.2" "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.9": @@ -89,12 +89,12 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e" - integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg== +"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.23.3", "@babel/generator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.4.tgz#4a41377d8566ec18f807f42962a7f3551de83d1c" + integrity sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ== dependencies: - "@babel/types" "^7.23.3" + "@babel/types" "^7.23.4" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -219,10 +219,10 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.22.5", "@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== "@babel/helper-validator-identifier@^7.22.19", "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" @@ -235,27 +235,27 @@ integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== "@babel/helpers@^7.23.2": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" - integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.4.tgz#7d2cfb969aa43222032193accd7329851facf3c1" + integrity sha512-HfcMizYz10cr3h29VqyfGL6ZWIjTwWfvYBMsBVGwpcbhNGe3wQ1ZXZRPzZoAHhd9OqHadHqjQ89iVKINXnbzuw== dependencies: "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.23.4" + "@babel/types" "^7.23.4" -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== dependencies: "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.21.8", "@babel/parser@^7.22.15", "@babel/parser@^7.22.5", "@babel/parser@^7.23.0", "@babel/parser@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" - integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== +"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.21.8", "@babel/parser@^7.22.15", "@babel/parser@^7.22.5", "@babel/parser@^7.23.0", "@babel/parser@^7.23.3", "@babel/parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.4.tgz#409fbe690c333bb70187e2de4021e1e47a026661" + integrity sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ== "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" @@ -297,7 +297,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.22.5": +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== @@ -326,9 +326,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.3.tgz#e99a3ff08f58edd28a8ed82481df76925a4ffca7" - integrity sha512-QPZxHrThbQia7UdvfpaRRlq/J9ciz1J4go0k+lPBXbgaNeY7IQrBj/9ceWjvMMI07/ZBzHl/F0R/2K0qH7jCVw== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -439,15 +439,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.15.tgz#7e6266d88705d7c49f11c98db8b9464531289cd6" - integrity sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/types" "^7.22.15" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.23.3" @@ -472,9 +472,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/runtime@^7.0.0": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" - integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.4.tgz#36fa1d2b36db873d25ec631dcc4923fdc1cf2e2e" + integrity sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg== dependencies: regenerator-runtime "^0.14.0" @@ -487,19 +487,19 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b" - integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ== +"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.23.3", "@babel/traverse@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.4.tgz#c2790f7edf106d059a0098770fe70801417f3f85" + integrity sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.3" + "@babel/code-frame" "^7.23.4" + "@babel/generator" "^7.23.4" "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.3" - "@babel/types" "^7.23.3" + "@babel/parser" "^7.23.4" + "@babel/types" "^7.23.4" debug "^4.1.0" globals "^11.1.0" @@ -512,21 +512,21 @@ "@babel/helper-validator-identifier" "^7.22.19" to-fast-properties "^2.0.0" -"@babel/types@7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" - integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== +"@babel/types@7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" + integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== dependencies: "@babel/helper-string-parser" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" - integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== +"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.4.tgz#7206a1810fc512a7f7f7d4dace4cb4c1c9dbfb8e" + integrity sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ== dependencies: - "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-string-parser" "^7.23.4" "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" @@ -562,9 +562,9 @@ "@bugsnag/node" "^7.19.0" "@bugsnag/js@^7.0.0", "@bugsnag/js@^7.20.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-7.21.0.tgz#0a8a9a61a43cf9b552dc70341ed49ee9da46a8f3" - integrity sha512-fFTR7cRBSlLtwa1wPTse92igZUEX2V95KyGGCXq2qb2F2w6hJ6oJDxA0BMPS8qqsciYXRjbfn8HX+TFgO1oErg== + version "7.22.2" + resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-7.22.2.tgz#4cd91c77e9e4657b8a952fad34eee40382753c81" + integrity sha512-HgKzjkwzMQKyokIFnyRMChONxM9AoR24Sk76tWcqIdFagE0bhnTgSn3qYT2bRVNODtWyQHiW6qjOOpgOM3Mjlw== dependencies: "@bugsnag/browser" "^7.21.0" "@bugsnag/node" "^7.19.0" @@ -660,10 +660,10 @@ xmlhttprequest "1.8.0" zkey-manager "^0.1.1" -"@clrfund/maci-contracts@^1.1.7": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@clrfund/maci-contracts/-/maci-contracts-1.1.7.tgz#8a35678cf76607c480ff164a70154c7bdc8b8c3c" - integrity sha512-m68HH6KEP64WBWhJmUdiwk3kKSLfC+sIpE+vp4awBkCstFaEEgGQNEdikxKo2rFFSGpn7XPegb6oCeDXQvWxgg== +"@clrfund/maci-contracts@^1.1.7", "@clrfund/maci-contracts@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@clrfund/maci-contracts/-/maci-contracts-1.1.9.tgz#baaa07c586d7851bfdefe6124fe048f0140c53f8" + integrity sha512-X6JQApedLX+LHsLz2bl1FdtMht8jP4pAhhGUSTJyW8mpwksKYINQt/EsJuaD2RPoyC2arp/VhpvSh5FHtZ2X2w== dependencies: "@clrfund/maci-core" "^1.1.7" "@clrfund/maci-crypto" "^1.1.7" @@ -767,10 +767,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.4.tgz#74752a09301b8c6b9a415fbda9fb71406a62a7b7" integrity sha512-mRsi2vJsk4Bx/AFsNBqOH2fqedxn5L/moT58xgg51DjX1la64Z3Npicut2VbhvDFO26qjWtPMsVxCd80YTFVeg== -"@esbuild/android-arm64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz#276c5f99604054d3dbb733577e09adae944baa90" - integrity sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ== +"@esbuild/android-arm64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.6.tgz#13d98a34bbbde4237867cc232307a20ded139b6f" + integrity sha512-KQ/hbe9SJvIJ4sR+2PcZ41IBV+LPJyYp6V1K1P1xcMRup9iYsBoQn4MzE3mhMLOld27Au2eDcLlIREeKGUXpHQ== "@esbuild/android-arm@0.18.20": version "0.18.20" @@ -782,10 +782,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.4.tgz#c27363e1e280e577d9b5c8fa7c7a3be2a8d79bf5" integrity sha512-uBIbiYMeSsy2U0XQoOGVVcpIktjLMEKa7ryz2RLr7L/vTnANNEsPVAh4xOv7ondGz6ac1zVb0F8Jx20rQikffQ== -"@esbuild/android-arm@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.5.tgz#4a3cbf14758166abaae8ba9c01a80e68342a4eec" - integrity sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA== +"@esbuild/android-arm@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.6.tgz#68898d949672c56f10451f540fd92301dc713fb3" + integrity sha512-muPzBqXJKCbMYoNbb1JpZh/ynl0xS6/+pLjrofcR3Nad82SbsCogYzUE6Aq9QT3cLP0jR/IVK/NHC9b90mSHtg== "@esbuild/android-x64@0.18.20": version "0.18.20" @@ -797,10 +797,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.4.tgz#6c9ee03d1488973d928618100048b75b147e0426" integrity sha512-4iPufZ1TMOD3oBlGFqHXBpa3KFT46aLl6Vy7gwed0ZSYgHaZ/mihbYb4t7Z9etjkC9Al3ZYIoOaHrU60gcMy7g== -"@esbuild/android-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.5.tgz#21a3d11cd4613d2d3c5ccb9e746c254eb9265b0a" - integrity sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA== +"@esbuild/android-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.6.tgz#51a0ab83680dedc6dd1ae26133def26b178ed3a1" + integrity sha512-VVJVZQ7p5BBOKoNxd0Ly3xUM78Y4DyOoFKdkdAe2m11jbh0LEU4bPles4e/72EMl4tapko8o915UalN/5zhspg== "@esbuild/darwin-arm64@0.18.20": version "0.18.20" @@ -812,10 +812,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.4.tgz#64e2ee945e5932cd49812caa80e8896e937e2f8b" integrity sha512-Lviw8EzxsVQKpbS+rSt6/6zjn9ashUZ7Tbuvc2YENgRl0yZTktGlachZ9KMJUsVjZEGFVu336kl5lBgDN6PmpA== -"@esbuild/darwin-arm64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz#714cb839f467d6a67b151ee8255886498e2b9bf6" - integrity sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw== +"@esbuild/darwin-arm64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.6.tgz#2883f14197111febb118c0463c080930a30883e5" + integrity sha512-91LoRp/uZAKx6ESNspL3I46ypwzdqyDLXZH7x2QYCLgtnaU08+AXEbabY2yExIz03/am0DivsTtbdxzGejfXpA== "@esbuild/darwin-x64@0.18.20": version "0.18.20" @@ -827,10 +827,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.4.tgz#d8e26e1b965df284692e4d1263ba69a49b39ac7a" integrity sha512-YHbSFlLgDwglFn0lAO3Zsdrife9jcQXQhgRp77YiTDja23FrC2uwnhXMNkAucthsf+Psr7sTwYEryxz6FPAVqw== -"@esbuild/darwin-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz#2c553e97a6d2b4ae76a884e35e6cbab85a990bbf" - integrity sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA== +"@esbuild/darwin-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.6.tgz#400bf20f9a35a7d68a17f5898c0f9ecb099f062b" + integrity sha512-QCGHw770ubjBU1J3ZkFJh671MFajGTYMZumPs9E/rqU52md6lIil97BR0CbPq6U+vTh3xnTNDHKRdR8ggHnmxQ== "@esbuild/freebsd-arm64@0.18.20": version "0.18.20" @@ -842,10 +842,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.4.tgz#29751a41b242e0a456d89713b228f1da4f45582f" integrity sha512-vz59ijyrTG22Hshaj620e5yhs2dU1WJy723ofc+KUgxVCM6zxQESmWdMuVmUzxtGqtj5heHyB44PjV/HKsEmuQ== -"@esbuild/freebsd-arm64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz#d554f556718adb31917a0da24277bf84b6ee87f3" - integrity sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ== +"@esbuild/freebsd-arm64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.6.tgz#8af07bd848afa2470b8a2339b203ce29a721152b" + integrity sha512-J53d0jGsDcLzWk9d9SPmlyF+wzVxjXpOH7jVW5ae7PvrDst4kiAz6sX+E8btz0GB6oH12zC+aHRD945jdjF2Vg== "@esbuild/freebsd-x64@0.18.20": version "0.18.20" @@ -857,10 +857,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.4.tgz#873edc0f73e83a82432460ea59bf568c1e90b268" integrity sha512-3sRbQ6W5kAiVQRBWREGJNd1YE7OgzS0AmOGjDmX/qZZecq8NFlQsQH0IfXjjmD0XtUYqr64e0EKNFjMUlPL3Cw== -"@esbuild/freebsd-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz#288f7358a3bb15d99e73c65c9adaa3dabb497432" - integrity sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ== +"@esbuild/freebsd-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.6.tgz#ae0230860e27df204a616671e028ff8fdffa009a" + integrity sha512-hn9qvkjHSIB5Z9JgCCjED6YYVGCNpqB7dEGavBdG6EjBD8S/UcNUIlGcB35NCkMETkdYwfZSvD9VoDJX6VeUVA== "@esbuild/linux-arm64@0.18.20": version "0.18.20" @@ -872,10 +872,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.4.tgz#659f2fa988d448dbf5010b5cc583be757cc1b914" integrity sha512-ZWmWORaPbsPwmyu7eIEATFlaqm0QGt+joRE9sKcnVUG3oBbr/KYdNE2TnkzdQwX6EDRdg/x8Q4EZQTXoClUqqA== -"@esbuild/linux-arm64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz#95933ae86325c93cb6b5e8333d22120ecfdc901b" - integrity sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA== +"@esbuild/linux-arm64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.6.tgz#3042bc423a978deab44a72244b863f743fd9fda1" + integrity sha512-HQCOrk9XlH3KngASLaBfHpcoYEGUt829A9MyxaI8RMkfRA8SakG6YQEITAuwmtzFdEu5GU4eyhKcpv27dFaOBg== "@esbuild/linux-arm@0.18.20": version "0.18.20" @@ -887,10 +887,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.4.tgz#d5b13a7ec1f1c655ce05c8d319b3950797baee55" integrity sha512-z/4ArqOo9EImzTi4b6Vq+pthLnepFzJ92BnofU1jgNlcVb+UqynVFdoXMCFreTK7FdhqAzH0vmdwW5373Hm9pg== -"@esbuild/linux-arm@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz#0acef93aa3e0579e46d33b666627bddb06636664" - integrity sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ== +"@esbuild/linux-arm@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.6.tgz#50a537de609315979509120b0181882978294db1" + integrity sha512-G8IR5zFgpXad/Zp7gr7ZyTKyqZuThU6z1JjmRyN1vSF8j0bOlGzUwFSMTbctLAdd7QHpeyu0cRiuKrqK1ZTwvQ== "@esbuild/linux-ia32@0.18.20": version "0.18.20" @@ -902,10 +902,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.4.tgz#878cd8bf24c9847c77acdb5dd1b2ef6e4fa27a82" integrity sha512-EGc4vYM7i1GRUIMqRZNCTzJh25MHePYsnQfKDexD8uPTCm9mK56NIL04LUfX2aaJ+C9vyEp2fJ7jbqFEYgO9lQ== -"@esbuild/linux-ia32@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz#b6e5c9e80b42131cbd6b1ddaa48c92835f1ed67f" - integrity sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ== +"@esbuild/linux-ia32@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.6.tgz#f99c48b597facf9cbf8e1a2522ce379b2ad7b0c4" + integrity sha512-22eOR08zL/OXkmEhxOfshfOGo8P69k8oKHkwkDrUlcB12S/sw/+COM4PhAPT0cAYW/gpqY2uXp3TpjQVJitz7w== "@esbuild/linux-loong64@0.18.20": version "0.18.20" @@ -917,10 +917,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.4.tgz#df890499f6e566b7de3aa2361be6df2b8d5fa015" integrity sha512-WVhIKO26kmm8lPmNrUikxSpXcgd6HDog0cx12BUfA2PkmURHSgx9G6vA19lrlQOMw+UjMZ+l3PpbtzffCxFDRg== -"@esbuild/linux-loong64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz#e5f0cf95a180158b01ff5f417da796a1c09dfbea" - integrity sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw== +"@esbuild/linux-loong64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.6.tgz#9fe79be31ce305564aa62da190f38e199d6d26b7" + integrity sha512-82RvaYAh/SUJyjWA8jDpyZCHQjmEggL//sC7F3VKYcBMumQjUL3C5WDl/tJpEiKtt7XrWmgjaLkrk205zfvwTA== "@esbuild/linux-mips64el@0.18.20": version "0.18.20" @@ -932,10 +932,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.4.tgz#76eae4e88d2ce9f4f1b457e93892e802851b6807" integrity sha512-keYY+Hlj5w86hNp5JJPuZNbvW4jql7c1eXdBUHIJGTeN/+0QFutU3GrS+c27L+NTmzi73yhtojHk+lr2+502Mw== -"@esbuild/linux-mips64el@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz#ae36fb86c7d5f641f3a0c8472e83dcb6ea36a408" - integrity sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg== +"@esbuild/linux-mips64el@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.6.tgz#5a922dad90fc8a83fd0631c136b46128153ffb6f" + integrity sha512-8tvnwyYJpR618vboIv2l8tK2SuK/RqUIGMfMENkeDGo3hsEIrpGldMGYFcWxWeEILe5Fi72zoXLmhZ7PR23oQA== "@esbuild/linux-ppc64@0.18.20": version "0.18.20" @@ -947,10 +947,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.4.tgz#c49032f4abbcfa3f747b543a106931fe3dce41ff" integrity sha512-tQ92n0WMXyEsCH4m32S21fND8VxNiVazUbU4IUGVXQpWiaAxOBvtOtbEt3cXIV3GEBydYsY8pyeRMJx9kn3rvw== -"@esbuild/linux-ppc64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz#7960cb1666f0340ddd9eef7b26dcea3835d472d0" - integrity sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q== +"@esbuild/linux-ppc64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.6.tgz#a7fccf924824999b301546843adb4f51051965e8" + integrity sha512-Qt+D7xiPajxVNk5tQiEJwhmarNnLPdjXAoA5uWMpbfStZB0+YU6a3CtbWYSy+sgAsnyx4IGZjWsTzBzrvg/fMA== "@esbuild/linux-riscv64@0.18.20": version "0.18.20" @@ -962,10 +962,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.4.tgz#0f815a090772138503ee0465a747e16865bf94b1" integrity sha512-tRRBey6fG9tqGH6V75xH3lFPpj9E8BH+N+zjSUCnFOX93kEzqS0WdyJHkta/mmJHn7MBaa++9P4ARiU4ykjhig== -"@esbuild/linux-riscv64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz#32207df26af60a3a9feea1783fc21b9817bade19" - integrity sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag== +"@esbuild/linux-riscv64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.6.tgz#41d2db11550662d6c03902d9d8d26b0ed5bb8d55" + integrity sha512-lxRdk0iJ9CWYDH1Wpnnnc640ajF4RmQ+w6oHFZmAIYu577meE9Ka/DCtpOrwr9McMY11ocbp4jirgGgCi7Ls/g== "@esbuild/linux-s390x@0.18.20": version "0.18.20" @@ -977,10 +977,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.4.tgz#8d2cca20cd4e7c311fde8701d9f1042664f8b92b" integrity sha512-152aLpQqKZYhThiJ+uAM4PcuLCAOxDsCekIbnGzPKVBRUDlgaaAfaUl5NYkB1hgY6WN4sPkejxKlANgVcGl9Qg== -"@esbuild/linux-s390x@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz#b38d5681db89a3723862dfa792812397b1510a7d" - integrity sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw== +"@esbuild/linux-s390x@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.6.tgz#d7a843a2620e73c5c9d65c482e2fbddc7e0f7753" + integrity sha512-MopyYV39vnfuykHanRWHGRcRC3AwU7b0QY4TI8ISLfAGfK+tMkXyFuyT1epw/lM0pflQlS53JoD22yN83DHZgA== "@esbuild/linux-x64@0.18.20": version "0.18.20" @@ -992,10 +992,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.4.tgz#f618bec2655de49bff91c588777e37b5e3169d4a" integrity sha512-Mi4aNA3rz1BNFtB7aGadMD0MavmzuuXNTaYL6/uiYIs08U7YMPETpgNn5oue3ICr+inKwItOwSsJDYkrE9ekVg== -"@esbuild/linux-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz#46feba2ad041a241379d150f415b472fe3885075" - integrity sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A== +"@esbuild/linux-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.6.tgz#d3f20f0c2bdaa1b9ed1c0df7db034771e7aa5234" + integrity sha512-UWcieaBzsN8WYbzFF5Jq7QULETPcQvlX7KL4xWGIB54OknXJjBO37sPqk7N82WU13JGWvmDzFBi1weVBajPovg== "@esbuild/netbsd-x64@0.18.20": version "0.18.20" @@ -1007,10 +1007,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.4.tgz#7889744ca4d60f1538d62382b95e90a49687cef2" integrity sha512-9+Wxx1i5N/CYo505CTT7T+ix4lVzEdz0uCoYGxM5JDVlP2YdDC1Bdz+Khv6IbqmisT0Si928eAxbmGkcbiuM/A== -"@esbuild/netbsd-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz#3b5c1fb068f26bfc681d31f682adf1bea4ef0702" - integrity sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g== +"@esbuild/netbsd-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.6.tgz#6108d7270599ee37cd57bb14e4516a83541885d5" + integrity sha512-EpWiLX0fzvZn1wxtLxZrEW+oQED9Pwpnh+w4Ffv8ZLuMhUoqR9q9rL4+qHW8F4Mg5oQEKxAoT0G+8JYNqCiR6g== "@esbuild/openbsd-x64@0.18.20": version "0.18.20" @@ -1022,10 +1022,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.4.tgz#c3e436eb9271a423d2e8436fcb120e3fd90e2b01" integrity sha512-MFsHleM5/rWRW9EivFssop+OulYVUoVcqkyOkjiynKBCGBj9Lihl7kh9IzrreDyXa4sNkquei5/DTP4uCk25xw== -"@esbuild/openbsd-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz#ca6830316ca68056c5c88a875f103ad3235e00db" - integrity sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA== +"@esbuild/openbsd-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.6.tgz#b1b5aaa2c9028e90a2bef6774a9c67451f53f164" + integrity sha512-fFqTVEktM1PGs2sLKH4M5mhAVEzGpeZJuasAMRnvDZNCV0Cjvm1Hu35moL2vC0DOrAQjNTvj4zWrol/lwQ8Deg== "@esbuild/sunos-x64@0.18.20": version "0.18.20" @@ -1037,10 +1037,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.4.tgz#f63f5841ba8c8c1a1c840d073afc99b53e8ce740" integrity sha512-6Xq8SpK46yLvrGxjp6HftkDwPP49puU4OF0hEL4dTxqCbfx09LyrbUj/D7tmIRMj5D5FCUPksBbxyQhp8tmHzw== -"@esbuild/sunos-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz#9efc4eb9539a7be7d5a05ada52ee43cda0d8e2dd" - integrity sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg== +"@esbuild/sunos-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.6.tgz#b51b648cea77c62b1934a4fdcfee7aaa9de174cb" + integrity sha512-M+XIAnBpaNvaVAhbe3uBXtgWyWynSdlww/JNZws0FlMPSBy+EpatPXNIlKAdtbFVII9OpX91ZfMb17TU3JKTBA== "@esbuild/win32-arm64@0.18.20": version "0.18.20" @@ -1052,10 +1052,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.4.tgz#80be69cec92da4da7781cf7a8351b95cc5a236b0" integrity sha512-PkIl7Jq4mP6ke7QKwyg4fD4Xvn8PXisagV/+HntWoDEdmerB2LTukRZg728Yd1Fj+LuEX75t/hKXE2Ppk8Hh1w== -"@esbuild/win32-arm64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz#29f8184afa7a02a956ebda4ed638099f4b8ff198" - integrity sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg== +"@esbuild/win32-arm64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.6.tgz#34e5665f239047c302c8d153406c87db22afd58a" + integrity sha512-2DchFXn7vp/B6Tc2eKdTsLzE0ygqKkNUhUBCNtMx2Llk4POIVMUq5rUYjdcedFlGLeRe1uLCpVvCmE+G8XYybA== "@esbuild/win32-ia32@0.18.20": version "0.18.20" @@ -1067,10 +1067,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.4.tgz#15dc0ed83d2794872b05d8edc4a358fecf97eb54" integrity sha512-ga676Hnvw7/ycdKB53qPusvsKdwrWzEyJ+AtItHGoARszIqvjffTwaaW3b2L6l90i7MO9i+dlAW415INuRhSGg== -"@esbuild/win32-ia32@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz#f3de07afb292ecad651ae4bb8727789de2d95b05" - integrity sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw== +"@esbuild/win32-ia32@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.6.tgz#f7aaebe325e67f44c0a738e80a98221504677b4a" + integrity sha512-PBo/HPDQllyWdjwAVX+Gl2hH0dfBydL97BAH/grHKC8fubqp02aL4S63otZ25q3sBdINtOBbz1qTZQfXbP4VBg== "@esbuild/win32-x64@0.18.20": version "0.18.20" @@ -1082,10 +1082,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.4.tgz#d46a6e220a717f31f39ae80f49477cc3220be0f0" integrity sha512-HP0GDNla1T3ZL8Ko/SHAS2GgtjOg+VmWnnYLhuTksr++EnduYB0f3Y2LzHsUwb2iQ13JGoY6G3R8h6Du/WG6uA== -"@esbuild/win32-x64@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz#faad84c41ba12e3a0acb52571df9bff37bee75f6" - integrity sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw== +"@esbuild/win32-x64@0.19.6": + version "0.19.6" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.6.tgz#7134e5dea1f5943b013e96fc34f9638a5f3d7e3e" + integrity sha512-OE7yIdbDif2kKfrGa+V0vx/B3FJv2L4KnIiLlvtibPyO9UkgO3rzYE0HhpREo2vmJ1Ixq1zwm9/0er+3VOSZJA== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -1119,10 +1119,10 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.48.0.tgz#642633964e217905436033a2bd08bf322849b7fb" integrity sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw== -"@eslint/js@8.53.0": - version "8.53.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.53.0.tgz#bea56f2ed2b5baea164348ff4d5a879f6f81f20d" - integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w== +"@eslint/js@8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.54.0.tgz#4fab9a2ff7860082c304f750e94acd644cf984cf" + integrity sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ== "@ethereum-waffle/chai@^3.4.4": version "3.4.4" @@ -2749,11 +2749,11 @@ semver "^7.3.8" "@netlify/functions-utils@^5.2.37": - version "5.2.40" - resolved "https://registry.yarnpkg.com/@netlify/functions-utils/-/functions-utils-5.2.40.tgz#093d6acc512c5dd0b5814ab4402d916ff5a44fb6" - integrity sha512-5mx23k4WW9R2WvBDa9mVCz0n3CAUUVbyoa7zOOjVRj2wuZAqnP9gTQa/g4Uhg3GvDxp90kHlDe5Amfc2K4j3MQ== + version "5.2.41" + resolved "https://registry.yarnpkg.com/@netlify/functions-utils/-/functions-utils-5.2.41.tgz#88bd31d0e8754724c1fffd5f687b3e78215e0d09" + integrity sha512-rvp11NquyVQ4d5rK6W6cP4M3iKyuOATqfEGlC7jLUZjMeNp4bQ5gPb5RaqqG5MHPY0KmdELMGGGgUxmCbh+Qxw== dependencies: - "@netlify/zip-it-and-ship-it" "9.26.1" + "@netlify/zip-it-and-ship-it" "9.26.2" cpy "^9.0.0" path-exists "^5.0.0" @@ -2869,9 +2869,9 @@ execa "^6.0.0" "@netlify/serverless-functions-api@^1.10.1", "@netlify/serverless-functions-api@^1.12.0": - version "1.12.0" - resolved "https://registry.yarnpkg.com/@netlify/serverless-functions-api/-/serverless-functions-api-1.12.0.tgz#b8ba310e6fdac9bf1f0e974856eb60f97d294e4d" - integrity sha512-LJt2gHzLQMgJLsLG9Chbu2Pxxi7Yzbj3Xcd9QlThvUlD7kf4nAr3lzzRJMZqo77rVNmfQX11W1uvGMSlduiKeA== + version "1.12.1" + resolved "https://registry.yarnpkg.com/@netlify/serverless-functions-api/-/serverless-functions-api-1.12.1.tgz#fb926f62377d84719c70f9a4493f3d04d6f6e7dc" + integrity sha512-+G9cTltqfH54dF4dLqoEOV2P4qTIY8dM9blUVqg+NtVTXyuadzgpHqtffhVeyeLytVnTx1238kWJUe+sV3bnlg== dependencies: "@netlify/node-cookies" "^0.1.0" urlpattern-polyfill "8.0.2" @@ -2914,13 +2914,13 @@ urlpattern-polyfill "8.0.2" yargs "^17.0.0" -"@netlify/zip-it-and-ship-it@9.26.1": - version "9.26.1" - resolved "https://registry.yarnpkg.com/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-9.26.1.tgz#fbd4380c25a1bc62fb4e138310fa1eba89033de1" - integrity sha512-1hJcNIqXU7y5TgAACaRZp4vu3Bknl2pD6Aq3WOdY4T1zuB/SyKOn8Im6wwYElpc4n5LDGIOPyJlLDNo+DOwoWA== +"@netlify/zip-it-and-ship-it@9.26.2": + version "9.26.2" + resolved "https://registry.yarnpkg.com/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-9.26.2.tgz#e531499922ed4e46fc5af603c5db2528ac3f6452" + integrity sha512-tsQbSfgOTEfZmSnUbCJiHDVyYDRN1gQQEWjAmJ90YI60ZloT4j7B4HlBt0gshU9pPCiDxoHhQMCk5pHg7//CSw== dependencies: "@babel/parser" "^7.22.5" - "@babel/types" "7.23.0" + "@babel/types" "7.23.3" "@netlify/binary-info" "^1.0.0" "@netlify/serverless-functions-api" "^1.12.0" "@vercel/nft" "^0.23.0" @@ -2928,7 +2928,7 @@ common-path-prefix "^3.0.0" cp-file "^10.0.0" es-module-lexer "^1.0.0" - esbuild "0.19.5" + esbuild "0.19.6" execa "^6.0.0" filter-obj "^5.0.0" find-up "^6.0.0" @@ -3211,7 +3211,7 @@ "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" -"@nomiclabs/hardhat-ethers@^2.0.0", "@nomiclabs/hardhat-ethers@^2.0.2", "@nomiclabs/hardhat-ethers@^2.2.1": +"@nomiclabs/hardhat-ethers@^2.0.0", "@nomiclabs/hardhat-ethers@^2.0.2", "@nomiclabs/hardhat-ethers@^2.2.3": version "2.2.3" resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz#b41053e360c31a32c2640c9a45ee981a7e603fe0" integrity sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg== @@ -4483,9 +4483,9 @@ "@types/chai" "*" "@types/chai@*", "@types/chai@^4.2.11", "@types/chai@^4.3.4": - version "4.3.10" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.10.tgz#2ad2959d1767edee5b0e4efb1a0cd2b500747317" - integrity sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg== + version "4.3.11" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c" + integrity sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ== "@types/cli-progress@^3.11.0": version "3.11.5" @@ -4589,9 +4589,9 @@ integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== "@types/luxon@^3.1.0": - version "3.3.4" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.4.tgz#cda5c0709a0c4c01ba059c40e62d76610479049a" - integrity sha512-H9OXxv4EzJwE75aTPKpiGXJq+y4LFxjpsdgKwSmr503P5DkWc3AG7VAFYrFNVvqemT5DfgZJV9itYhqBHSGujA== + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.5.tgz#ffdcec196994998dbef6284523b3ac88a9e6c45f" + integrity sha512-1cyf6Ge/94zlaWIZA2ei1pE6SZ8xpad2hXaYa5JEFiaUH0YS494CZwyi4MXNpXD9oEuv6ZH0Bmh0e7F9sPhmZA== "@types/minimatch@^3.0.4": version "3.0.5" @@ -4624,9 +4624,9 @@ form-data "^4.0.0" "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "20.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.1.tgz#9d578c610ce1e984adda087f685ace940954fe19" - integrity sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA== + version "20.9.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.3.tgz#e089e1634436f676ff299596c9531bd2b59fffc6" + integrity sha512-nk5wXLAXGBKfrhLB0cyHGbSqopS+nz0BUgZkUQqSHSSgdee0kssp1IAqlQOu333bW+gMNs2QREx7iynm19Abxw== dependencies: undici-types "~5.26.4" @@ -4724,9 +4724,9 @@ "@types/node" "*" "@types/semver@^7.3.12": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35" - integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg== + version "7.5.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== "@types/triple-beam@^1.3.2": version "1.3.5" @@ -4734,9 +4734,9 @@ integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== "@types/trusted-types@^2.0.2": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.6.tgz#d12451beaeb9c3838f12024580dc500b7e88b0ad" - integrity sha512-HYtNooPvUY9WAVRBr4u+4Qa9fYD1ze2IUlAD3HoA6oehn1taGwBx3Oa52U4mTslTS+GAExKpaFu39Y5xUEwfjg== + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== "@types/web-bluetooth@^0.0.16": version "0.0.16" @@ -4756,9 +4756,9 @@ "@types/node" "*" "@types/ws@^8.0.0": - version "8.5.9" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.9.tgz#384c489f99c83225a53f01ebc3eddf3b8e202a8c" - integrity sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg== + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" @@ -4768,9 +4768,9 @@ integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^16.0.0": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.8.tgz#0d57a5a491d85ae75d372a32e657b1779b86c65d" - integrity sha512-1GwLEkmFafeb/HbE6pC7tFlgYSQ4Iqh2qlWCq8xN+Qfaiaxr2PcLfuhfRFRYqI6XJyeFoLYyKnhFbNsst9FMtQ== + version "16.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" + integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== dependencies: "@types/yargs-parser" "*" @@ -6965,9 +6965,9 @@ bfj@^7.0.2: tryer "^1.0.1" big-integer@^1.6.42, big-integer@^1.6.48: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== bigint-crypto-utils@^3.0.23: version "3.3.0" @@ -7644,9 +7644,9 @@ camelcase@^7.0.1: integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: - version "1.0.30001562" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz#9d16c5fd7e9c592c4cd5e304bc0f75b0008b2759" - integrity sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng== + version "1.0.30001563" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz#aa68a64188903e98f36eb9c56e48fba0c1fe2a32" + integrity sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw== capital-case@^1.0.4: version "1.0.4" @@ -8015,9 +8015,9 @@ circomlibjs@^0.1.7: ffjavascript "^0.2.45" citty@^0.1.3, citty@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.4.tgz#91091be06ae4951dffa42fd443de7fe72245f2e0" - integrity sha512-Q3bK1huLxzQrvj7hImJ7Z1vKYJRPQCDnd0EjXfHMidcjecGOMuLrmuQmtWmFkuKLcMThlGh1yCKG8IEc6VeNXQ== + version "0.1.5" + resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.5.tgz#fe37ceae5dc764af75eb2fece99d2bf527ea4e50" + integrity sha512-AS7n5NSc0OQVMV9v6wt3ByujNIrne0/cTjiC2MYqhvao57VNfiuVksTSr2p17nVOhEr2KtqiAkGwHcgMC/qUuQ== dependencies: consola "^3.2.3" @@ -8589,9 +8589,9 @@ copy-template-dir@1.4.0: run-parallel "^1.1.4" core-js-pure@^3.0.1: - version "3.33.2" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.33.2.tgz#644830db2507ef84d068a70980ccd99c275f5fa6" - integrity sha512-a8zeCdyVk7uF2elKIGz67AjcXOxjRbwOLz8SbklEso1V+2DoW4OkAMZN9S9GBgvZIaqQi/OemFX4OiSoQEmg1Q== + version "3.33.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.33.3.tgz#cbf9180ac4c4653823d784862bfb5c77eac0bf98" + integrity sha512-taJ00IDOP+XYQEA2dAe4ESkmHt1fL8wzYDo3mRWQey8uO9UojlBFMneA65kMyxfYP7106c6LzWaq7/haDT6BCQ== core-js@^2.4.0, core-js@^2.5.0: version "2.6.12" @@ -9163,7 +9163,7 @@ defined@~1.0.1: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== -defu@^6.1.2: +defu@^6.1.2, defu@^6.1.3: version "6.1.3" resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.3.tgz#6d7f56bc61668e844f9f593ace66fd67ef1205fd" integrity sha512-Vy2wmG3NTkmHNg/kzpuvHhkqeIx3ODWqasgCRbKtbXEN0G+HpEEv9BtJLp7ZG1CZloFaC41Ah3ZFbq7aqCqMeQ== @@ -9540,9 +9540,9 @@ electron-fetch@^1.7.2: encoding "^0.1.13" electron-to-chromium@^1.3.47, electron-to-chromium@^1.4.535: - version "1.4.587" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.587.tgz#d8b864f21338b60798d447a3d83b90753f701d07" - integrity sha512-RyJX0q/zOkAoefZhB9XHghGeATVP0Q3mwA253XD/zj2OeXc+JZB9pCaEv6R578JUYaWM9PRhye0kXvd/V1cQ3Q== + version "1.4.590" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.590.tgz#85a428fbabb77265a4804040837ed4f2538e3300" + integrity sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww== elegant-spinner@^1.0.1: version "1.0.1" @@ -9867,33 +9867,33 @@ esbuild@0.19.4: "@esbuild/win32-ia32" "0.19.4" "@esbuild/win32-x64" "0.19.4" -esbuild@0.19.5: - version "0.19.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.5.tgz#53a0e19dfbf61ba6c827d51a80813cf071239a8c" - integrity sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ== +esbuild@0.19.6: + version "0.19.6" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.6.tgz#baa0e8b6b9e655c54ffd57f1772e44677a7931cc" + integrity sha512-Xl7dntjA2OEIvpr9j0DVxxnog2fyTGnyVoQXAMQI6eR3mf9zCQds7VIKUDCotDgE/p4ncTgeRqgX8t5d6oP4Gw== optionalDependencies: - "@esbuild/android-arm" "0.19.5" - "@esbuild/android-arm64" "0.19.5" - "@esbuild/android-x64" "0.19.5" - "@esbuild/darwin-arm64" "0.19.5" - "@esbuild/darwin-x64" "0.19.5" - "@esbuild/freebsd-arm64" "0.19.5" - "@esbuild/freebsd-x64" "0.19.5" - "@esbuild/linux-arm" "0.19.5" - "@esbuild/linux-arm64" "0.19.5" - "@esbuild/linux-ia32" "0.19.5" - "@esbuild/linux-loong64" "0.19.5" - "@esbuild/linux-mips64el" "0.19.5" - "@esbuild/linux-ppc64" "0.19.5" - "@esbuild/linux-riscv64" "0.19.5" - "@esbuild/linux-s390x" "0.19.5" - "@esbuild/linux-x64" "0.19.5" - "@esbuild/netbsd-x64" "0.19.5" - "@esbuild/openbsd-x64" "0.19.5" - "@esbuild/sunos-x64" "0.19.5" - "@esbuild/win32-arm64" "0.19.5" - "@esbuild/win32-ia32" "0.19.5" - "@esbuild/win32-x64" "0.19.5" + "@esbuild/android-arm" "0.19.6" + "@esbuild/android-arm64" "0.19.6" + "@esbuild/android-x64" "0.19.6" + "@esbuild/darwin-arm64" "0.19.6" + "@esbuild/darwin-x64" "0.19.6" + "@esbuild/freebsd-arm64" "0.19.6" + "@esbuild/freebsd-x64" "0.19.6" + "@esbuild/linux-arm" "0.19.6" + "@esbuild/linux-arm64" "0.19.6" + "@esbuild/linux-ia32" "0.19.6" + "@esbuild/linux-loong64" "0.19.6" + "@esbuild/linux-mips64el" "0.19.6" + "@esbuild/linux-ppc64" "0.19.6" + "@esbuild/linux-riscv64" "0.19.6" + "@esbuild/linux-s390x" "0.19.6" + "@esbuild/linux-x64" "0.19.6" + "@esbuild/netbsd-x64" "0.19.6" + "@esbuild/openbsd-x64" "0.19.6" + "@esbuild/sunos-x64" "0.19.6" + "@esbuild/win32-arm64" "0.19.6" + "@esbuild/win32-ia32" "0.19.6" + "@esbuild/win32-x64" "0.19.6" esbuild@^0.18.10: version "0.18.20" @@ -10083,14 +10083,14 @@ eslint@8.48.0: text-table "^0.2.0" eslint@^8.28.0, eslint@^8.31.0: - version "8.53.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.53.0.tgz#14f2c8244298fcae1f46945459577413ba2697ce" - integrity sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag== + version "8.54.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.54.0.tgz#588e0dd4388af91a2e8fa37ea64924074c783537" + integrity sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.3" - "@eslint/js" "8.53.0" + "@eslint/js" "8.54.0" "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -12310,16 +12310,16 @@ gtoken@^5.0.4: jws "^4.0.0" h3@^1.8.1, h3@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/h3/-/h3-1.8.2.tgz#69ea8ca0285c1bb268cd08b9a7017e02939f88b7" - integrity sha512-1Ca0orJJlCaiFY68BvzQtP2lKLk46kcLAxVM8JgYbtm2cUg6IY7pjpYgWMwUvDO9QI30N5JAukOKoT8KD3Q0PQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/h3/-/h3-1.9.0.tgz#c5f512a93026df9837db6f30c9ef51135dd46752" + integrity sha512-+F3ZqrNV/CFXXfZ2lXBINHi+rM4Xw3CDC5z2CDK3NMPocjonKipGLLDSkrqY9DOrioZNPTIdDMWfQKm//3X2DA== dependencies: cookie-es "^1.0.0" - defu "^6.1.2" - destr "^2.0.1" - iron-webcrypto "^0.10.1" + defu "^6.1.3" + destr "^2.0.2" + iron-webcrypto "^1.0.0" radix3 "^1.1.0" - ufo "^1.3.0" + ufo "^1.3.2" uncrypto "^0.1.3" unenv "^1.7.4" @@ -12362,7 +12362,7 @@ hardhat-contract-sizer@^2.10.0, hardhat-contract-sizer@^2.6.1: cli-table3 "^0.6.0" strip-ansi "^6.0.0" -hardhat@^2.12.5, hardhat@^2.12.6: +hardhat@^2.12.6, hardhat@^2.19.1: version "2.19.1" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.1.tgz#5e09e8070ecfc6109ba9d3a4a117ec2b0643032a" integrity sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw== @@ -13333,10 +13333,10 @@ ipld@^0.25.3: multicodec "^1.0.0" typical "^6.0.0" -iron-webcrypto@^0.10.1: - version "0.10.1" - resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-0.10.1.tgz#cab8636a468685533a8521bfd7f06b19b7174809" - integrity sha512-QGOS8MRMnj/UiOa+aMIgfyHcvkhqNUsUxb1XzskENvbo+rEfp6TOwqd1KPuDzXC4OnGHcMSVxDGRoilqB8ViqA== +iron-webcrypto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-1.0.0.tgz#e3b689c0c61b434a0a4cb82d0aeabbc8b672a867" + integrity sha512-anOK1Mktt8U1Xi7fCM3RELTuYbnFikQY5VtrDj7kPgpejV7d43tWKhzgioO0zpkazLEL/j/iayRqnJhrGfqUsg== is-absolute@^1.0.0: version "1.0.0" @@ -14779,11 +14779,16 @@ light-my-request@^5.6.1: process-warning "^2.0.0" set-cookie-parser "^2.4.1" -lilconfig@^2.0.5, lilconfig@^2.1.0: +lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== +lilconfig@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" + integrity sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -15302,11 +15307,9 @@ lru-cache@5.1.1, lru-cache@^5.1.1: yallist "^3.0.2" lru-cache@^10.0.2, "lru-cache@^9.1.1 || ^10.0.0": - version "10.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.2.tgz#34504678cc3266b09b8dfd6fab4e1515258271b7" - integrity sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg== - dependencies: - semver "^7.3.5" + version "10.0.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.3.tgz#b40014d7d2d16d94130b87297a04a1f24874ae7c" + integrity sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg== lru-cache@^3.2.0: version "3.2.0" @@ -16537,9 +16540,9 @@ node-forge@^1.3.1: integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e" - integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ== + version "4.7.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.0.tgz#749f0033590b2a89ac8edb5e0775f95f5ae86d15" + integrity sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg== node-gyp-build@~4.1.0: version "4.1.1" @@ -17765,12 +17768,12 @@ postcss-js@^4.0.1: camelcase-css "^2.0.1" postcss-load-config@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" - integrity sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA== + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== dependencies: - lilconfig "^2.0.5" - yaml "^2.1.1" + lilconfig "^3.0.0" + yaml "^2.3.4" postcss-nested@^6.0.1: version "6.0.1" @@ -17915,9 +17918,9 @@ process-warning@^1.0.0: integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== process-warning@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.3.0.tgz#02ccba12fd55c05879cb7ddab31b6a20af2fe4d9" - integrity sha512-N6mp1+2jpQr3oCFMz6SeHRGbv6Slb20bRhj4v3xR99HqNToAcOe1MFOp4tytyzOfJn+QtN8Rf7U/h2KAn4kC6g== + version "2.3.1" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.3.1.tgz#0caf992272c439f45dd416e1407ee25a3d4c778a" + integrity sha512-JjBvFEn7MwFbzUDa2SRtKJSsyO0LlER4V/FmwLMhBlXNbGgGxdyFCxIdMDLerWUycsVUyaoM9QFLvppFy4IWaQ== process@^0.10.0: version "0.10.1" @@ -19657,7 +19660,7 @@ source-map-support@0.5.12: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@0.5.21, source-map-support@^0.5.11, source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.20, source-map-support@^0.5.21: +source-map-support@0.5.21, source-map-support@^0.5.11, source-map-support@^0.5.13, source-map-support@^0.5.19, source-map-support@^0.5.20, source-map-support@^0.5.21: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -20804,17 +20807,6 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -ts-node@^8.8.1: - version "8.10.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" - integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.17" - yn "3.1.1" - tslib@1.14.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -21026,9 +21018,9 @@ typescript@^4.2.3, typescript@^4.9.3: integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== typescript@^5.0.0, typescript@^5.0.4: - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + version "5.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" + integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== typewise-core@^1.2, typewise-core@^1.2.0: version "1.2.0" @@ -21067,7 +21059,7 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -ufo@^1.3.0, ufo@^1.3.1: +ufo@^1.3.0, ufo@^1.3.1, ufo@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.2.tgz#c7d719d0628a1c80c006d2240e0d169f6e3c0496" integrity sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA== @@ -22774,7 +22766,7 @@ yaml@1.10.2, yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.1.1, yaml@^2.1.3: +yaml@^2.1.3, yaml@^2.3.4: version "2.3.4" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==