mirror of
https://github.com/quadratic-gardens/qfi.git
synced 2026-01-10 06:28:12 -05:00
squash and merge
This commit is contained in:
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal 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
6
lerna.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"packages": ["packages/*"],
|
||||
"version": "0.0.0",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true
|
||||
}
|
||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "qfi",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
41
package.json
Normal file
41
package.json
Normal 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
23
packages/app/.gitignore
vendored
Normal 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
30
packages/contracts/.gitignore
vendored
Normal 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*
|
||||
11
packages/contracts/.solhint.json
Normal file
11
packages/contracts/.solhint.json
Normal 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"
|
||||
}
|
||||
}
|
||||
1
packages/contracts/.solhintignore
Normal file
1
packages/contracts/.solhintignore
Normal file
@@ -0,0 +1 @@
|
||||
contracts/snarkVerifiers/
|
||||
13
packages/contracts/contracts/BaseERC20Token.sol
Normal file
13
packages/contracts/contracts/BaseERC20Token.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
99
packages/contracts/contracts/FundsManager.sol
Normal file
99
packages/contracts/contracts/FundsManager.sol
Normal 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));
|
||||
}
|
||||
}
|
||||
245
packages/contracts/contracts/GrantRound.sol
Normal file
245
packages/contracts/contracts/GrantRound.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
148
packages/contracts/contracts/GrantRoundFactory.sol
Normal file
148
packages/contracts/contracts/GrantRoundFactory.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
340
packages/contracts/contracts/QFI.sol
Normal file
340
packages/contracts/contracts/QFI.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
29
packages/contracts/docs/GrantRound.md
Normal file
29
packages/contracts/docs/GrantRound.md
Normal 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.
|
||||
|
||||
46
packages/contracts/docs/GrantRoundFactory.md
Normal file
46
packages/contracts/docs/GrantRoundFactory.md
Normal 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
|
||||
|
||||
65
packages/contracts/docs/QFI.md
Normal file
65
packages/contracts/docs/QFI.md
Normal 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
|
||||
5
packages/contracts/docs/README.md
Normal file
5
packages/contracts/docs/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Introduction
|
||||
|
||||
* [Quadratic Funding Infrastructure Docs](QFI.md)
|
||||
* [Grant Round Factory Docs](GrantRoundFactory.md)
|
||||
* [Grant Round Docs](GrantRound.md)
|
||||
5
packages/contracts/docs/SUMMARY.md
Normal file
5
packages/contracts/docs/SUMMARY.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Summary
|
||||
|
||||
* [Quadratic Funding Infrastructure Docs](QFI.md)
|
||||
* [Grant Round Factory Docs](GrantRoundFactory.md)
|
||||
* [Grant Round Docs](GrantRound.md)
|
||||
18
packages/contracts/docs/book.json
Normal file
18
packages/contracts/docs/book.json
Normal 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;"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
packages/contracts/docs/package-lock.json
generated
Normal file
21
packages/contracts/docs/package-lock.json
generated
Normal 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="
|
||||
}
|
||||
}
|
||||
}
|
||||
7
packages/contracts/docs/rtfm.md
Normal file
7
packages/contracts/docs/rtfm.md
Normal 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
|
||||
51
packages/contracts/docs/templates/contract.hbs
vendored
Normal file
51
packages/contracts/docs/templates/contract.hbs
vendored
Normal 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}}
|
||||
163
packages/contracts/hardhat.config.ts
Normal file
163
packages/contracts/hardhat.config.ts
Normal 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;
|
||||
346
packages/contracts/napkin/ClrFund.sol
Normal file
346
packages/contracts/napkin/ClrFund.sol
Normal 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));
|
||||
}
|
||||
}
|
||||
114
packages/contracts/napkin/ClrHelper.sol
Normal file
114
packages/contracts/napkin/ClrHelper.sol
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
269
packages/contracts/napkin/FundingRound.sol
Normal file
269
packages/contracts/napkin/FundingRound.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
26
packages/contracts/napkin/IFundingRound.sol
Normal file
26
packages/contracts/napkin/IFundingRound.sol
Normal 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;
|
||||
}
|
||||
15
packages/contracts/napkin/IMACI.sol
Normal file
15
packages/contracts/napkin/IMACI.sol
Normal 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);
|
||||
}
|
||||
45
packages/contracts/napkin/IPoll.sol
Normal file
45
packages/contracts/napkin/IPoll.sol
Normal 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);
|
||||
|
||||
}
|
||||
26
packages/contracts/napkin/IQuadraticFunding.sol
Normal file
26
packages/contracts/napkin/IQuadraticFunding.sol
Normal 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;
|
||||
|
||||
}
|
||||
64
packages/contracts/package.json
Normal file
64
packages/contracts/package.json
Normal 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"
|
||||
}
|
||||
51
packages/contracts/precompiled/PoseidonT3.json
Normal file
51
packages/contracts/precompiled/PoseidonT3.json
Normal file
File diff suppressed because one or more lines are too long
51
packages/contracts/precompiled/PoseidonT6.json
Normal file
51
packages/contracts/precompiled/PoseidonT6.json
Normal file
File diff suppressed because one or more lines are too long
9
packages/contracts/solcover.js
Normal file
9
packages/contracts/solcover.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const shell = require("shelljs");
|
||||
|
||||
module.exports = {
|
||||
istanbulReporter: ["html", "lcov"],
|
||||
providerOptions: {
|
||||
mnemonic: process.env.MNEMONIC,
|
||||
},
|
||||
skipFiles: ["tests"],
|
||||
};
|
||||
87
packages/contracts/tests/FundsManager.ts
Normal file
87
packages/contracts/tests/FundsManager.ts
Normal 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 () => {
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
230
packages/contracts/tests/GrantRound.ts
Normal file
230
packages/contracts/tests/GrantRound.ts
Normal 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 () => {
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
29
packages/contracts/tests/GrantRoundFactory.sol
Normal file
29
packages/contracts/tests/GrantRoundFactory.sol
Normal 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 () => {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
299
packages/contracts/tests/QF/00-deploy.ts
Normal file
299
packages/contracts/tests/QF/00-deploy.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
177
packages/contracts/tests/QF/01-configure.ts
Normal file
177
packages/contracts/tests/QF/01-configure.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QF/02-grantRound.ts
Normal file
179
packages/contracts/tests/QF/02-grantRound.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QF/03-voting.ts
Normal file
179
packages/contracts/tests/QF/03-voting.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QF/04-process.ts
Normal file
179
packages/contracts/tests/QF/04-process.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QF/05-finalize.ts
Normal file
179
packages/contracts/tests/QF/05-finalize.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
119
packages/contracts/tests/QFI.ts
Normal file
119
packages/contracts/tests/QFI.ts
Normal 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 () => {
|
||||
|
||||
// })
|
||||
|
||||
})
|
||||
299
packages/contracts/tests/QV/00-deploy.ts
Normal file
299
packages/contracts/tests/QV/00-deploy.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
177
packages/contracts/tests/QV/01-configure.ts
Normal file
177
packages/contracts/tests/QV/01-configure.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QV/02-poll.ts
Normal file
179
packages/contracts/tests/QV/02-poll.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QV/03-voting.ts
Normal file
179
packages/contracts/tests/QV/03-voting.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QV/04-process.ts
Normal file
179
packages/contracts/tests/QV/04-process.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
179
packages/contracts/tests/QV/05-verify.ts
Normal file
179
packages/contracts/tests/QV/05-verify.ts
Normal 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 () => {
|
||||
|
||||
});
|
||||
});
|
||||
16
packages/contracts/tsconfig.json
Normal file
16
packages/contracts/tsconfig.json
Normal 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
23
packages/hooks/.gitignore
vendored
Normal 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
23
packages/ui/.gitignore
vendored
Normal 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*
|
||||
Reference in New Issue
Block a user