Skip to content

Commit

Permalink
feat(contracts): add deploy scripts
Browse files Browse the repository at this point in the history
- [x] Override deploy steps for poll factory and maci
- [x] Integration maci protocol deploy tasks
  • Loading branch information
0xmad committed Aug 19, 2024
1 parent 6cf7392 commit 11b9ee7
Show file tree
Hide file tree
Showing 10 changed files with 884 additions and 70 deletions.
3 changes: 2 additions & 1 deletion packages/contracts/contracts/mocks/Mocker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
pragma solidity ^0.8.20;

import "maci-contracts/contracts/crypto/Hasher.sol";

Check warning on line 4 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/crypto/Hasher.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "maci-contracts/contracts/crypto/Verifier.sol";

Check warning on line 5 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/crypto/Verifier.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "maci-contracts/contracts/crypto/MockVerifier.sol";

Check warning on line 6 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/crypto/MockVerifier.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "maci-contracts/contracts/gatekeepers/FreeForAllSignUpGatekeeper.sol";
import "maci-contracts/contracts/gatekeepers/FreeForAllGatekeeper.sol";

Check warning on line 7 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/gatekeepers/FreeForAllGatekeeper.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "maci-contracts/contracts/initialVoiceCreditProxy/ConstantInitialVoiceCreditProxy.sol";

Check warning on line 8 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/initialVoiceCreditProxy/ConstantInitialVoiceCreditProxy.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "maci-contracts/contracts/VkRegistry.sol";

Check warning on line 9 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/VkRegistry.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "maci-contracts/contracts/TallyFactory.sol";

Check warning on line 10 in packages/contracts/contracts/mocks/Mocker.sol

View workflow job for this annotation

GitHub Actions / check (lint:sol)

global import of path maci-contracts/contracts/TallyFactory.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
Expand Down
440 changes: 440 additions & 0 deletions packages/contracts/deploy-config-example.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions packages/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import "@nomicfoundation/hardhat-toolbox";
import dotenv from "dotenv";
import "hardhat-artifactor";
import "hardhat-contract-sizer";
import "maci-contracts/tasks/deploy";
import "maci-contracts/tasks/runner/deployFull";
import "maci-contracts/tasks/runner/verifyFull";
import "solidity-docgen";

import type { HardhatUserConfig } from "hardhat/config";

// Don't forget to import new tasks here
import "./tasks/deploy";
import { EChainId, ESupportedChains, getEtherscanApiKeys, getNetworkRpcUrls } from "./tasks/helpers/constants";

dotenv.config();
Expand Down
12 changes: 8 additions & 4 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,26 @@
"types": "tsc -p tsconfig.json --noEmit",
"docs": "hardhat docgen",
"coverage": "BLOCK_GAS_LIMIT=1599511627775 hardhat coverage",
"test": "hardhat test --network hardhat"
"test": "hardhat test --network hardhat",
"deploy": "hardhat deploy-full",
"deploy:localhost": "pnpm run deploy"
},
"dependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.6",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@openzeppelin/contracts": "^5.0.2",
"circomlibjs": "^0.1.7",
"ethers": "^6.13.2",
"hardhat": "^2.22.8",
"lowdb": "^1.0.0",
"maci-contracts": "0.0.0-ci.b8d42a3",
"maci-core": "^2.0.0",
"maci-domainobjs": "^2.0.0",
"maci-contracts": "^2.2.1",
"maci-core": "^2.2.0",
"maci-domainobjs": "^2.2.0",
"solidity-docgen": "^0.6.0-beta.36"
},
"devDependencies": {
"@types/chai": "^4.3.11",
"@types/circomlibjs": "^0.1.6",
"@types/lowdb": "^1.0.15",
"@types/mocha": "^10.0.7",
"@types/node": "^22.2.0",
Expand Down
13 changes: 12 additions & 1 deletion packages/contracts/scripts/compileSol.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { poseidonContract } from "circomlibjs";
import hre from "hardhat";
import { buildPoseidonT3, buildPoseidonT4, buildPoseidonT5, buildPoseidonT6 } from "maci-contracts";

import fs from "fs";
import path from "path";
Expand All @@ -10,6 +10,17 @@ const PATHS = [
path.resolve(__dirname, "..", "typechain-types"),
];

type ExtendedHre = typeof hre & { overwriteArtifact: (name: string, code: unknown) => Promise<void> };

const buildPoseidon = async (numInputs: number) => {
await (hre as ExtendedHre).overwriteArtifact(`PoseidonT${numInputs + 1}`, poseidonContract.createCode(numInputs));
};

const buildPoseidonT3 = (): Promise<void> => buildPoseidon(2);
const buildPoseidonT4 = (): Promise<void> => buildPoseidon(3);
const buildPoseidonT5 = (): Promise<void> => buildPoseidon(4);
const buildPoseidonT6 = (): Promise<void> => buildPoseidon(5);

async function main(): Promise<void> {
await Promise.all(PATHS.map((filepath) => fs.existsSync(filepath) && fs.promises.rm(filepath, { recursive: true })));

Expand Down
21 changes: 21 additions & 0 deletions packages/contracts/tasks/deploy/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import fs from "fs";
import path from "path";

/**
* The same as individual imports but doesn't require to add new import line every time
*/
["maci"].forEach((folder) => {
const tasksPath = path.resolve(__dirname, folder);

if (fs.existsSync(tasksPath)) {
fs.readdirSync(tasksPath)
.filter(
(p) =>
(p.endsWith(".ts") && !p.endsWith("index.ts") && !p.endsWith("d.ts")) ||
(p.endsWith(".js") && !p.endsWith("index.js")),
)
.forEach((task) => {
import(`${tasksPath}/${task}`);
});
}
});
51 changes: 51 additions & 0 deletions packages/contracts/tasks/deploy/maci/05-pollFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ContractStorage, Deployment, EContracts, type IDeployParams } from "maci-contracts";

import { EDeploySteps } from "../../helpers/constants";

const deployment = Deployment.getInstance();
const storage = ContractStorage.getInstance();

/**
* Deploy step registration and task itself
*/
deployment.deployTask(EDeploySteps.PollFactory, "Deploy poll factory").then((task) =>
task.setAction(async ({ incremental }: IDeployParams, hre) => {
deployment.setHre(hre);
const deployer = await deployment.getDeployer();

const pollFactoryContractAddress = storage.getAddress(EContracts.PollFactory, hre.network.name);

if (incremental && pollFactoryContractAddress) {
return;
}

const poseidonT3ContractAddress = storage.mustGetAddress(EContracts.PoseidonT3, hre.network.name);
const poseidonT4ContractAddress = storage.mustGetAddress(EContracts.PoseidonT4, hre.network.name);
const poseidonT5ContractAddress = storage.mustGetAddress(EContracts.PoseidonT5, hre.network.name);
const poseidonT6ContractAddress = storage.mustGetAddress(EContracts.PoseidonT6, hre.network.name);

const linkedPollFactoryContract = await hre.ethers.getContractFactory(
"contracts/maci/PollFactory.sol:PollFactory",
{
signer: deployer,
libraries: {
PoseidonT3: poseidonT3ContractAddress,
PoseidonT4: poseidonT4ContractAddress,
PoseidonT5: poseidonT5ContractAddress,
PoseidonT6: poseidonT6ContractAddress,
},
},
);

const pollFactoryContract = await deployment.deployContractWithLinkedLibraries({
contractFactory: linkedPollFactoryContract,
});

await storage.register({
id: EContracts.PollFactory,
contract: pollFactoryContract,
args: [],
network: hre.network.name,
});
}),
);
138 changes: 138 additions & 0 deletions packages/contracts/tasks/deploy/maci/08-maci.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {
ContractStorage,
Deployment,
EContracts,
genEmptyBallotRoots,
type IDeployParams,
type GitcoinPassportGatekeeper,
type EASGatekeeper,
type ZupassGatekeeper,
type SemaphoreGatekeeper,
type HatsGatekeeperBase,
} from "maci-contracts";

import { MACI } from "../../../typechain-types";
import { EDeploySteps } from "../../helpers/constants";

const deployment = Deployment.getInstance();
const storage = ContractStorage.getInstance();

const DEFAULT_STATE_TREE_DEPTH = 10;

/**
* Deploy step registration and task itself
*/
deployment.deployTask(EDeploySteps.Maci, "Deploy MACI contract").then((task) =>
task.setAction(async ({ incremental }: IDeployParams, hre) => {
deployment.setHre(hre);
const deployer = await deployment.getDeployer();

const maciContractAddress = storage.getAddress(EContracts.MACI, hre.network.name);

if (incremental && maciContractAddress) {
return;
}

const poseidonT3ContractAddress = storage.mustGetAddress(EContracts.PoseidonT3, hre.network.name);
const poseidonT4ContractAddress = storage.mustGetAddress(EContracts.PoseidonT4, hre.network.name);
const poseidonT5ContractAddress = storage.mustGetAddress(EContracts.PoseidonT5, hre.network.name);
const poseidonT6ContractAddress = storage.mustGetAddress(EContracts.PoseidonT6, hre.network.name);

const maciContractFactory = await hre.ethers.getContractFactory("contracts/maci/MACI.sol:MACI", {
signer: deployer,
libraries: {
PoseidonT3: poseidonT3ContractAddress,
PoseidonT4: poseidonT4ContractAddress,
PoseidonT5: poseidonT5ContractAddress,
PoseidonT6: poseidonT6ContractAddress,
},
});

const constantInitialVoiceCreditProxyContractAddress = storage.mustGetAddress(
EContracts.ConstantInitialVoiceCreditProxy,
hre.network.name,
);
const gatekeeper =
deployment.getDeployConfigField<EContracts | null>(EContracts.MACI, "gatekeeper") ||
EContracts.FreeForAllGatekeeper;
const gatekeeperContractAddress = storage.mustGetAddress(gatekeeper, hre.network.name);
const pollFactoryContractAddress = storage.mustGetAddress(EContracts.PollFactory, hre.network.name);
const messageProcessorFactoryContractAddress = storage.mustGetAddress(
EContracts.MessageProcessorFactory,
hre.network.name,
);
const tallyFactoryContractAddress = storage.mustGetAddress(EContracts.TallyFactory, hre.network.name);

const stateTreeDepth =
deployment.getDeployConfigField<number | null>(EContracts.MACI, "stateTreeDepth") ?? DEFAULT_STATE_TREE_DEPTH;

const emptyBallotRoots = genEmptyBallotRoots(stateTreeDepth);

const maciContract = await deployment.deployContractWithLinkedLibraries<MACI>(
{ contractFactory: maciContractFactory },
pollFactoryContractAddress,
messageProcessorFactoryContractAddress,
tallyFactoryContractAddress,
gatekeeperContractAddress,
constantInitialVoiceCreditProxyContractAddress,
stateTreeDepth,
emptyBallotRoots,
);

if (gatekeeper === EContracts.EASGatekeeper) {
const gatekeeperContract = await deployment.getContract<EASGatekeeper>({
name: EContracts.EASGatekeeper,
address: gatekeeperContractAddress,
});
const maciInstanceAddress = await maciContract.getAddress();

await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
} else if (gatekeeper === EContracts.GitcoinPassportGatekeeper) {
const gatekeeperContract = await deployment.getContract<GitcoinPassportGatekeeper>({
name: EContracts.GitcoinPassportGatekeeper,
address: gatekeeperContractAddress,
});
const maciInstanceAddress = await maciContract.getAddress();

await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
} else if (gatekeeper === EContracts.ZupassGatekeeper) {
const gatekeeperContract = await deployment.getContract<ZupassGatekeeper>({
name: EContracts.ZupassGatekeeper,
address: gatekeeperContractAddress,
});
const maciInstanceAddress = await maciContract.getAddress();
await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
} else if (gatekeeper === EContracts.SemaphoreGatekeeper) {
const gatekeeperContract = await deployment.getContract<SemaphoreGatekeeper>({
name: EContracts.SemaphoreGatekeeper,
address: gatekeeperContractAddress,
});

const maciInstanceAddress = await maciContract.getAddress();
await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
} else if (gatekeeper === EContracts.HatsGatekeeper) {
const gatekeeperContract = await deployment.getContract<HatsGatekeeperBase>({
name: EContracts.HatsGatekeeper,
address: gatekeeperContractAddress,
});

const maciInstanceAddress = await maciContract.getAddress();
await gatekeeperContract.setMaciInstance(maciInstanceAddress).then((tx) => tx.wait());
}

await storage.register({
id: EContracts.MACI,
contract: maciContract,
args: [
pollFactoryContractAddress,
messageProcessorFactoryContractAddress,
tallyFactoryContractAddress,
gatekeeperContractAddress,
constantInitialVoiceCreditProxyContractAddress,
stateTreeDepth,
emptyBallotRoots.map((root) => root.toString()),
],
network: hre.network.name,
});
}),
);
17 changes: 17 additions & 0 deletions packages/contracts/tasks/helpers/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
import { EDeploySteps as EMaciDeploySteps } from "maci-contracts";

/**
* Deploy steps for maci-platform related constacts
*/
export enum EPlatformDeployStep {
Registry = "full:deploy-registry",
}

/**
* Deploy steps for maci and maci-platform
*/
export const EDeploySteps = {
...EMaciDeploySteps,
...EPlatformDeployStep,
};

/**
* Supported networks for deployment and task running
*/
Expand Down
Loading

0 comments on commit 11b9ee7

Please sign in to comment.