squash and merge

This commit is contained in:
daodesigner
2022-01-10 11:49:31 -05:00
commit 33c81dde4a
58 changed files with 28224 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/abi
/cache
/coverage
/typechain
/coverage.json
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
packages/.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

6
lerna.json Normal file
View File

@@ -0,0 +1,6 @@
{
"packages": ["packages/*"],
"version": "0.0.0",
"npmClient": "yarn",
"useWorkspaces": true
}

6
package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "qfi",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

41
package.json Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "@qfi/qfi",
"description": "Quadratic Funding Infrastructure",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "git+https://github.com/"
},
"private": true,
"devDependencies": {
"lerna": "^4.0.0"
},
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/@nomiclabs/**",
"**/gitbook-cli/**",
"**/fs-extra/**"
]
},
"scripts": {
"build": "yarn workspaces run build",
"build:contracts": "yarn workspace @qfi/contracts run build",
"build:subgraph": "yarn workspace @qfi/subgraph run build",
"build:ui": "yarn workspace @qfi/ui run build",
"build:hooks": "yarn workspace @qfi/hooks run build",
"test": "yarn workspaces run test",
"test:contracts": "yarn workspace @qfi/contracts run test",
"deploy:subgraph":"yarn workspace @qfi/subgraph run deploy"
},
"keywords": [
"RxC"
],
"author": "daodesigner",
"bugs": {
"url": "https://github.com/"
},
"homepage": "https://github.com/"
}

23
packages/app/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

30
packages/contracts/.gitignore vendored Normal file
View File

@@ -0,0 +1,30 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/abi
/abi/maci-contracts/contracts/**/*.json
/cache
/coverage
/typechain
/coverage.json
/docs/node_modules
/docs/_book
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -0,0 +1,11 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", "^0.7.2"],
"not-rely-on-time": "off",
"quotes": ["error", "single"],
"reason-string": ["warn", { "maxLength": 64 }],
"max-states-count": ["warn", 18],
"check-send-result": "off"
}
}

View File

@@ -0,0 +1 @@
contracts/snarkVerifiers/

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.2;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
contract BaseERC20Token is ERC20 {
constructor(uint256 initialSupply)
ERC20('Any old ERC20 token', 'AOE')
{
_mint(msg.sender, initialSupply);
}
}

View File

@@ -0,0 +1,99 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.7.2;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/utils/EnumerableSet.sol';
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import {GrantRound} from './GrantRound.sol';
/**
* @title Funds Manager
* @author Q
* @notice Handles funding sources and donations for Grant rounds
* @dev Responsible for sending matching funds to the Grant round contract
*/
contract FundsManager is Ownable{
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for ERC20;
EnumerableSet.AddressSet private fundingSources;
constructor() {}
/**
* @dev Add matching funds source.
* @param _source Address of a funding source.
*/
function addFundingSource(address _source) external {
bool result = fundingSources.add(_source);
require(result, 'Factory: Funding source already added');
// emit FundingSourceAdded(_source);
}
/**
* @dev Remove matching funds source.
* @param _source Address of the funding source.
*/
function removeFundingSource(address _source) external {
bool result = fundingSources.remove(_source);
require(result, 'Factory: Funding source not found');
// emit FundingSourceRemoved(_source);
}
/**
* @dev Get total amount of matching funds.
*/
function getMatchingFunds(ERC20 token) external view returns (uint256) {
uint256 matchingPoolSize = token.balanceOf(address(this));
for (uint256 index = 0; index < fundingSources.length(); index++) {
address fundingSource = fundingSources.at(index);
uint256 allowance = token.allowance(fundingSource, address(this));
uint256 balance = token.balanceOf(fundingSource);
uint256 contribution = allowance < balance ? allowance : balance;
matchingPoolSize += contribution;
}
return matchingPoolSize;
}
/*
* @dev Transfer funds from matching pool to current funding round and finalize it.
* @param _totalSpent Total amount of spent voice credits.
* @param _totalSpentSalt The salt.
*/
function transferMatchingFunds(
// uint256 _totalSpent,
// uint256 _totalSpentSalt,
GrantRound currentRound
) internal {
require(
address(currentRound) != address(0),
'Factory: Funding round has not been deployed'
);
ERC20 roundToken = currentRound.nativeToken();
// Factory contract is the default funding source
uint256 matchingPoolSize = roundToken.balanceOf(address(this));
if (matchingPoolSize > 0) {
roundToken.safeTransfer(address(currentRound), matchingPoolSize);
}
// Pull funds from other funding sources
for (uint256 index = 0; index < fundingSources.length(); index++) {
address fundingSource = fundingSources.at(index);
uint256 allowance = roundToken.allowance(
fundingSource,
address(this)
);
uint256 balance = roundToken.balanceOf(fundingSource);
uint256 contribution = allowance < balance ? allowance : balance;
if (contribution > 0) {
roundToken.safeTransferFrom(
fundingSource,
address(currentRound),
contribution
);
}
}
// currentRound.finalize(_totalSpent, _totalSpentSalt);
// emit RoundFinalized(address(currentRound));
}
}

View File

@@ -0,0 +1,245 @@
//SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.2;
import {PollFactory, Poll, PollProcessorAndTallyer} from "maci-contracts/contracts/Poll.sol";
import {VkRegistry} from "maci-contracts/contracts/VkRegistry.sol";
import {Params} from "maci-contracts/contracts/Params.sol";
import {IMACI} from "maci-contracts/contracts/IMACI.sol";
import {AccQueue} from "maci-contracts/contracts/trees/AccQueue.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {IRecipientRegistry} from "./recipientRegistry/IRecipientRegistry.sol";
/**
* @title Quadratic Funding Round Contract
* @author Q
* @notice This contract manages contributions, withdrawals, Voting and the distribution of funds for a particular grant round.
* @dev Inherits from Poll Contract and uses the Poll Contract interface to manage the voting.
*/
contract GrantRound is Poll {
using SafeERC20 for ERC20;
uint256 public voiceCreditFactor;
address public coordinator;
ERC20 public nativeToken;
IRecipientRegistry public recipientRegistry;
mapping(uint256 => bool) private recipients;
bool public isCancelled;
bool public isFinalized;
string public tallyHash;
uint256 public totalSpent;
uint256 public totalVotes;
uint256 public matchingPoolSize;
/**
* @notice Constructor for the GrantRound, special type of poll that implements Quadratic Funding.
* @dev Binds the contracts that will be used to tally the Poll and sets duration ans coordinator.
* @param _voiceCreditFactor uint256 The factor by which the voiceCredit is multiplied to determine the amount of voice credits that are distributed to the contributor.
* @param _coordinator The address of the coordinator of the GrantRound.
* @param _nativeToken The address of the ERC20 token used for the GrantRound.
* @param _duration uint256, the duration of the GrantRound
* @param _maxValues MaxValues stored in memory, the maxMessages and maxVoteOptions of the GrantRound as uint256 values
* @param _treeDepths TreeDepths stored in memory, intStateTreeDepth, messageTreeSubDepth, messageTreeDepth, and voteOptionTreeDepth as uint8 values
* @param _batchSizes BatchSizes stored in memory, this inlcudes the message batch size and the tally batch size
* @param _coordinatorPubKey PubKey stored in memory, MACI pubkey of the coordinator of the GrantRounds
* @param _extContracts ExtContracts stored in memory, this includes the maci, and messageAq contracts that will be used to tally the Poll.
*/
constructor(
uint256 _voiceCreditFactor,
address _coordinator,
ERC20 _nativeToken,
uint256 _duration,
MaxValues memory _maxValues,
TreeDepths memory _treeDepths,
BatchSizes memory _batchSizes,
PubKey memory _coordinatorPubKey,
ExtContracts memory _extContracts,
IRecipientRegistry _recipientRegistry
)
Poll(
_duration,
_maxValues,
_treeDepths,
_batchSizes,
_coordinatorPubKey,
_extContracts
)
{
voiceCreditFactor = _voiceCreditFactor;
coordinator = _coordinator;
nativeToken = _nativeToken;
recipientRegistry = _recipientRegistry;
}
/**
* @notice Allows anyone to publish a batch of messages (an encrypted command and signature).
* @dev This function also enqueues the messages.
* @param _messages Message[] The messages to publish as an array of Message structs.
* @param _encPubKeys PubKey[] An array of epheremal public keys which can be combined with the
* coordinator's private key to generate an ECDH shared key with which
* to encrypt the message.
*/
function publishMessageBatch(
Message[] calldata _messages,
PubKey[] calldata _encPubKeys
) external {
uint256 batchSize = _messages.length;
for (uint8 i = 0; i < batchSize; i++) {
publishMessage(_messages[i], _encPubKeys[i]);
}
}
/**
* @dev Publish the IPFS hash of the vote tally. Only coordinator can publish.
* @param _tallyHash IPFS hash of the vote tally.
*/
function publishTallyHash(string calldata _tallyHash) external {
require(
msg.sender == coordinator,
"GrantRound: Sender is not the coordinator"
);
require(!isFinalized, "GrantRound: Round finalized");
require(
bytes(_tallyHash).length != 0,
"GrantRound: Tally hash is empty string"
);
tallyHash = _tallyHash;
// emit TallyPublished(_tallyHash);
}
/*
* @dev Get the total amount of votes from MACI,
* verify the total amount of spent voice credits across all recipients,
* and allow recipients to claim funds.
* @param _totalSpent Total amount of spent voice credits.
* @param _totalSpentSalt The salt.
*/
function finalize(uint256 _totalSpent, uint256 _totalSpentSalt)
external
onlyOwner
isAfterVotingDeadline
{
require(!isFinalized, "GrantRound: Already finalized");
require(stateAqMerged, "GrantRound: State AQ not merged");
require(
address(extContracts.maci) != address(0),
"GrantRound: MACI not deployed"
);
require(
bytes(tallyHash).length != 0,
"GrantRound: Tally hash has not been published"
);
// If nobody voted, the round should be cancelled to avoid locking of matching funds
require(numMessages > 0, "GrantRound: No votes");
//TODO: Actually verify using new decorators
bool verified = verifySpentVoiceCredits(_totalSpent, _totalSpentSalt);
require(
verified,
"GrantRound: Incorrect total amount of spent voice credits"
);
totalSpent = _totalSpent;
// Total amount of spent voice credits is the size of the pool of direct rewards.
// Everything else, including unspent voice credits and downscaling error,
// is considered a part of the matching pool
matchingPoolSize =
nativeToken.balanceOf(address(this)) -
totalSpent *
voiceCreditFactor;
isFinalized = true;
}
/**
* @dev Cancel funding round.
*/
function cancel() external onlyOwner {
require(!isFinalized, "GrantRound: Already finalized");
isFinalized = true;
isCancelled = true;
}
/**
* @dev Get allocated token amount (without verification).
* @param _tallyResult The result of vote tally for the recipient.
* @param _spent The amount of voice credits spent on the recipient.
*/
function getAllocatedAmount(uint256 _tallyResult, uint256 _spent)
public
view
returns (uint256)
{
return
(matchingPoolSize * _tallyResult) /
totalVotes +
_spent *
voiceCreditFactor;
}
/*
* @dev Claim allocated tokens.
* @param _voteOptionIndex Vote option index.
* @param _tallyResult The result of vote tally for the recipient.
* @param _tallyResultProof Proof of correctness of the vote tally.
* @param _tallyResultSalt Salt.
* @param _spent The amount of voice credits spent on the recipient.
* @param _spentProof Proof of correctness for the amount of spent credits.
* @param _spentSalt Salt.
*/
function claimFunds(
uint256 _voteOptionIndex,
uint256 _tallyResult,
uint256[][] memory _tallyResultProof,
uint256 _spentVoiceCreditsHash,
uint256 _perVOSpentVoiceCreditsHash,
uint256 _tallyCommitment,
uint256 _spent,
uint256[][] memory _spentProof,
uint256 _spentSalt
) external {
require(isFinalized, "GrantRound: Round not finalized");
require(!isCancelled, "GrantRound: Round has been cancelled");
require(
!recipients[_voteOptionIndex],
"FundingRound: Funds already claimed"
);
{
bool resultVerified = verifyTallyResult(
_voteOptionIndex,
_tallyResult,
_tallyResultProof,
_spentVoiceCreditsHash,
_perVOSpentVoiceCreditsHash,
_tallyCommitment
);
require(resultVerified, "FundingRound: Incorrect tally result");
bool spentVerified = verifyPerVOSpentVoiceCredits(
_voteOptionIndex,
_spent,
_spentProof,
_spentSalt
);
require(
spentVerified,
"FundingRound: Incorrect amount of spent voice credits"
);
}
recipients[_voteOptionIndex] = true;
(uint256 deployTime, uint256 duration) = getDeployTimeAndDuration();
address recipient = recipientRegistry.getRecipientAddress(
_voteOptionIndex,
deployTime,
deployTime + duration
);
if (recipient == address(0)) {
// Send funds back to the matching pool
recipient = owner();
}
uint256 allocatedAmount = getAllocatedAmount(_tallyResult, _spent);
nativeToken.safeTransfer(recipient, allocatedAmount);
}
}

View File

@@ -0,0 +1,148 @@
//SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.2;
import {PollFactory, Poll, MessageAqFactory, PollDeploymentParams} from "maci-contracts/contracts/Poll.sol";
import {VkRegistry} from "maci-contracts/contracts/VkRegistry.sol";
import {Params} from "maci-contracts/contracts/Params.sol";
import {Hasher} from "maci-contracts/contracts/crypto/Hasher.sol";
import {IMACI} from "maci-contracts/contracts/IMACI.sol";
import {AccQueue} from "maci-contracts/contracts/trees/AccQueue.sol";
import {DomainObjs, IPubKey, IMessage} from "maci-contracts/contracts/DomainObjs.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {GrantRound} from "./GrantRound.sol";
import {IRecipientRegistry} from "./recipientRegistry/IRecipientRegistry.sol";
/**
* @title Quadratic Funding Round Factory Contracts
* @author Q
* @notice Deploys new Quadratic Funding Round Contracts
* @dev Factory Contract to deploy a special type of Poll called GrantRound.
*/
contract GrantRoundFactory is
Params,
IPubKey,
IMessage,
Ownable,
Hasher,
PollDeploymentParams
{
using SafeERC20 for ERC20;
MessageAqFactory public messageAqFactory;
IRecipientRegistry public recipientRegistry;
constructor() {
}
/**
* @notice Sets the MessageAqFactory to use for the grant rounds
* @dev public function, _messageAqFactory should deploy new AccQueueQuinaryMaci AccQueue(s), need to set before calling deployGrantRound
* @param _messageAqFactory MessageAqFactory stored in memory
*/
function setMessageAqFactory(MessageAqFactory _messageAqFactory)
public
onlyOwner
{
messageAqFactory = _messageAqFactory;
}
/**
* @notice Sets the recipientRegistry to use for the grant rounds
* @dev public function,
* @param _recipientRegistry IRecipientRegistry stored in memory
*/
function setRecipientRegistry(IRecipientRegistry _recipientRegistry)
public
onlyOwner
{
recipientRegistry = _recipientRegistry;
}
/**
* @notice Deploys a new GrantRound Contract
* @dev public function
* @param _voiceCreditFactor uint256 The factor by which the voiceCredit is multiplied to determine the amount of voice credits that are distributed to the contributor.
* @param _coordinator The address of the coordinator of the GrantRound.
* @param _nativeToken The address of the ERC20 token used for the GrantRound.
* @param _duration uint256 stored in memory, the duration of the GrantRound
* @param _maxValues MaxValues stored in memory, the maxMessages and maxVoteOptions of the GrantRound as uint256 values
* @param _treeDepths TreeDepths stored in memory, intStateTreeDepth, messageTreeSubDepth, messageTreeDepth, and voteOptionTreeDepth as uint8 values
* @param _batchSizes BatchSizes stored in memory, messageBatchSize and tallyBatchSize as uint8 values
* @param _coordinatorPubKey PubKey stored in memory, MACI pubkey of the coordinator of the GrantRound
* @param _maci VkRegistry
* @param _grantRoundOwner address stored in memory
*/
/*
* Deploy a new Poll contract and AccQueue contract for messages.
*/
function deployGrantRound(
uint256 _voiceCreditFactor,
address _coordinator,
ERC20 _nativeToken,
uint256 _duration,
MaxValues memory _maxValues,
TreeDepths memory _treeDepths,
BatchSizes memory _batchSizes,
PubKey memory _coordinatorPubKey,
VkRegistry _vkRegistry,
IMACI _maci,
address _grantRoundOwner
) public onlyOwner returns (GrantRound) {
uint8 treeArity = 5;
// Validate _maxValues
// NOTE: these checks may not be necessary. Removing them will save
// 0.28 Kb of bytecode.
// maxVoteOptions must be less than 2 ** 50 due to circuit limitations;
// it will be packed as a 50-bit value along with other values as one
// of the inputs (aka packedVal)
require(
_maxValues.maxMessages <= treeArity**_treeDepths.messageTreeDepth &&
_maxValues.maxMessages >= _batchSizes.messageBatchSize &&
_maxValues.maxMessages % _batchSizes.messageBatchSize == 0 &&
_maxValues.maxVoteOptions <=
treeArity**_treeDepths.voteOptionTreeDepth &&
_maxValues.maxVoteOptions < (2**50),
"PollFactory: invalid _maxValues"
);
AccQueue messageAq = messageAqFactory.deploy(
_treeDepths.messageTreeSubDepth
);
ExtContracts memory extContracts;
// TODO: remove _vkRegistry; only PollProcessorAndTallyer needs it
extContracts.vkRegistry = _vkRegistry;
extContracts.maci = _maci;
extContracts.messageAq = messageAq;
GrantRound grantRound = new GrantRound(
_voiceCreditFactor,
_coordinator,
_nativeToken,
_duration,
_maxValues,
_treeDepths,
_batchSizes,
_coordinatorPubKey,
extContracts,
recipientRegistry
);
// Make the Poll contract own the messageAq contract, so only it can
// run enqueue/merge
messageAq.transferOwnership(address(grantRound));
// TODO: should this be _maci.owner() instead?
grantRound.transferOwnership(_grantRoundOwner);
return grantRound;
}
}

View File

@@ -0,0 +1,340 @@
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.2;
import {MACI} from "maci-contracts/contracts/MACI.sol";
import {Params} from "maci-contracts/contracts/Params.sol";
import {PollFactory, Poll, PollProcessorAndTallyer, MessageAqFactory} from "maci-contracts/contracts/Poll.sol";
import {VkRegistry} from "maci-contracts/contracts/VkRegistry.sol";
import {InitialVoiceCreditProxy} from "maci-contracts/contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol";
import {SignUpGatekeeper} from "maci-contracts/contracts/gatekeepers/SignUpGatekeeper.sol";
import {ConstantInitialVoiceCreditProxy} from "maci-contracts/contracts/initialVoiceCreditProxy/ConstantInitialVoiceCreditProxy.sol";
import {FreeForAllGatekeeper} from "maci-contracts/contracts/gatekeepers/FreeForAllSignUpGatekeeper.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import {GrantRound} from "./GrantRound.sol";
import {FundsManager} from "./FundsManager.sol";
import {GrantRoundFactory} from "./GrantRoundFactory.sol";
/**
* @title Quadratic Funding Infrastructure
* @author Q
* @notice Top level contract for the Quadratic Funding Infrastructure
* @dev Special type of MACI that allows for a quadratic funding scheme.
*/
contract QFI is MACI, FundsManager {
using SafeERC20 for ERC20;
enum Stage {
// The contract is not yet initialized
NOT_INITIALIZED,
// The current poll/grant round is NOT started and MACI is waiting for SignUp/Topups from contributions
WAITING_FOR_SIGNUPS_AND_TOPUPS,
// The current poll/grant round is started, voice credit balances are locked, can submit messages to current poll
VOTING_PERIOD_OPEN,
// The current poll/grant round is over, cannot submit messages to current poll, but votes have not been tallied yet
WAITING_FOR_FINALIZATION,
// The current poll/grant round is over, and votes have been tallied, and matching funds have been distributed to grant contract
FINALIZED,
// The current poll/grant round is cancelled, and contributions can be withdrawn
CANCELLED
}
Stage public currentStage;
// Constants
uint256 private constant MAX_VOICE_CREDITS = 10**9; // MACI allows 2 ** 32 voice credits max
uint256 private constant MAX_CONTRIBUTION_AMOUNT = 10**4; // In tokens
// State
uint256 public voiceCreditFactor;
struct ContributorStatus {
uint256 voiceCredits;
bool isRegistered;
}
MessageAqFactory public messageAqFactoryGrants;
GrantRoundFactory public grantRoundFactory;
GrantRound public currentGrantRound;
PollProcessorAndTallyer public pollProcessorAndTallyer;
uint256 public nextGrantRoundId;
uint256 public contributorCount;
ERC20 public nativeToken;
// A mapping of grantRound IDs to GrantRound contracts.
mapping(uint256 => GrantRound) public grantRounds;
mapping(address => ContributorStatus) private contributors;
/**
* @notice Constructor for the Quadratic Funding Infrastructure
* @dev Binds the contracts that are needed for the Quadratic Funding Infrastructure
* @param _grantRoundFactory GrantRoundFactory, the contract that will be used to create GrantRounds which are a special type of Poll
* @param _pollFactory PollFactory, the contract that will be used to create Polls
* @param _signUpGatekeeper SignUpGatekeeper, the contract that will be used to limit who can sign up to MACI
* @param _initialVoiceCreditProxy InitialVoiceCreditProxy, the contract that will be used to set the initial voice credit balance for a user
*/
constructor(
ERC20 _nativeToken,
GrantRoundFactory _grantRoundFactory,
PollFactory _pollFactory,
SignUpGatekeeper _signUpGatekeeper,
InitialVoiceCreditProxy _initialVoiceCreditProxy
) MACI(_pollFactory, _signUpGatekeeper, _initialVoiceCreditProxy) {
grantRoundFactory = _grantRoundFactory;
currentStage = Stage.NOT_INITIALIZED;
nativeToken = _nativeToken;
voiceCreditFactor =
(MAX_CONTRIBUTION_AMOUNT * uint256(10)**nativeToken.decimals()) /
MAX_VOICE_CREDITS;
voiceCreditFactor = voiceCreditFactor > 0 ? voiceCreditFactor : 1;
}
/*
* Initialise the various factory/helper contracts. This should only be run
* once and it must be run before deploying the first Poll.
*/
function initialize(
VkRegistry _vkRegistry,
MessageAqFactory _messageAqFactoryPolls,
MessageAqFactory _messageAqFactoryGrantRounds
) public onlyOwner {
// The VkRegistry owner must be the owner of this contract, this is checked in the init function
init(_vkRegistry, _messageAqFactoryPolls);
messageAqFactoryGrants = _messageAqFactoryGrantRounds;
require(
grantRoundFactory.owner() == address(this),
"MACI: GrantFactory owner incorrectly set"
);
// The PollFactory needs to store the MessageAqFactory address
grantRoundFactory.setMessageAqFactory(_messageAqFactoryGrantRounds);
// The MessageAQFactory owner must be the PollFactory contract
require(
messageAqFactoryGrants.owner() == address(grantRoundFactory),
"MACI: MessageAqFactory owner incorrectly set"
);
currentStage = Stage.WAITING_FOR_SIGNUPS_AND_TOPUPS;
// emit InitGrantsMessageAQ(_vkRegistry, _messageAqFactory);
}
/**
* @notice Sets the PollProcessorAndTallyer to use for the grant round
* @dev public function,
* @param _pollProcessorAndTallyer PollProcessorAndTallyer stored in memory
*/
function setPollProcessorAndTallyer(
PollProcessorAndTallyer _pollProcessorAndTallyer
) public onlyOwner {
pollProcessorAndTallyer = _pollProcessorAndTallyer;
}
/**
* @notice Contribute tokens to this funding round.
* @dev public function, allows a user to contribute to this funding round by sending tokens in exchange for voice credits.
* @param pubKey Contributor's public key.
* @param amount Contribution amount.
*/
function contribute(PubKey calldata pubKey, uint256 amount) external {
require(
numSignUps < STATE_TREE_ARITY**stateTreeDepth,
"MACI: maximum number of signups reached"
);
require(
currentStage == Stage.WAITING_FOR_SIGNUPS_AND_TOPUPS,
"QFI: Not accepting signups or top ups"
);
require(
amount > 0,
"QFI: Contribution amount must be greater than zero"
);
require(
amount <= MAX_VOICE_CREDITS * voiceCreditFactor,
"QFI: Contribution amount is too large"
);
// TODO: TOP UP CHECK
require(
contributors[msg.sender].voiceCredits == 0,
"QFI: top ups not supported, donate to matching pool instead"
);
uint256 voiceCredits = amount / voiceCreditFactor;
contributors[msg.sender] = ContributorStatus(voiceCredits, false);
contributorCount += 1;
bytes memory signUpGatekeeperData = abi.encode(
msg.sender,
voiceCredits
);
bytes memory initialVoiceCreditProxyData = abi.encode(
msg.sender,
voiceCredits
);
nativeToken.safeTransferFrom(msg.sender, address(this), amount);
signUp(pubKey, signUpGatekeeperData, initialVoiceCreditProxyData);
// emit Contribution(msg.sender, amount);
}
/**
* @dev Get the amount of voice credits for a given address.
* This function is a part of the InitialVoiceCreditProxy interface.
* @param _data Encoded address of a user.
*/
function getVoiceCredits(
address, /* _caller */
bytes memory _data
) public view returns (uint256) {
address user = abi.decode(_data, (address));
uint256 initialVoiceCredits = contributors[user].voiceCredits;
require(
initialVoiceCredits > 0,
"FundingRound: User does not have any voice credits"
);
return initialVoiceCredits;
}
/**
* @dev Withdraw contributed funds from the pool if the round has been cancelled.
*/
function withdrawContribution() external {
// require(isCancelled, 'FundingRound: Round not cancelled');
// Reconstruction of exact contribution amount from VCs may not be possible due to a loss of precision
uint256 amount = contributors[msg.sender].voiceCredits *
voiceCreditFactor;
require(amount > 0, "FundingRound: Nothing to withdraw");
contributors[msg.sender].voiceCredits = 0;
nativeToken.safeTransfer(msg.sender, amount);
// emit ContributionWithdrawn(msg.sender);
}
/**
* @notice Deploys a new grant round.
* @dev Deploys a special kind of Poll called a GrantRound.
* @param _duration uint256 stored in memory, the duration of the GrantRound
* @param _maxValues MaxValues stored in memory, the maxMessages and maxVoteOptions of the GrantRound as uint256 values
* @param _treeDepths TreeDepths stored in memory, intStateTreeDepth, messageTreeSubDepth, messageTreeDepth, and voteOptionTreeDepth as uint8 values
* @param _coordinatorPubKey PubKey stored in memory, MACI pubkey of the coordinator of the GrantRounds
*/
function deployGrantRound(
uint256 _duration,
MaxValues memory _maxValues,
TreeDepths memory _treeDepths,
PubKey memory _coordinatorPubKey,
address coordinator
) public afterInit onlyOwner {
require(
currentStage == Stage.WAITING_FOR_SIGNUPS_AND_TOPUPS,
"MACI: Cannot deploy a new grant round while not in the WAITING_FOR_SIGNUPS_AND_TOPUPS stage"
);
uint256 pollId = nextPollId;
uint256 grantRoundId = nextGrantRoundId;
// The message batch size and the tally batch size
BatchSizes memory batchSizes = BatchSizes(
MESSAGE_TREE_ARITY**uint8(_treeDepths.messageTreeSubDepth),
STATE_TREE_ARITY**uint8(_treeDepths.intStateTreeDepth)
);
GrantRound g = grantRoundFactory.deployGrantRound(
voiceCreditFactor,
coordinator,
nativeToken,
_duration,
_maxValues,
_treeDepths,
batchSizes,
_coordinatorPubKey,
vkRegistry,
this,
owner()
);
currentGrantRound = g;
polls[pollId] = g;
grantRounds[grantRoundId] = g;
// Increment the grantRound ID for the next poll
nextGrantRoundId++;
// Increment the poll ID for the next poll
nextPollId++;
currentStage = Stage.VOTING_PERIOD_OPEN;
emit DeployPoll(pollId, address(g), _coordinatorPubKey);
// emit deployGrantRound( grantRoundId, _duration, _maxValues, _treeDepths, _coordinatorPubKey);
}
/**
* @notice Retrieves the grant round contract given its ID.
* @dev public view function, returns the Poll address given its grantRoundId.
* @param _grantRoundId uint256 grantRoundId of the GrantRound to retrieve
* @return returns the GrantRound contract
*/
function getGrantRound(uint256 _grantRoundId)
public
view
returns (GrantRound)
{
require(
_grantRoundId < nextGrantRoundId,
"MACI: grantRound with _grantRoundId does not exist"
);
return grantRounds[_grantRoundId];
}
/**
* @notice Calculate the voting deadline for a grant round.
* @dev public view function
* @param grantRound uint256 grantRoundId of the GrantRound to use
* @return uint256 the voting deadline for the current grant round
*/
function getVotingDeadline(uint256 grantRound)
public
view
returns (uint256)
{
GrantRound g = getGrantRound(grantRound);
(uint256 deployTime, uint256 duration) = g.getDeployTimeAndDuration();
// Require that the voting period is over
uint256 deadline = duration + deployTime;
return deadline;
}
function closeVotingAndWaitForDeadline() public onlyOwner {
require(
currentStage == Stage.VOTING_PERIOD_OPEN,
"MACI: Cannot deploy a new grant round while not in the WAITING_FOR_SIGNUPS_AND_TOPUPS stage"
);
//TODO: ACTUALLY CLOSE THE VOTING PERIOD on the grant round contract
currentStage = Stage.WAITING_FOR_FINALIZATION;
}
function finalizeCurrentRound(uint256 _totalSpent, uint256 _totalSpentSalt)
external
onlyOwner
{
require(
currentStage == Stage.WAITING_FOR_FINALIZATION,
"QFI: Cannot finalize a grant round while not in the WAITING_FOR_FINALIZATION stage"
);
bool proccesingComplete = pollProcessorAndTallyer.processingComplete();
require(proccesingComplete, "QFI: messages have not been proccessed");
GrantRound g = currentGrantRound;
//NOTE: tansfer the funds to the grant round contract first before finalizing, so that the matching pool is calculated correctly
//NOTE: matching pool will be balance of the grant contract less the totalSpent * voiceCreditFactor
transferMatchingFunds(g);
g.finalize(_totalSpent, _totalSpentSalt);
currentStage = Stage.FINALIZED;
}
function acceptContributionsAndTopUpsBeforeNewRound() public onlyOwner {
require(
currentStage == Stage.FINALIZED,
"QFI: Cannot deploy a new grant round while not in the WAITING_FOR_SIGNUPS_AND_TOPUPS stage"
);
currentStage = Stage.WAITING_FOR_SIGNUPS_AND_TOPUPS;
}
}

View File

@@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.2;
import './IRecipientRegistry.sol';
/**
* @dev Abstract contract containing common methods for recipient registries.
*/
abstract contract BaseRecipientRegistry is IRecipientRegistry {
// Structs
struct Recipient {
address addr;
uint256 index;
uint256 addedAt;
uint256 removedAt;
}
// State
address public controller;
uint256 public maxRecipients;
mapping(bytes32 => Recipient) internal recipients;
bytes32[] private removed;
// Slot 0 corresponds to index 1
// Each slot contains a history of recipients who occupied it
bytes32[][] private slots;
/**
* @dev Set maximum number of recipients.
* @param _maxRecipients Maximum number of recipients.
* @return True if operation is successful.
*/
function setMaxRecipients(uint256 _maxRecipients)
override
external
returns (bool)
{
require(
_maxRecipients >= maxRecipients,
'RecipientRegistry: Max number of recipients can not be decreased'
);
if (controller != msg.sender) {
// This allows other clrfund instances to use the registry
// but only controller can actually increase the limit.
return false;
}
maxRecipients = _maxRecipients;
return true;
}
/**
* @dev Register recipient as eligible for funding allocation.
* @param _recipientId The ID of recipient.
* @param _recipient The address that receives funds.
* @return Recipient index.
*/
function _addRecipient(bytes32 _recipientId, address _recipient)
internal
returns (uint256)
{
require(maxRecipients > 0, 'RecipientRegistry: Recipient limit is not set');
require(recipients[_recipientId].index == 0, 'RecipientRegistry: Recipient already registered');
uint256 recipientIndex = 0;
uint256 nextRecipientIndex = slots.length + 1;
if (nextRecipientIndex <= maxRecipients) {
// Assign next index in sequence
recipientIndex = nextRecipientIndex;
bytes32[] memory history = new bytes32[](1);
history[0] = _recipientId;
slots.push(history);
} else {
// Assign one of the vacant recipient indexes
require(removed.length > 0, 'RecipientRegistry: Recipient limit reached');
bytes32 removedRecipient = removed[removed.length - 1];
removed.pop();
recipientIndex = recipients[removedRecipient].index;
slots[recipientIndex - 1].push(_recipientId);
}
recipients[_recipientId] = Recipient(_recipient, recipientIndex, block.timestamp, 0);
return recipientIndex;
}
/**
* @dev Remove recipient from the registry.
* @param _recipientId The ID of recipient.
*/
function _removeRecipient(bytes32 _recipientId)
internal
{
require(recipients[_recipientId].index != 0, 'RecipientRegistry: Recipient is not in the registry');
require(recipients[_recipientId].removedAt == 0, 'RecipientRegistry: Recipient already removed');
recipients[_recipientId].removedAt = block.timestamp;
removed.push(_recipientId);
}
/**
* @dev Get recipient address by index.
* @param _index Recipient index.
* @param _startTime The start time of the funding round.
* @param _endTime The end time of the funding round.
* @return Recipient address.
*/
function getRecipientAddress(
uint256 _index,
uint256 _startTime,
uint256 _endTime
)
override
external
view
returns (address)
{
if (_index == 0 || _index > slots.length) {
return address(0);
}
bytes32[] memory history = slots[_index - 1];
if (history.length == 0) {
// Slot is not occupied
return address(0);
}
address prevRecipientAddress = address(0);
for (uint256 idx = history.length; idx > 0; idx--) {
bytes32 recipientId = history[idx - 1];
Recipient memory recipient = recipients[recipientId];
if (recipient.addedAt > _endTime) {
// Recipient added after the end of the funding round, skip
continue;
}
else if (recipient.removedAt != 0 && recipient.removedAt <= _startTime) {
// Recipient had been already removed when the round started
// Stop search because subsequent items were removed even earlier
return prevRecipientAddress;
}
// This recipient is valid, but the recipient who occupied
// this slot before also needs to be checked.
prevRecipientAddress = recipient.addr;
}
return prevRecipientAddress;
}
/**
* @dev Get recipient count.
* @return count of active recipients in the registry.
*/
function getRecipientCount() public view returns(uint256) {
return slots.length - removed.length;
}
}

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.2;
/**
* @dev Interface of the recipient registry.
*
* This contract must do the following:
*
* - Add recipients to the registry.
* - Allow only legitimate recipients into the registry.
* - Assign an unique index to each recipient.
* - Limit the maximum number of entries according to a parameter set by the funding round factory.
* - Remove invalid entries.
* - Prevent indices from changing during the funding round.
* - Find address of a recipient by their unique index.
*/
interface IRecipientRegistry {
function setMaxRecipients(uint256 _maxRecipients) external returns (bool);
function getRecipientAddress(uint256 _index, uint256 _startBlock, uint256 _endBlock) external view returns (address);
}

View File

@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.2;
import '@openzeppelin/contracts/access/Ownable.sol';
import './BaseRecipientRegistry.sol';
/**
* @dev Recipient registry with optimistic execution of registrations and removals.
*/
contract OptimisticRecipientRegistry is Ownable, BaseRecipientRegistry {
// Enums
enum RequestType {
Registration,
Removal
}
// Structs
struct Request {
RequestType requestType;
address payable requester;
uint256 submissionTime;
uint256 deposit;
address recipientAddress; // Only for registration requests
string recipientMetadata; // Only for registration requests
}
// State
uint256 public baseDeposit;
uint256 public challengePeriodDuration;
mapping(bytes32 => Request) private requests;
// Events
event RequestSubmitted(
bytes32 indexed _recipientId,
RequestType indexed _type,
address _recipient,
string _metadata,
uint256 _timestamp
);
event RequestResolved(
bytes32 indexed _recipientId,
RequestType indexed _type,
bool indexed _rejected,
uint256 _recipientIndex, // Only for accepted registration requests
uint256 _timestamp
);
/**
* @dev Deploy the registry.
* @param _baseDeposit Deposit required to add or remove item.
* @param _challengePeriodDuration The time owner has to challenge a request (seconds).
* @param _controller Controller address. Normally it's a funding round factory contract.
*/
constructor(
uint256 _baseDeposit,
uint256 _challengePeriodDuration,
address _controller
)
public
{
baseDeposit = _baseDeposit;
challengePeriodDuration = _challengePeriodDuration;
controller = _controller;
}
/**
* @dev Change base deposit.
*/
function setBaseDeposit(uint256 _baseDeposit)
external
onlyOwner
{
baseDeposit = _baseDeposit;
}
/**
* @dev Change challenge period duration.
*/
function setChallengePeriodDuration(uint256 _challengePeriodDuration)
external
onlyOwner
{
challengePeriodDuration = _challengePeriodDuration;
}
/**
* @dev Submit recipient registration request.
* @param _recipient The address that receives funds.
* @param _metadata The metadata info of the recipient.
*/
function addRecipient(address _recipient, string calldata _metadata)
external
payable
{
require(_recipient != address(0), 'RecipientRegistry: Recipient address is zero');
require(bytes(_metadata).length != 0, 'RecipientRegistry: Metadata info is empty string');
bytes32 recipientId = keccak256(abi.encodePacked(_recipient, _metadata));
require(recipients[recipientId].index == 0, 'RecipientRegistry: Recipient already registered');
require(requests[recipientId].submissionTime == 0, 'RecipientRegistry: Request already submitted');
require(msg.value == baseDeposit, 'RecipientRegistry: Incorrect deposit amount');
requests[recipientId] = Request(
RequestType.Registration,
msg.sender,
block.timestamp,
msg.value,
_recipient,
_metadata
);
emit RequestSubmitted(
recipientId,
RequestType.Registration,
_recipient,
_metadata,
block.timestamp
);
}
/**
* @dev Submit recipient removal request.
* @param _recipientId The ID of recipient.
*/
function removeRecipient(bytes32 _recipientId)
external
payable
{
require(recipients[_recipientId].index != 0, 'RecipientRegistry: Recipient is not in the registry');
require(recipients[_recipientId].removedAt == 0, 'RecipientRegistry: Recipient already removed');
require(requests[_recipientId].submissionTime == 0, 'RecipientRegistry: Request already submitted');
require(msg.value == baseDeposit, 'RecipientRegistry: Incorrect deposit amount');
requests[_recipientId] = Request(
RequestType.Removal,
msg.sender,
block.timestamp,
msg.value,
address(0),
''
);
emit RequestSubmitted(
_recipientId,
RequestType.Removal,
address(0),
'',
block.timestamp
);
}
/**
* @dev Reject request.
* @param _recipientId The ID of recipient.
* @param _beneficiary Address to which the deposit should be transferred.
*/
function challengeRequest(bytes32 _recipientId, address payable _beneficiary)
external
onlyOwner
returns (bool)
{
Request memory request = requests[_recipientId];
require(request.submissionTime != 0, 'RecipientRegistry: Request does not exist');
delete requests[_recipientId];
bool isSent = _beneficiary.send(request.deposit);
emit RequestResolved(
_recipientId,
request.requestType,
true,
0,
block.timestamp
);
return isSent;
}
/**
* @dev Execute request.
* @param _recipientId The ID of recipient.
*/
function executeRequest(bytes32 _recipientId)
external
returns (bool)
{
Request memory request = requests[_recipientId];
require(request.submissionTime != 0, 'RecipientRegistry: Request does not exist');
if (msg.sender != owner()) {
require(
block.timestamp - request.submissionTime >= challengePeriodDuration,
'RecipientRegistry: Challenge period is not over'
);
}
uint256 recipientIndex = 0;
if (request.requestType == RequestType.Removal) {
_removeRecipient(_recipientId);
} else {
// WARNING: Could revert if no slots are available or if recipient limit is not set
recipientIndex = _addRecipient(_recipientId, request.recipientAddress);
}
delete requests[_recipientId];
bool isSent = request.requester.send(request.deposit);
emit RequestResolved(
_recipientId,
request.requestType,
false,
recipientIndex,
block.timestamp
);
return isSent;
}
}

View File

@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.2;
import '@openzeppelin/contracts/access/Ownable.sol';
import './BaseRecipientRegistry.sol';
/**
* @dev A simple recipient registry managed by a trusted entity.
*/
contract SimpleRecipientRegistry is Ownable, BaseRecipientRegistry {
// Events
event RecipientAdded(
bytes32 indexed _recipientId,
address _recipient,
string _metadata,
uint256 _index,
uint256 _timestamp
);
event RecipientRemoved(
bytes32 indexed _recipientId,
uint256 _timestamp
);
/**
* @dev Deploy the registry.
* @param _controller Controller address. Normally it's a funding round factory contract.
*/
constructor(
address _controller
)
public
{
controller = _controller;
}
/**
* @dev Register recipient as eligible for funding allocation.
* @param _recipient The address that receives funds.
* @param _metadata The metadata info of the recipient.
*/
function addRecipient(address _recipient, string calldata _metadata)
external
onlyOwner
{
require(_recipient != address(0), 'RecipientRegistry: Recipient address is zero');
require(bytes(_metadata).length != 0, 'RecipientRegistry: Metadata info is empty string');
bytes32 recipientId = keccak256(abi.encodePacked(_recipient, _metadata));
uint256 recipientIndex = _addRecipient(recipientId, _recipient);
emit RecipientAdded(recipientId, _recipient, _metadata, recipientIndex, block.timestamp);
}
/**
* @dev Remove recipient from the registry.
* @param _recipientId The ID of recipient.
*/
function removeRecipient(bytes32 _recipientId)
external
onlyOwner
{
_removeRecipient(_recipientId);
emit RecipientRemoved(_recipientId, block.timestamp);
}
}

View File

@@ -0,0 +1,29 @@
# Grant Round Contract
This contract manages contributions, withdrawals, Voting and the distribution of funds for a particular grant round.
## Functions
### constructor
```solidity
function constructor(
uint256 _duration,
struct Params.MaxValues _maxValues,
struct Params.TreeDepths _treeDepths,
struct Params.BatchSizes _batchSizes,
struct IPubKey.PubKey _coordinatorPubKey,
struct PollDeploymentParams.ExtContracts _extContracts
) public
```
Constructor for the GrantRound, special type of poll that implements Quadratic Funding.
Binds the contracts that will be used to tally the Poll and sets duration ans coordinator.
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_duration` | uint256 | uint256, the duration of the GrantRound
|`_maxValues` | struct Params.MaxValues | MaxValues stored in memory, the maxMessages and maxVoteOptions of the GrantRound as uint256 values
|`_treeDepths` | struct Params.TreeDepths | TreeDepths stored in memory, intStateTreeDepth, messageTreeSubDepth, messageTreeDepth, and voteOptionTreeDepth as uint8 values
|`_batchSizes` | struct Params.BatchSizes | BatchSizes stored in memory, this inlcudes the message batch size and the tally batch size
|`_coordinatorPubKey` | struct IPubKey.PubKey | PubKey stored in memory, MACI pubkey of the coordinator of the GrantRounds
|`_extContracts` | struct PollDeploymentParams.ExtContracts | ExtContracts stored in memory, this includes the maci, and messageAq contracts that will be used to tally the Poll.

View File

@@ -0,0 +1,46 @@
# Grant Round Factory Contract
Deploys new Quadratic Funding Round Contracts
## Functions
### setMessageAqFactory
```solidity
function setMessageAqFactory(
contract MessageAqFactory _messageAqFactory
) public
```
Sets the MessageAqFactory to use for the grant round
public function, _messageAqFactory should deploy new AccQueueQuinaryMaci AccQueue(s), need to set before calling deployGrantRound
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_messageAqFactory` | contract MessageAqFactory | MessageAqFactory stored in memory
### deployGrantRound
```solidity
function deployGrantRound(
uint256 _duration,
struct Params.MaxValues _maxValues,
struct Params.TreeDepths _treeDepths,
struct Params.BatchSizes _batchSizes,
struct IPubKey.PubKey _coordinatorPubKey,
contract VkRegistry _maci,
contract IMACI _grantRoundOwner
) public returns (contract GrantRound)
```
Deploys a new GrantRound Contract
public function
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_duration` | uint256 | uint256 stored in memory, the duration of the GrantRound
|`_maxValues` | struct Params.MaxValues | MaxValues stored in memory, the maxMessages and maxVoteOptions of the GrantRound as uint256 values
|`_treeDepths` | struct Params.TreeDepths | TreeDepths stored in memory, intStateTreeDepth, messageTreeSubDepth, messageTreeDepth, and voteOptionTreeDepth as uint8 values
|`_batchSizes` | struct Params.BatchSizes | BatchSizes stored in memory, messageBatchSize and tallyBatchSize as uint8 values
|`_coordinatorPubKey` | struct IPubKey.PubKey | PubKey stored in memory, MACI pubkey of the coordinator of the GrantRound
|`_maci` | contract VkRegistry | VkRegistry
|`_grantRoundOwner` | contract IMACI | address stored in memory

View File

@@ -0,0 +1,65 @@
# Quadratic Funding Infrastructure
Top level contract for the Quadratic Funding Infrastructure
## Functions
### constructor
```solidity
function constructor(
contract GrantRoundFactory _grantRoundFactory,
contract PollFactory _pollFactory,
contract SignUpGatekeeper _signUpGatekeeper,
contract InitialVoiceCreditProxy _initialVoiceCreditProxy
) public
```
Constructor for the Quadratic Funding Infrastructure
Binds the contracts that are needed for the Quadratic Funding Infrastructure
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_grantRoundFactory` | contract GrantRoundFactory | GrantRoundFactory, the contract that will be used to create GrantRounds which are a special type of Poll
|`_pollFactory` | contract PollFactory | PollFactory, the contract that will be used to create Polls
|`_signUpGatekeeper` | contract SignUpGatekeeper | SignUpGatekeeper, the contract that will be used to limit who can sign up to MACI
|`_initialVoiceCreditProxy` | contract InitialVoiceCreditProxy | InitialVoiceCreditProxy, the contract that will be used to set the initial voice credit balance for a user
### deployGrantRound
```solidity
function deployGrantRound(
uint256 _duration,
struct Params.MaxValues _maxValues,
struct Params.TreeDepths _treeDepths,
struct IPubKey.PubKey _coordinatorPubKey
) public
```
Deploys a new grant round.
Deploys a special kind of Poll called a GrantRound.
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_duration` | uint256 | uint256 stored in memory, the duration of the GrantRound
|`_maxValues` | struct Params.MaxValues | MaxValues stored in memory, the maxMessages and maxVoteOptions of the GrantRound as uint256 values
|`_treeDepths` | struct Params.TreeDepths | TreeDepths stored in memory, intStateTreeDepth, messageTreeSubDepth, messageTreeDepth, and voteOptionTreeDepth as uint8 values
|`_coordinatorPubKey` | struct IPubKey.PubKey | PubKey stored in memory, MACI pubkey of the coordinator of the GrantRounds
### getGrantRound
```solidity
function getGrantRound(
uint256 _grantRoundId
) public returns (contract GrantRound)
```
Retrieves the grant round contract given its ID.
public view function, returns the Poll address given its grantRoundId.
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_grantRoundId` | uint256 | uint256 grantRoundId of the GrantRound to retrieve
#### Return Values:
| Name | Type | Description |
| :----------------------------- | :------------ | :--------------------------------------------------------------------------- |
|`returns`| uint256 | the GrantRound contract

View File

@@ -0,0 +1,5 @@
# Introduction
* [Quadratic Funding Infrastructure Docs](QFI.md)
* [Grant Round Factory Docs](GrantRoundFactory.md)
* [Grant Round Docs](GrantRound.md)

View File

@@ -0,0 +1,5 @@
# Summary
* [Quadratic Funding Infrastructure Docs](QFI.md)
* [Grant Round Factory Docs](GrantRoundFactory.md)
* [Grant Round Docs](GrantRound.md)

View File

@@ -0,0 +1,18 @@
{
"plugins": [
"github",
"insert-logo",
"back-to-top-button"
],
"pluginsConfig": {
"github": {
"url": "https://github.com/quadratic-funding/QFI.git"
},
"insert-logo": {
"url": "https://is4-ssl.mzstatic.com/image/thumb/Podcasts125/v4/37/95/c9/3795c92b-4c3f-81d5-3773-7954820be833/mza_1482433603479614239.jpg/100x100bb.jpg",
"style": "background: none;"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"gitbook-plugin-back-to-top-button": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/gitbook-plugin-back-to-top-button/-/gitbook-plugin-back-to-top-button-0.1.4.tgz",
"integrity": "sha1-5iGDOLDvGdWOb2YAmUNQt26ANd8="
},
"gitbook-plugin-github": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/gitbook-plugin-github/-/gitbook-plugin-github-2.0.0.tgz",
"integrity": "sha1-UWbnY8/MQC1DKIC3pshcHFS1ao0="
},
"gitbook-plugin-insert-logo": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/gitbook-plugin-insert-logo/-/gitbook-plugin-insert-logo-0.1.5.tgz",
"integrity": "sha1-2q6N2kGiNtVPE5MeVwsmcpVXiFo="
}
}
}

View File

@@ -0,0 +1,7 @@
npx solidity-docgen --solc-module solc-0.8 -t ./docs/templates/
cd docs
nvm use v12.18.1
gitbook install
gitbook build
gitbook serve

View File

@@ -0,0 +1,51 @@
{{{natspec.userdoc}}}
{{{natspec.devdoc}}}
{{#if ownFunctions}}
## Functions
{{/if}}
{{#ownFunctions}}
### {{name}}
```solidity
function {{name}}(
{{#natspec.params}}
{{#lookup ../args.types @index}}{{/lookup}} {{param}}{{#if @last}}{{else}},{{/if}}
{{/natspec.params}}
) {{visibility}}{{#if outputs}} returns ({{outputs}}){{/if}}
```
{{#if natspec.userdoc}}{{natspec.userdoc}}{{/if}}
{{#if natspec.devdoc}}{{natspec.devdoc}}{{/if}}
{{#if natspec.params}}
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
{{#natspec.params}}
|`{{param}}` | {{#lookup ../args.types @index}}{{/lookup}} | {{ description }}{{/natspec.params}}{{/if}}
{{#if natspec.returns}}
#### Return Values:
| Name | Type | Description |
| :----------------------------- | :------------ | :--------------------------------------------------------------------------- |
{{#natspec.returns}}
|`{{param}}`| {{#lookup ../args.types @index}}{{/lookup}} | {{{description}}}{{/natspec.returns}}{{/if}}
{{/ownFunctions}}
{{#if ownEvents}}
## Events
{{/if}}
{{#ownEvents}}
### {{name}}
```solidity
event {{name}}(
{{#natspec.params}}
{{#lookup ../args.types @index}}{{/lookup}} {{param}}{{#if @last}}{{else}},{{/if}}
{{/natspec.params}}
)
```
{{#if natspec.userdoc}}{{natspec.userdoc}}{{/if}}
{{#if natspec.devdoc}}{{natspec.devdoc}}{{/if}}
{{#if natspec.params}}
#### Parameters:
| Name | Type | Description |
| :----------------------------- | :------------ | :--------------------------------------------- |
{{#natspec.params}}
|`{{param}}`| {{#lookup ../args.types @index}}{{/lookup}} | {{{description}}}{{/natspec.params}}{{/if}}
{{/ownEvents}}

View File

@@ -0,0 +1,163 @@
import { task } from "hardhat/config";
import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv'
import { HardhatUserConfig } from "hardhat/types";
import { NetworkUserConfig } from "hardhat/types";
import "@typechain/hardhat";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";
import "@nomiclabs/hardhat-ganache";
import "solidity-coverage";
import "hardhat-gas-reporter";
import "hardhat-contract-sizer";
import "hardhat-abi-exporter";
dotenv.config();
const WALLET_MNEMONIC =
process.env.WALLET_MNEMONIC || "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat";
const INFURA_API_KEY = process.env.INFURA_API_KEY || "";
const GAS_LIMIT = 20000000;
const CHAIN_IDS = {
ganache: 1337,
goerli: 5,
hardhat: 31337,
kovan: 42,
mainnet: 1,
rinkeby: 4,
ropsten: 3,
xdai: 100,
};
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (args, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(await account.getAddress());
}
});
function createTestnetConfig(network: keyof typeof CHAIN_IDS): NetworkUserConfig {
const url: string = "https://" + network + ".infura.io/v3/" + INFURA_API_KEY;
return {
accounts: {
count: 10,
initialIndex: 0,
mnemonic: WALLET_MNEMONIC,
// eslint-disable-next-line quotes
path: "m/44'/60'/0'/0",
},
chainId: CHAIN_IDS[network],
url,
};
}
const config: HardhatUserConfig = {
networks: {
hardhat: {
gas: GAS_LIMIT,
blockGasLimit: GAS_LIMIT,
accounts: { mnemonic: WALLET_MNEMONIC },
chainId: CHAIN_IDS.hardhat,
},
localhost: {
url: "http://127.0.0.1:8545",
gas: GAS_LIMIT,
blockGasLimit: GAS_LIMIT,
accounts: { mnemonic: WALLET_MNEMONIC },
},
ganache: {
// Workaround for https://github.com/nomiclabs/hardhat/issues/518
url: "http://127.0.0.1:8555",
gasLimit: GAS_LIMIT,
} as any,
xdai: {
url: process.env.XDAI_JSONRPC_HTTP_URL || "https://rpc.xdaichain.com",
timeout: 60000,
accounts: { mnemonic: WALLET_MNEMONIC },
chainId: CHAIN_IDS.xdai,
},
mainnet: createTestnetConfig("mainnet"),
goerli: createTestnetConfig("goerli"),
kovan: createTestnetConfig("kovan"),
rinkeby: createTestnetConfig("rinkeby"),
ropsten: createTestnetConfig("ropsten"),
},
paths: {
artifacts: "build/contracts",
tests: "tests",
},
contractSizer: {
alphaSort: true,
runOnCompile: true,
disambiguatePaths: false,
},
gasReporter: {
currency: "USD",
gasPrice: 100,
coinmarketcap: "603bd12e-d2f3-4a9f-8c82-d5e346d9d482"
},
typechain: {
outDir: "typechain/",
target: "ethers-v5",
alwaysGenerateOverloads: false,
externalArtifacts: ['precompiled/*.json'],
},
solidity: {
version: "0.7.2",
settings: {
optimizer: {
enabled: true,
runs: 0,
},
},
overrides: {
"contracts/GrantRound.sol": {
version: "0.7.2",
settings: {
optimizer: {
enabled: true,
runs: 0,
},
},
},
},
},
};
// task('compile', 'Compiles the entire project, building all artifacts', async (_, { config }, runSuper) => {
// await runSuper();
// });
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (args, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(await account.getAddress());
}
});
// task('compile', 'Compiles the entire project, building all artifacts', async (_, { config }, runSuper) => {
// await runSuper();
// // Copy Poseidon artifacts
// const poseidons = ['PoseidonT3', 'PoseidonT6']
// for (const contractName of poseidons) {
// const artifact = JSON.parse(fs.readFileSync(`precompiled/${contractName}.json`).toString())
// fs.writeFileSync(
// path.join(config.paths.artifacts, `${contractName}.json`),
// JSON.stringify({ ...artifact, linkReferences: {} }),
// )
// }
// });
export default config;

View File

@@ -0,0 +1,346 @@
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.2;
import {Poll, PollFactory, PollProcessorAndTallyer, MessageAqFactory} from "../Poll.sol";
import "./FundingRound.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
/*
* Minimum Quadratic Funding Infrastructure
* Version 1
*/
contract ClrFund is IQuadraticFunding, ClrHelper, MACI {
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for ERC20;
// Structs
struct ContributorStatus {
uint256 voiceCredits;
bool isRegistered;
FundingRound lastContribution;
}
// State
FundingRound[] private rounds;
mapping(address => ContributorStatus) private contributors;
event SignUp(
uint256 _stateIndex,
PubKey _userPubKey,
uint256 _voiceCreditBalance,
uint256 _timestamp
);
event RoundStarted(address _round);
event RoundFinalized(address _round);
event DeployPoll(uint256 _pollId, address _pollAddr, PubKey _pubKey);
/*
* Allows any eligible user sign up. The sign-up gatekeeper should prevent
* double sign-ups or ineligible users from doing so. This function will
* only succeed if the sign-up deadline has not passed. It also enqueues a
* fresh state leaf into the state AccQueue.
* @param _userPubKey The user's desired public key.
* @param _signUpGatekeeperData Data to pass to the sign-up gatekeeper's
* register() function. For instance, the POAPGatekeeper or
* SignUpTokenGatekeeper requires this value to be the ABI-encoded
* token ID.
* @param _initialVoiceCreditProxyData Data to pass to the
* InitialVoiceCreditProxy, which allows it to determine how many voice
* credits this user should have.
*/
function signUp(PubKey memory _pubKey, uint256 _initialVoiceCreditBalance)
internal
afterInit
{
// The circuits only support up to (5 ** 10 - 1) signups
require(
numSignUps < STATE_TREE_ARITY**stateTreeDepth,
"MACI: maximum number of signups reached"
);
require(
_pubKey.x < SNARK_SCALAR_FIELD && _pubKey.y < SNARK_SCALAR_FIELD,
"MACI: _pubKey values should be less than the snark scalar field"
);
// The limit on voice credits is 2 ^ 32 which is hardcoded into the
// MessageValidator circuit, specifically at check that there are
// sufficient voice credits (using GreaterEqThan(32)).
// TODO: perhaps increase this to 2 ^ 50 = 1125899906842624?
require(
_initialVoiceCreditBalance <= 4294967296,
"MACI: too many voice credits"
);
uint256 timestamp = block.timestamp;
// Create a state leaf and enqueue it.
uint256 stateLeaf = hashStateLeaf(
StateLeaf(_pubKey, voiceCreditBalance, timestamp)
);
uint256 stateIndex = stateAq.enqueue(stateLeaf);
// Increment the number of signups
numSignUps++;
emit SignUp(stateIndex, _pubKey, voiceCreditBalance, timestamp);
}
/**
* @dev Register user for voting.
* This function is part of SignUpGatekeeper interface.
* @param _data Encoded address of a contributor.
*/
function register(PubKey memory _pubKey) public override {
address user = msg.sender;
bool verified = userRegistry.isVerifiedUser(user);
require(verified, "FundingRound: User has not been verified");
require(
!contributors[user].isRegistered,
"FundingRound: User already registered"
);
contributors[user].isRegistered = true;
}
/**
* @dev Contribute tokens to this funding round.
* @param pubKey Contributor's public key.
* @param amount Contribution amount.
*/
function contribute(
PubKey calldata _pubKey,
uint256 amount,
FundingRound _fundingRound
) external {
require(address(maci) != address(0), "FundingRound: MACI not deployed");
require(
contributors[user].isRegistered == true,
"FundingRound: user not registered"
);
// require(!_fundingRound.isAfterVotingDeadline(), 'FundingRound: Voting has not been finished');
// require(!_fundingRound.isFinalized, "FundingRound: Round finalized");
// require( amount > 0, "FundingRound: Contribution amount must be greater than zero");
//NOTE: if they try to contribute not to the current round then throw
if (_fundingRound != getCurrentRound()) {
require(
false,
"FundingRound: can only contribute to current funding round"
);
}
// NOTE: if they have never contributed then just sign them up with MACI and add to stateAQ
else if (contributors[msg.sender].lastContribution == address(0)) {
uint256 voiceCredits = amount / voiceCreditFactor;
contributors[msg.sender] = ContributorStatus(
voiceCredits,
true,
true
);
contributorCount += 1;
nativeToken.safeTransferFrom(msg.sender, address(this), amount);
//NOTE: actually top up the voice credits
signUp(_pubKey, voiceCredits);
emit Contribution(msg.sender, amount);
} else if (_fundingRound == getCurrentRound()) {
//NOTE: if they have contributed before to this round then top up
if (
contributors[msg.sender].lastContribution == getCurrentRound()
) {
require(false, "FundingRound: feature not supported");
//NOTE: implement "topups"
// uint256 voiceCredits = (amount + contributors[msg.sender].voiceCredits) / voiceCreditFactor;
// contributors[msg.sender] = ContributorStatus(voiceCredits, true, true));
// contributorCount += 1;
// nativeToken.safeTransferFrom(msg.sender, address(this), amount);
// topUp(_pubKey, voiceCredits);
}
//NOTE: if they have contributed before but not to this round then clear previous balance and top up'
else {
require(false, "FundingRound: feature not supported");
//NOTE: implement "topups"
// uint256 voiceCredits = (amount / voiceCreditFactor;
// contributors[msg.sender] = ContributorStatus(voiceCredits, true, true));
// contributorCount += 1;
// nativeToken.safeTransferFrom(msg.sender, address(this), amount);
// topUp(_pubKey, voiceCredits);
}
}
}
/**
* @dev Get the amount of voice credits for a given address.
* This function is a part of the InitialVoiceCreditProxy interface.
* @param _data Encoded address of a user.
*/
function getVoiceCredits(
address, /* _caller */
bytes memory _data
) public view override returns (uint256) {
address user = abi.decode(_data, (address));
uint256 initialVoiceCredits = contributors[user].voiceCredits;
require(
initialVoiceCredits > 0,
"FundingRound: User does not have any voice credits"
);
return initialVoiceCredits;
}
/**
* @dev Withdraw contributed funds from the pool if the round has been cancelled.
*/
function withdrawContribution() external {
require(isCancelled, "FundingRound: Round not cancelled");
// Reconstruction of exact contribution amount from VCs may not be possible due to a loss of precision
uint256 amount = contributors[msg.sender].voiceCredits *
voiceCreditFactor;
require(amount > 0, "FundingRound: Nothing to withdraw");
contributors[msg.sender].voiceCredits = 0;
nativeToken.safeTransfer(msg.sender, amount);
emit ContributionWithdrawn(msg.sender);
}
/**
* @dev Transfer funds from matching pool to current funding round and finalize it.
* @param _totalSpent Total amount of spent voice credits.
* @param _totalSpentSalt The salt.
*/
function transferMatchingFunds(uint256 _totalSpent, uint256 _totalSpentSalt)
external
onlyOwner
{
FundingRound currentRound = getCurrentRound();
require(
address(currentRound) != address(0),
"Factory: Funding round has not been deployed"
);
ERC20 roundToken = currentRound.nativeToken();
// Factory contract is the default funding source
uint256 matchingPoolSize = roundToken.balanceOf(address(this));
if (matchingPoolSize > 0) {
roundToken.safeTransfer(address(currentRound), matchingPoolSize);
}
// Pull funds from other funding sources
for (uint256 index = 0; index < fundingSources.length(); index++) {
address fundingSource = fundingSources.at(index);
uint256 allowance = roundToken.allowance(
fundingSource,
address(this)
);
uint256 balance = roundToken.balanceOf(fundingSource);
uint256 contribution = allowance < balance ? allowance : balance;
if (contribution > 0) {
roundToken.safeTransferFrom(
fundingSource,
address(currentRound),
contribution
);
}
}
//NOTE: requires poll.verifySpentVoiceCredits(_totalSpent,_totalSpentSalt);
currentRound.finalize(_totalSpent, _totalSpentSalt);
emit RoundFinalized(address(currentRound));
}
function getCurrentRound()
public
view
returns (FundingRound _currentRound)
{
if (rounds.length == 0) {
return FundingRound(address(0));
}
return rounds[rounds.length - 1];
}
/*
* Deploy new FundingRound and Poll contracts.
*/
function deployNewRound(
uint256 _duration,
MaxValues memory _maxValues,
TreeDepths memory _treeDepths,
PubKey memory _coordinatorPubKey
) external afterInit {
uint256 pollId = nextPollId;
// The message batch size and the tally batch size
BatchSizes memory batchSizes = BatchSizes(
MESSAGE_TREE_ARITY**uint8(_treeDepths.messageTreeSubDepth),
STATE_TREE_ARITY**uint8(_treeDepths.intStateTreeDepth)
);
Poll p = pollFactory.deploy(
_duration,
_maxValues,
_treeDepths,
batchSizes,
_coordinatorPubKey,
vkRegistry,
this,
owner()
);
polls[pollId] = p;
// Increment the poll ID for the next poll
nextPollId++;
emit DeployPoll(pollId, address(p), _coordinatorPubKey);
require(
p.owner() == address(this),
"Factory: MACI factory is not owned by FR factory"
);
require(
address(userRegistry) != address(0),
"Factory: User registry is not set"
);
require(
address(recipientRegistry) != address(0),
"Factory: Recipient registry is not set"
);
require(
address(nativeToken) != address(0),
"Factory: Native token is not set"
);
require(coordinator != address(0), "Factory: No coordinator");
FundingRound currentRound = getCurrentRound();
require(
address(currentRound) == address(0) || currentRound.isFinalized(),
"Factory: Current round is not finalized"
);
//Make sure that the max number of recipients is set correctly
//NOTE: Deploy funding round and link to Poll contracts
FundingRound newRound = new FundingRound(
p,
nativeToken,
userRegistry,
recipientRegistry,
coordinator
);
rounds.push(newRound);
emit DeployFundingRound(address(newRound));
}
/**
* @dev Cancel current round.
*/
function cancelCurrentRound() external onlyOwner {
FundingRound currentRound = getCurrentRound();
require(
address(currentRound) != address(0),
"Factory: Funding round has not been deployed"
);
require(
!currentRound.isFinalized(),
"Factory: Current round is finalized"
);
currentRound.cancel();
emit RoundFinalized(address(currentRound));
}
}

View File

@@ -0,0 +1,114 @@
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
/*
* Minimum Quadratic Funding Infrastructure
* Version 1
*/
contract ClrHelper {
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for ERC20;
// Events
event FundingSourceAdded(address _source);
event FundingSourceRemoved(address _source);
event TokenChanged(address _token);
event CoordinatorChanged(address _coordinator);
// State
address public coordinator;
ERC20 public nativeToken;
IUserRegistry public userRegistry;
IRecipientRegistry public recipientRegistry;
PubKey public coordinatorPubKey;
EnumerableSet.AddressSet private fundingSources;
/**
* @dev Set registry of verified users.
* @param _userRegistry Address of a user registry.
*/
function setUserRegistry(IUserRegistry _userRegistry) external onlyOwner {
userRegistry = _userRegistry;
}
/**
* @dev Set recipient registry.
* @param _recipientRegistry Address of a recipient registry.
*/
function setRecipientRegistry(IRecipientRegistry _recipientRegistry)
external
onlyOwner
{
recipientRegistry = _recipientRegistry;
(, , uint256 maxVoteOptions) = maciFactory.maxValues();
recipientRegistry.setMaxRecipients(maxVoteOptions);
}
/**
* @dev Set token in which contributions are accepted.
* @param _token Address of the token contract.
*/
function setToken(address _token) external onlyOwner {
nativeToken = ERC20(_token);
emit TokenChanged(_token);
}
/**
* @dev Set coordinator's address and public key.
* @param _coordinator Coordinator's address.
* @param _coordinatorPubKey Coordinator's public key.
*/
function setCoordinator(
address _coordinator,
PubKey memory _coordinatorPubKey
) external onlyOwner {
coordinator = _coordinator;
coordinatorPubKey = _coordinatorPubKey;
emit CoordinatorChanged(_coordinator);
}
/**
* @dev Add matching funds source.
* @param _source Address of a funding source.
*/
function addFundingSource(address _source) external onlyOwner {
bool result = fundingSources.add(_source);
require(result, "Factory: Funding source already added");
emit FundingSourceAdded(_source);
}
/**
* @dev Remove matching funds source.
* @param _source Address of the funding source.
*/
function removeFundingSource(address _source) external onlyOwner {
bool result = fundingSources.remove(_source);
require(result, "Factory: Funding source not found");
emit FundingSourceRemoved(_source);
}
/**
* @dev Get total amount of matching funds.
*/
function getMatchingFunds(ERC20 token) external view returns (uint256) {
uint256 matchingPoolSize = token.balanceOf(address(this));
for (uint256 index = 0; index < fundingSources.length(); index++) {
address fundingSource = fundingSources.at(index);
uint256 allowance = token.allowance(fundingSource, address(this));
uint256 balance = token.balanceOf(fundingSource);
uint256 contribution = allowance < balance ? allowance : balance;
matchingPoolSize += contribution;
}
return matchingPoolSize;
}
}

View File

@@ -0,0 +1,269 @@
// SPDX-License-Identifier: GPL-3.0
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.2;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "maci-contracts/sol/MACI.sol";
import "maci-contracts/sol/MACISharedObjs.sol";
import "maci-contracts/sol/gatekeepers/SignUpGatekeeper.sol";
import "maci-contracts/sol/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol";
import "./userRegistry/IUserRegistry.sol";
import "./recipientRegistry/IRecipientRegistry.sol";
contract FundingRound is
Ownable,
MACISharedObjs,
SignUpGatekeeper,
InitialVoiceCreditProxy
{
using SafeERC20 for ERC20;
// Constants
uint256 private constant MAX_VOICE_CREDITS = 10**9; // MACI allows 2 ** 32 voice credits max
uint256 private constant MAX_CONTRIBUTION_AMOUNT = 10**4; // In tokens
// State
uint256 public voiceCreditFactor;
uint256 public contributorCount;
uint256 public matchingPoolSize;
uint256 public totalSpent;
uint256 public totalVotes;
bool public isFinalized = false;
bool public isCancelled = false;
address public coordinator;
MACI public maci;
Poll public poll;
ERC20 public nativeToken;
IUserRegistry public userRegistry;
IRecipientRegistry public recipientRegistry;
string public tallyHash;
mapping(uint256 => bool) private recipients;
// Events
event Contribution(address indexed _sender, uint256 _amount);
event ContributionWithdrawn(address indexed _contributor);
event FundsClaimed(
uint256 indexed _voteOptionIndex,
address indexed _recipient,
uint256 _amount
);
event TallyPublished(string _tallyHash);
event Voted(address indexed _contributor);
/**
* @dev Set round parameters.
* @param _nativeToken Address of a token which will be accepted for contributions.
* @param _userRegistry Address of the registry of verified users.
* @param _recipientRegistry Address of the recipient registry.
* @param _coordinator Address of the coordinator.
*/
constructor(
Poll _poll,
ERC20 _nativeToken,
IUserRegistry _userRegistry,
IRecipientRegistry _recipientRegistry,
address _coordinator
) public {
//NOTE: initiate funding round with poll
poll = _poll;
nativeToken = _nativeToken;
voiceCreditFactor =
(MAX_CONTRIBUTION_AMOUNT * uint256(10)**nativeToken.decimals()) /
MAX_VOICE_CREDITS;
voiceCreditFactor = voiceCreditFactor > 0 ? voiceCreditFactor : 1;
userRegistry = _userRegistry;
recipientRegistry = _recipientRegistry;
coordinator = _coordinator;
}
/**
* @dev Link MACI instance to this funding round.
*/
function setPoll(Poll _poll) external onlyOwner {
require(
address(poll) == address(0),
"FundingRound: Already linked to Poll instance"
);
// require(
// //TODO: feature request
// !_poll.isAfterVotingDeadline(),
// 'FundingRound: Signup deadline must be in the future'
// );
poll = _poll;
}
/**
* @dev Submit a batch of messages along with corresponding ephemeral public keys.
*/
function vote(Message memory _message, PubKey memory _encPubKey) external {
poll.publishMessage(_message, _encPubKey);
emit Voted(msg.sender);
}
/**
* @dev Publish the IPFS hash of the vote tally. Only coordinator can publish.
* @param _tallyHash IPFS hash of the vote tally.
*/
function publishTallyHash(string calldata _tallyHash) external {
require(
msg.sender == coordinator,
"FundingRound: Sender is not the coordinator"
);
require(!isFinalized, "FundingRound: Round finalized");
require(
bytes(_tallyHash).length != 0,
"FundingRound: Tally hash is empty string"
);
tallyHash = _tallyHash;
emit TallyPublished(_tallyHash);
}
/**
* @dev Get the total amount of votes from MACI,
* verify the total amount of spent voice credits across all recipients,
* and allow recipients to claim funds.
* @param _totalSpent Total amount of spent voice credits.
* @param _totalSpentSalt The salt.
*/
function finalize(uint256 _totalSpent, uint256 _totalSpentSalt)
external
onlyOwner
{
require(!isFinalized, "FundingRound: Already finalized");
require(address(poll) != address(0), "FundingRound: MACI not deployed");
//NOTE: stub out or add extra functions to poll ABI
// require(!poll.isAfterVotingDeadline(), 'FundingRound: Voting has not been finished');
// require(!poll.hasUntalliedStateLeaves(), 'FundingRound: Votes has not been tallied');
// require(bytes(tallyHash).length != 0, 'FundingRound: Tally hash has not been published');
//NOTE: stubout or add extra functions to poll ABI ist his in PollProcessorAndTallyer?
totalVotes = poll.totalVotes();
// If nobody voted, the round should be cancelled to avoid locking of matching funds
require(totalVotes > 0, "FundingRound: No votes");
//NOTE: stubout or add extra functions to poll ABI is this in PollProcessorAndTallyer?
// bool verified = poll.verifySpentVoiceCredits(
// _totalSpent,
// _totalSpentSalt
// );
require(
verified,
"FundingRound: Incorrect total amount of spent voice credits"
);
totalSpent = _totalSpent;
// Total amount of spent voice credits is the size of the pool of direct rewards.
// Everything else, including unspent voice credits and downscaling error,
// is considered a part of the matching pool
matchingPoolSize =
nativeToken.balanceOf(address(this)) -
totalSpent *
voiceCreditFactor;
isFinalized = true;
}
/**
* @dev Cancel funding round.
*/
function cancel() external onlyOwner {
require(!isFinalized, "FundingRound: Already finalized");
isFinalized = true;
isCancelled = true;
}
/**
* @dev Get allocated token amount (without verification).
* @param _tallyResult The result of vote tally for the recipient.
* @param _spent The amount of voice credits spent on the recipient.
*/
function getAllocatedAmount(uint256 _tallyResult, uint256 _spent)
public
view
returns (uint256)
{
return
(matchingPoolSize * _tallyResult) /
totalVotes +
_spent *
voiceCreditFactor;
}
/**
* @dev Claim allocated tokens.
* @param _voteOptionIndex Vote option index.
* @param _tallyResult The result of vote tally for the recipient.
* @param _tallyResultProof Proof of correctness of the vote tally.
* @param _tallyResultSalt Salt.
* @param _spent The amount of voice credits spent on the recipient.
* @param _spentProof Proof of correctness for the amount of spent credits.
* @param _spentSalt Salt.
*/
function claimFunds(
uint256 _voteOptionIndex,
uint256 _tallyResult,
uint256[][] calldata _tallyResultProof,
uint256 _tallyResultSalt,
uint256 _spent,
uint256[][] calldata _spentProof,
uint256 _newTallyCommitment
) external {
require(isFinalized, "FundingRound: Round not finalized");
require(!isCancelled, "FundingRound: Round has been cancelled");
require(
!recipients[_voteOptionIndex],
"FundingRound: Funds already claimed"
);
// create scope to avoid 'stack too deep' error
{
//NOTE: Verify Tally Proof
(uint256 numSignUps, ) = poll.numSignUpsAndMessages();
(, uint256 tallyBatchSize) = poll.batchSizes();
bool isValid = poll.verifyTallyProof(
poll,
_tallyResultProof,
numSignUps,
batchStartIndex,
tallyBatchSize,
_newTallyCommitment
);
//NOTE: Verify voice credits spent on the recipient trying to claim
require(resultVerified, "FundingRound: Incorrect tally result");
//NOTE: stubout or add extra functions to poll ABI
// bool spentVerified = poll.verifyPerVOSpentVoiceCredits(
// voteOptionTreeDepth,
// _voteOptionIndex,
// _spent,
// _spentProof,
// _spentSalt
// );
// require(
// spentVerified,
// "FundingRound: Incorrect amount of spent voice credits"
// );
}
recipients[_voteOptionIndex] = true;
(uint256 deployTime, uint256 duration) = poll.getDeployTimeAndDuration();
uint256 startTime = deployTime;
address recipient = recipientRegistry.getRecipientAddress(
_voteOptionIndex,
startTime,
startTime +
duration
);
if (recipient == address(0)) {
// Send funds back to the matching pool
recipient = owner();
}
//NOTE: need to implement verifyPerVOSpentVoiceCredits
uint256 allocatedAmount = getAllocatedAmount(_tallyResult, _spent);
nativeToken.safeTransfer(recipient, allocatedAmount);
emit FundsClaimed(_voteOptionIndex, recipient, allocatedAmount);
}
}

View File

@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.2;
interface IFundingRound {
function setPoll(Poll _poll) external;
function vote(Message memory _message, PubKey memory _encPubKey) external;
function publishTallyHash(string calldata _tallyHash) external;
function finalize(uint256 _totalSpent, uint256 _totalSpentSalt) external;
function cancel() external;
function getAllocatedAmount(uint256 _tallyResult, uint256 _spent) external;
function claimFunds(
uint256 _voteOptionIndex,
uint256 _tallyResult,
uint256[][] calldata _tallyResultProof,
uint256 _tallyResultSalt,
uint256 _spent,
uint256[][] calldata _spentProof,
uint256 _newTallyCommitment
) external;
}

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.2;
import { VkRegistry } from "./VkRegistry.sol";
import { AccQueue } from "./trees/AccQueue.sol";
interface IMACI {
function stateTreeDepth() external view returns (uint8);
function vkRegistry() external view returns (VkRegistry);
function getStateAqRoot() external view returns (uint256);
function mergeStateAqSubRoots(uint256 _numSrQueueOps, uint256 _pollId) external;
function mergeStateAq(uint256 _pollId) external returns (uint256);
function numSignUps() external view returns (uint256);
function stateAq() external view returns (AccQueue);
}

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.2;
interface IPoll {
function numSignUpsAndMessages() external returns (uint256, uint256);
function batchSizes() external returns (uint256, uint256);
function publishMessage(Message memory _message, PubKey memory _encPubKey);
function isAfterVotingDeadline() external view returns (bool);
function hasUntalliedStateLeaves() external view returns (bool);
function verifyPerVOSpentVoiceCredits(
uint256 voteOptionTreeDepth,
uint256_voteOptionIndex,
uint256 _spent,
uint256 _spentProof,
uint256 spentSalt
) external returns (bool);
function verifyTallyResult(
uint256 voteOptionTreeDepth,
uint256 voteOptionIndex,
uint256 tallyResult,
uint256 tallyResultProof,
uint256 tallyResultSalt
) external returns (bool);
function verifySpentVoiceCredits(
uint256 _totalSpent,
uint256 _totalSpentSalt
) external returns (bool);
function verifyTallyProof(
Poll _poll,
uint256[8] memory _proof,
uint256 _numSignUps,
uint256 _batchStartIndex,
uint256 _tallyBatchSize,
uint256 _newTallyCommitment
) external view returns (bool);
}

View File

@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.2;
import './FundingRound.sol';
import '../DomainObjs.sol';
interface IQuadraticFunding {
function setUserRegistry(IUserRegistry _userRegistry) external;
function setRecipientRegistry(IRecipientRegistry _recipientRegistry) external;
function setToken(address _token) external;
function setCoordinator( address _coordinator, PubKey memory _coordinatorPubKey) external;
function addFundingSource(address _source) external;
function removeFundingSource(address _source) external;
function getMatchingFunds(ERC20 token) external view;
function transferMatchingFunds( uint256 _totalSpent, uint256 _totalSpentSalt )external;
function signUp( PubKey memory _pubKey, uint256 _initialVoiceCreditBalance) internal;
function register(PubKey memory _pubKey) external;
function contribute( PubKey memory _pubKey, uint256 amount, address _fundingRound) external;
function withdrawContribution() external;
function getCurrentRound() public view returns (FundingRound);
function deployNewRound() external;
function cancelCurrentRound() external;
}

View File

@@ -0,0 +1,64 @@
{
"name": "@quadratic-funding/contracts",
"version": "0.0.1",
"description": "quadratic-funding Contracts",
"main": "index.js",
"scripts": {
"clean": "shx rm -rf ./build ./cache ./coverage ./typechain ./coverage.json ./abi",
"hardhat": "hardhat",
"build": "hardhat compile",
"coverage": "yarn typechain && hardhat coverage --solcoverjs ./.solcover.js --temp build/contracts --testfiles \"./tests/**/*.ts\"",
"node": "hardhat node --port 18545",
"test": "hardhat test",
"lint:js": "eslint '{tests,scripts}/**/*.ts'",
"lint:solidity": "solhint 'contracts/**/*.sol'",
"lint": "yarn lint:solidity",
"typechain": "hardhat typechain",
"docs:generate":"npx solidity-docgen --solc-module solc-0.7 -t ./docs/templates/ -i ./contracts -o ./docs"
},
"dependencies": {
"@openzeppelin/contracts": "^3.3.0-solc-0.7",
"argparse": "^1.0.10",
"circomlib": "0.5.1",
"maci-contracts":"^1.0.4"
},
"devDependencies": {
"maci-circuits": "^1.0.4",
"maci-core": "^1.0.4",
"maci-crypto": "^1.0.4",
"maci-domainobjs": "^1.0.4",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^2.1.1",
"@nomiclabs/hardhat-ganache": "^2.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@typechain/ethers-v5": "^8.0.1",
"@typechain/hardhat": "^3.0.0",
"@types/chai": "^4.2.15",
"@types/chai-as-promised": "^7.1.3",
"@types/mocha": "^9.0.0",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.34.0",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"dotenv": "^10.0.0",
"eslint": "^8.2.0",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.5.1",
"hardhat": "^2.6.8",
"hardhat-abi-exporter": "^2.2.1",
"hardhat-contract-sizer": "^2.0.3",
"hardhat-gas-reporter": "^1.0.4",
"mocha": "^9.1.3",
"shelljs": "^0.8.4",
"shx": "^0.3.3",
"solc-0.7": "npm:solc@^0.7.2",
"solidity-coverage": "^0.7.17",
"solhint": "^3.3.6",
"ts-node": "^10.4.0",
"typechain": "^6.0.2",
"typescript": "^4.4.4"
},
"author": "",
"license": "GPL-3.0"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
const shell = require("shelljs");
module.exports = {
istanbulReporter: ["html", "lcov"],
providerOptions: {
mnemonic: process.env.MNEMONIC,
},
skipFiles: ["tests"],
};

View File

@@ -0,0 +1,87 @@
describe('Grant Round Factory', () => {
beforeEach(async () => {
})
it('verify - initializes properly', async () => {
})
it('verify - configured properly', async () => {
})
describe('managing funding sources', () => {
it('verify - allows owner to add funding source', async () => {
})
it('require fail - allows only owner to add funding source', async () => {
})
it('require fail - reverts if funding source is already added', async () => {
})
it('verify - allows owner to remove funding source', async () => {
})
it('require fail - allows only owner to remove funding source', async () => {
})
it('require fail - reverts if funding source is already removed', async () => {
})
})
it('allows direct contributions to the matching pool', async () => {
})
describe('withdrawing funds', () => {
it('allows contributors to withdraw funds', async () => {
})
it('disallows withdrawal if round is not cancelled', async () => {
})
it('reverts if user did not contribute to the round', async () => {
})
it('reverts if funds are already withdrawn', async () => {
})
})
describe('transferring matching funds', () => {
it('returns the amount of available matching funding', async () => {
})
it('pulls funds from funding source', async () => {
})
it('pulls funds from funding source if allowance is greater than balance', async () => {
})
})
})

View File

@@ -0,0 +1,230 @@
describe('Funding Round Factory', () => {
beforeEach(async () => {
})
it('initializes', async () => {
})
it('configured', async () => {
})
describe('Grant Round', () => {
it('initializes grant round correctly', async () => {
})
describe('accepting contributions', () => {
it('accepts contributions from everyone', async () => {
})
it('rejects contributions if MACI has not been linked to a round', async () => {
})
it('limits the number of contributors', async () => {
})
it('rejects contributions if funding round has been finalized', async () => {
})
it('rejects contributions with zero amount', async () => {
})
it('rejects contributions that are too large', async () => {
})
it('allows to contribute only once per round', async () => {
})
it('requires approval to transfer', async () => {
})
it('rejects contributions from unverified users', async () => {
})
it('should not allow users who have not contributed to sign up directly in MACI', async () => {
})
it('should not allow users who have already signed up to sign up directly in MACI', async () => {
})
it('should not return the amount of voice credits for user who has not contributed', async () => {
})
})
describe('voting', () => {
it('submits a vote', async () => {
})
it('submits a key-changing message', async () => {
})
it('submits an invalid vote', async () => {
})
it('submits a vote for invalid vote option', async () => {
})
it('submits a batch of messages', async () => {
})
describe('publishing tally hash', () => {
it('allows coordinator to publish vote tally hash', async () => {
})
it('allows only coordinator to publish tally hash', async () => {
})
it('reverts if round has been finalized', async () => {
})
it('rejects empty string', async () => {
})
})
describe('finalizing round', () => {
it('allows owner to finalize round', async () => {
})
it('allows owner to finalize round when matching pool is empty', async () => {
})
it('counts direct token transfers to funding round as matching pool contributions', async () => {
})
it('reverts if round has been finalized already', async () => {
})
it('reverts if voting is still in progress', async () => {
})
it('reverts if votes has not been tallied', async () => {
})
it('reverts if tally hash has not been published', async () => {
})
it('reverts if total votes is zero', async () => {
})
it('reverts if total amount of spent voice credits is incorrect', async () => {
})
it('allows only owner to finalize round', async () => {
})
})
describe('cancelling round', () => {
it('allows owner to cancel round', async () => {
})
it('reverts if round has been finalized already', async () => {
})
it('reverts if round has been cancelled already', async () => {
})
it('allows only owner to cancel round', async () => {
})
})
describe('claiming funds', () => {
it('allows recipient to claim allocated funds', async () => {
})
it('allows address different than recipient to claim allocated funds', async () => {
})
it('allows recipient to claim zero amount', async () => {
})
it('allows recipient to claim if the matching pool is empty', async () => {
})
it('should not allow recipient to claim funds if round has not been finalized', async () => {
})
it('should not allow recipient to claim funds if round has been cancelled', async () => {
})
it('sends funds allocated to unverified recipients back to matching pool', async () => {
})
it('allows recipient to claim allocated funds only once', async () => {
})
it('should verify that tally result is correct', async () => {
})
it('should verify that amount of spent voice credits is correct', async () => {
})
})
})
describe('cancelling round', () => {
it('allows owner to cancel round', async () => {
})
it('allows only owner to cancel round', async () => {
})
it('reverts if round has not been deployed', async () => {
})
it('reverts if round is finalized', async () => {
})
})
})

View File

@@ -0,0 +1,29 @@
describe('Grant Round Factory', () => {
beforeEach(async () => {
})
it('verify - initializes properly', async () => {
})
it('verify - configured properly', async () => {
})
describe('changing recipient registry', () => {
it('verify - allows owner to set recipient registry', async () => {
})
it('require fail - allows only owner to set recipient registry', async () => {
})
it('varify - allows owner to change recipient registry', async () => {
})
})
})

View File

@@ -0,0 +1,299 @@
import { ethers } from "hardhat";
import { BigNumber, Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {MACI} from '../../typechain/MACI'
import {MACI__factory} from '../../typechain/factories/MACI__factory'
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure Deploy", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let QFIFactory: QFI__factory;
let MACIFactory: MACI__factory;
let poseidonT3: PoseidonT3;
let poseidonT4: PoseidonT4;
let poseidonT5: PoseidonT5;
let poseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let qfi: QFI;
let maci: MACI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
poseidonT3 = await PoseidonT3Factory.deploy();
poseidonT4 = await PoseidonT4Factory.deploy();
poseidonT5 = await PoseidonT5Factory.deploy();
poseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: poseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: poseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: poseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: poseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
MACIFactory = new MACI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
});
it("deploys Poseidon Contracts", async () => {
expect(poseidonT3.address).to.not.equal(ethers.constants.AddressZero);
expect(poseidonT4.address).to.not.equal(ethers.constants.AddressZero);
expect(poseidonT5.address).to.not.equal(ethers.constants.AddressZero);
expect(poseidonT6.address).to.not.equal(ethers.constants.AddressZero);
//NOTE: These should be correct as they are from precompiled contracts
expect( await poseidonT3["poseidon(uint256[2])"]([BigNumber.from(0), BigNumber.from(0)]) ).to.be.equal(BigNumber.from('0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864'));
expect( await poseidonT6["poseidon(uint256[5])"]([BigNumber.from(0), BigNumber.from(0),BigNumber.from(0), BigNumber.from(0),BigNumber.from(0)]) ).to.be.equal(BigNumber.from('0x2066be41bebe6caf7e079360abe14fbf9118c62eabc42e2fe75e342b160a95bc'));
//NOTE: These should be incorrect as they are not from precompiled contracts
expect( await poseidonT4.poseidon([BigNumber.from(0), BigNumber.from(0),BigNumber.from(0)])).to.be.equal(BigNumber.from('0x00'));
expect( await poseidonT5.poseidon([BigNumber.from(0), BigNumber.from(0),BigNumber.from(0),BigNumber.from(0)])).to.be.equal(BigNumber.from('0x00'));
});
it("deploys GrantRoundFactory Contracts", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
await expect((await grantRoundFactory.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys PollFactory Contracts", async () => {
pollFactory = await PollFactoryFactory.deploy();
await expect((await pollFactory.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys MessageAqFactory Contract", async () => {
messageAqFactory = await MessageAqFactoryFactory.deploy();
await expect((await messageAqFactory.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys VKRegistry Contracts", async () => {
vkRegistry = await VKRegistryFactory.deploy();
await expect((await vkRegistry.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys constantInitialVoiceCreditProxy Contract", async () => {
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
await expect((await constantInitialVoiceCreditProxy.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys freeForAllGateKeeper Contract", async () => {
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
await expect((await freeForAllGateKeeper.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys Recipient Registry Contract", async () => {
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
await expect((await optimisticRecipientRegistry.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys ERC20 token Contract", async () => {
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
await expect((await baseERC20Token.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys QFI Contracts", async () => {
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect((await qfi.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys MACI Contracts", async () => {
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
pollFactory = await PollFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
maci = await MACIFactory.deploy(
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect((await maci.deployTransaction.wait()).status).to.not.equal(0);
});
it("transfers PollFactory ownership to Quadratic Funding Infrastructure Contract", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
const tx = await grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
});
it("transfers messageAqFactory ownership to Poll Factory Contract", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
});
it("initializes Quadratic Funding Infrastructure", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
});

View File

@@ -0,0 +1,177 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure Configure", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
});
it("initialises Quadratic Funding Infrastructure", async () => {
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("Starts a new Quadratic Funding Grant Round", async () => {
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("", async () => {
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("", async () => {
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("", async () => {
});
});

View File

@@ -0,0 +1,119 @@
describe('Funding Round Factory', () => {
beforeEach(async () => {
})
it('initializes', async () => {
})
it('configured', async () => {
})
describe('changing signup gatekeeper', () => {
it('allows owner to set signup gatekeeper', async () => {
})
it('allows only owner to set signup gatekeeper', async () => {
})
it('allows owner to change signup gatekeeper', async () => {
})
})
describe('deploying funding round', () => {
it('deploys funding round', async () => {
})
it('require fail - reverts if signup gatekeeper is not set', async () => {
})
it('require fail - reverts if recipient registry is not set', async () => {
})
it('require fail - reverts if native token is not set', async () => {
})
it('require fail - reverts if coordinator is not set', async () => {
})
it('require fail - reverts if current round is not finalized', async () => {
})
it('require fail - verify - deploys new funding round after previous round has been finalized', async () => {
})
it('require fail - only owner can deploy funding round', async () => {
})
})
describe('transferring matching funds', () => {
it('returns the amount of available matching funding', async () => {
})
it('allows owner to finalize round', async () => {
})
it('does not allow funds to be sent without a tally', async () => {
})
it('pulls funds from funding source', async () => {
})
it('pulls funds from funding source if allowance is greater than balance', async () => {
})
it('allows only owner to finalize round', async () => {
})
it('reverts if round has not been deployed', async () => {
})
})
// describe('cancelling round', () => {
// it('allows owner to cancel round', async () => {
// })
// it('allows only owner to cancel round', async () => {
// })
// it('reverts if round has not been deployed', async () => {
// })
// it('reverts if round is finalized', async () => {
// })
// })
// it('allows owner to set native token', async () => {
// })
// it('only owner can set native token', async () => {
// })
})

View File

@@ -0,0 +1,299 @@
import { ethers } from "hardhat";
import { BigNumber, Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {MACI} from '../../typechain/MACI'
import {MACI__factory} from '../../typechain/factories/MACI__factory'
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure Deploy", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let QFIFactory: QFI__factory;
let MACIFactory: MACI__factory;
let poseidonT3: PoseidonT3;
let poseidonT4: PoseidonT4;
let poseidonT5: PoseidonT5;
let poseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let qfi: QFI;
let maci: MACI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
poseidonT3 = await PoseidonT3Factory.deploy();
poseidonT4 = await PoseidonT4Factory.deploy();
poseidonT5 = await PoseidonT5Factory.deploy();
poseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: poseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: poseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: poseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: poseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
MACIFactory = new MACI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
});
it("deploys Poseidon Contracts", async () => {
expect(poseidonT3.address).to.not.equal(ethers.constants.AddressZero);
expect(poseidonT4.address).to.not.equal(ethers.constants.AddressZero);
expect(poseidonT5.address).to.not.equal(ethers.constants.AddressZero);
expect(poseidonT6.address).to.not.equal(ethers.constants.AddressZero);
//NOTE: These should be correct as they are from precompiled contracts
expect( await poseidonT3["poseidon(uint256[2])"]([BigNumber.from(0), BigNumber.from(0)]) ).to.be.equal(BigNumber.from('0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864'));
expect( await poseidonT6["poseidon(uint256[5])"]([BigNumber.from(0), BigNumber.from(0),BigNumber.from(0), BigNumber.from(0),BigNumber.from(0)]) ).to.be.equal(BigNumber.from('0x2066be41bebe6caf7e079360abe14fbf9118c62eabc42e2fe75e342b160a95bc'));
//NOTE: These should bbe incorrect as they are not from precompiled contracts
expect( await poseidonT4.poseidon([BigNumber.from(0), BigNumber.from(0),BigNumber.from(0)])).to.be.equal(BigNumber.from('0x00'));
expect( await poseidonT5.poseidon([BigNumber.from(0), BigNumber.from(0),BigNumber.from(0),BigNumber.from(0)])).to.be.equal(BigNumber.from('0x00'));
});
it("deploys GrantRoundFactory Contracts", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
await expect((await grantRoundFactory.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys PollFactory Contracts", async () => {
pollFactory = await PollFactoryFactory.deploy();
await expect((await pollFactory.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys MessageAqFactory Contract", async () => {
messageAqFactory = await MessageAqFactoryFactory.deploy();
await expect((await messageAqFactory.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys VKRegistry Contracts", async () => {
vkRegistry = await VKRegistryFactory.deploy();
await expect((await vkRegistry.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys constantInitialVoiceCreditProxy Contract", async () => {
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
await expect((await constantInitialVoiceCreditProxy.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys freeForAllGateKeeper Contract", async () => {
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
await expect((await freeForAllGateKeeper.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys Recipient Registry Contract", async () => {
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
await expect((await optimisticRecipientRegistry.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys ERC20 token Contract", async () => {
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
await expect((await baseERC20Token.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys QFI Contracts", async () => {
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect((await qfi.deployTransaction.wait()).status).to.not.equal(0);
});
it("deploys MACI Contracts", async () => {
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
pollFactory = await PollFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
maci = await MACIFactory.deploy(
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect((await maci.deployTransaction.wait()).status).to.not.equal(0);
});
it("transfers PollFactory ownership to Quadratic Funding Infrastructure Contract", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
});
it("transfers messageAqFactory ownership to Poll Factory Contract", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
});
it("initializes Quadratic Funding Infrastructure", async () => {
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
});

View File

@@ -0,0 +1,177 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure Configure", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
});
it("initialises Quadratic Funding Infrastructure", async () => {
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("Starts a new Quadratic Voting Poll", async () => {
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("", async () => {
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("", async () => {
});
});

View File

@@ -0,0 +1,179 @@
import { ethers } from "hardhat";
import { Signer } from "ethers";
import chai from "chai";
import { solidity } from "ethereum-waffle";
import { PoseidonT3 } from "../../typechain/PoseidonT3";
import { PoseidonT3__factory } from "../../typechain/factories/PoseidonT3__factory";
import { PoseidonT4 } from "../../typechain/PoseidonT4";
import { PoseidonT4__factory } from "../../typechain/factories/PoseidonT4__factory";
import { PoseidonT5 } from "../../typechain/PoseidonT5";
import { PoseidonT5__factory } from "../../typechain/factories/PoseidonT5__factory";
import { PoseidonT6 } from "../../typechain/PoseidonT6";
import { PoseidonT6__factory } from "../../typechain/factories/PoseidonT6__factory";
import {
GrantRoundFactoryLibraryAddresses,
GrantRoundFactory__factory,
} from "../../typechain/factories/GrantRoundFactory__factory";
import { PollFactory__factory, PollFactoryLibraryAddresses } from "../../typechain/factories/PollFactory__factory";
import {
MessageAqFactory__factory,
MessageAqFactoryLibraryAddresses,
} from "../../typechain/factories/MessageAqFactory__factory";
import { QFI__factory, QFILibraryAddresses } from "../../typechain/factories/QFI__factory";
import { GrantRoundFactory } from "../../typechain/GrantRoundFactory";
import { PollFactory } from "../../typechain/PollFactory";
import { MessageAqFactory } from "../../typechain/MessageAqFactory";
import { QFI } from "../../typechain/QFI";
import { VkRegistry__factory } from "../../typechain/factories/VkRegistry__factory";
import { FreeForAllGatekeeper__factory } from "../../typechain/factories/FreeForAllGatekeeper__factory";
import { ConstantInitialVoiceCreditProxy__factory } from "../../typechain/factories/ConstantInitialVoiceCreditProxy__factory";
import { VkRegistry } from "../../typechain/VkRegistry";
import { FreeForAllGatekeeper } from "../../typechain/FreeForAllGatekeeper";
import { ConstantInitialVoiceCreditProxy } from "../../typechain/ConstantInitialVoiceCreditProxy";
import { OptimisticRecipientRegistry } from "../../typechain/OptimisticRecipientRegistry";
import { OptimisticRecipientRegistry__factory } from "../../typechain/factories/OptimisticRecipientRegistry__factory";
import { BaseERC20Token } from "../../typechain/BaseERC20Token";
import { BaseERC20Token__factory } from "../../typechain/factories/BaseERC20Token__factory";
import { PollProcessorAndTallyer } from "../../typechain/PollProcessorAndTallyer";
import { PollProcessorAndTallyer__factory } from "../../typechain/factories/PollProcessorAndTallyer__factory";
import {MockVerifier} from '../../typechain/MockVerifier'
import {MockVerifier__factory} from '../../typechain/factories/MockVerifier__factory'
chai.use(solidity);
const { expect } = chai;
describe("Quadratic Funding Infrastructure New Grant Round", () => {
let deployer: Signer;
let deployerAddress: string;
let PoseidonT3Factory: PoseidonT3__factory;
let PoseidonT4Factory: PoseidonT4__factory;
let PoseidonT5Factory: PoseidonT5__factory;
let PoseidonT6Factory: PoseidonT6__factory;
let linkedLibraryAddresses:
| QFILibraryAddresses
| PollFactoryLibraryAddresses
| MessageAqFactoryLibraryAddresses
| GrantRoundFactoryLibraryAddresses;
let GrantRoundFactory: GrantRoundFactory__factory;
let PollFactoryFactory: PollFactory__factory;
let MessageAqFactoryFactory: MessageAqFactory__factory;
let FreeForAllGateKeeperFactory: FreeForAllGatekeeper__factory;
let ConstantInitialVoiceCreditProxyFactory: ConstantInitialVoiceCreditProxy__factory;
let VKRegistryFactory: VkRegistry__factory;
let RecipientRegistryFactory: OptimisticRecipientRegistry__factory;
let BaseERC20TokenFactory: BaseERC20Token__factory;
let PollProcessorAndTallyerFactory: PollProcessorAndTallyer__factory;
let MockVerifierFactory: MockVerifier__factory;
let QFIFactory: QFI__factory;
let PoseidonT3: PoseidonT3;
let PoseidonT4: PoseidonT4;
let PoseidonT5: PoseidonT5;
let PoseidonT6: PoseidonT6;
let grantRoundFactory: GrantRoundFactory;
let pollFactory: PollFactory;
let messageAqFactory: MessageAqFactory;
let messageAqFactoryGrants: MessageAqFactory;
let vkRegistry: VkRegistry;
let constantInitialVoiceCreditProxy: ConstantInitialVoiceCreditProxy;
let freeForAllGateKeeper: FreeForAllGatekeeper;
let optimisticRecipientRegistry: OptimisticRecipientRegistry;
let baseERC20Token: BaseERC20Token;
let pollProcessorAndTallyer: PollProcessorAndTallyer;
let mockVerifier: MockVerifier;
let qfi: QFI;
beforeEach(async () => {
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
PoseidonT3Factory = new PoseidonT3__factory(deployer);
PoseidonT4Factory = new PoseidonT4__factory(deployer);
PoseidonT5Factory = new PoseidonT5__factory(deployer);
PoseidonT6Factory = new PoseidonT6__factory(deployer);
PoseidonT3 = await PoseidonT3Factory.deploy();
PoseidonT4 = await PoseidonT4Factory.deploy();
PoseidonT5 = await PoseidonT5Factory.deploy();
PoseidonT6 = await PoseidonT6Factory.deploy();
linkedLibraryAddresses = {
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT5"]: PoseidonT5.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT3"]: PoseidonT3.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT6"]: PoseidonT6.address,
["maci-contracts/contracts/crypto/Hasher.sol:PoseidonT4"]: PoseidonT4.address,
};
GrantRoundFactory = new GrantRoundFactory__factory({ ...linkedLibraryAddresses }, deployer);
PollFactoryFactory = new PollFactory__factory({ ...linkedLibraryAddresses }, deployer);
MessageAqFactoryFactory = new MessageAqFactory__factory({ ...linkedLibraryAddresses }, deployer);
QFIFactory = new QFI__factory({ ...linkedLibraryAddresses }, deployer);
VKRegistryFactory = new VkRegistry__factory(deployer);
ConstantInitialVoiceCreditProxyFactory = new ConstantInitialVoiceCreditProxy__factory(deployer);
FreeForAllGateKeeperFactory = new FreeForAllGatekeeper__factory(deployer);
RecipientRegistryFactory = new OptimisticRecipientRegistry__factory(deployer);
BaseERC20TokenFactory = new BaseERC20Token__factory(deployer);
PollProcessorAndTallyerFactory = new PollProcessorAndTallyer__factory(deployer);
MockVerifierFactory = new MockVerifier__factory(deployer);
optimisticRecipientRegistry = await RecipientRegistryFactory.deploy(0, 0, deployerAddress);
grantRoundFactory = await GrantRoundFactory.deploy();
grantRoundFactory.setRecipientRegistry(optimisticRecipientRegistry.address);
pollFactory = await PollFactoryFactory.deploy();
messageAqFactory = await MessageAqFactoryFactory.deploy();
messageAqFactoryGrants = await MessageAqFactoryFactory.deploy();
freeForAllGateKeeper = await FreeForAllGateKeeperFactory.deploy();
constantInitialVoiceCreditProxy = await ConstantInitialVoiceCreditProxyFactory.deploy(0);
vkRegistry = await VKRegistryFactory.deploy();
baseERC20Token = await BaseERC20TokenFactory.deploy(100);
mockVerifier = await MockVerifierFactory.deploy();
pollProcessorAndTallyer = await PollProcessorAndTallyerFactory.deploy(mockVerifier.address);
qfi = await QFIFactory.deploy(
baseERC20Token.address,
grantRoundFactory.address,
pollFactory.address,
freeForAllGateKeeper.address,
constantInitialVoiceCreditProxy.address
);
await expect(pollFactory.transferOwnership(qfi.address))
.to.emit(pollFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(grantRoundFactory.transferOwnership(qfi.address))
.to.emit(grantRoundFactory, "OwnershipTransferred")
.withArgs(deployerAddress, qfi.address);
await expect(messageAqFactory.transferOwnership(pollFactory.address))
.to.emit(messageAqFactory, "OwnershipTransferred")
.withArgs(deployerAddress, pollFactory.address);
await expect(messageAqFactoryGrants.transferOwnership(grantRoundFactory.address))
.to.emit(messageAqFactoryGrants, "OwnershipTransferred")
.withArgs(deployerAddress, grantRoundFactory.address);
await expect(qfi.initialize(vkRegistry.address, messageAqFactory.address, messageAqFactoryGrants.address))
.to.emit(qfi, "Init")
.withArgs(vkRegistry.address, messageAqFactory.address);
});
it("", async () => {
});
});

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"resolveJsonModule": true
},
"include": ["./scripts", "./tests", "./typechain"],
"files": [
"./hardhat.config.ts",
"./node_modules/@nomiclabs/hardhat-ethers/src/type-extensions.d.ts",
"./node_modules/@nomiclabs/hardhat-waffle/src/type-extensions.d.ts"
]
}

23
packages/hooks/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

23
packages/ui/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

22449
yarn.lock Normal file

File diff suppressed because it is too large Load Diff