Skip to content

Commit

Permalink
chore(contract): add contract verification task
Browse files Browse the repository at this point in the history
- [x] Update contract storage with already verified contracts
- [x] Fix deployment for poseidon contracts
- [x] Add contract verifier
  • Loading branch information
0xmad committed Jan 29, 2024
1 parent 6ebb3c0 commit f78b86c
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 13 deletions.
2 changes: 2 additions & 0 deletions contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { HardhatUserConfig } from "hardhat/config";
import "./tasks/deploy";
import { EChainId, ESupportedChains, NETWORKS_DEFAULT_GAS, getNetworkRpcUrls } from "./tasks/helpers/constants";
import "./tasks/runner/deployFull";
import "./tasks/runner/verifyFull";

dotenv.config();

Expand All @@ -23,6 +24,7 @@ const getCommonNetworkConfig = (networkName: ESupportedChains, chainId: number,
blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
gasMultiplier: DEFAULT_GAS_MUL,
gasPrice: NETWORKS_DEFAULT_GAS[networkName],
saveDeployments: true,
chainId,
accounts: {
mnemonic: mnemonic || process.env.MNEMONIC || TEST_MNEMONIC,
Expand Down
4 changes: 3 additions & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@
"test:subsidy": "hardhat test ./tests/Subsidy.test.ts",
"test:eas_gatekeeper": "hardhat test ./tests/EASGatekeeper.test.ts",
"deploy": "hardhat deploy-full",
"verify": "hardhat verify-full",
"deploy:hardhat": "pnpm run deploy",
"deploy:sepolia": "pnpm run deploy --network sepolia"
"deploy:sepolia": "pnpm run deploy --network sepolia",
"verify:sepolia": "pnpm run verify --network sepolia"
},
"dependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.5",
Expand Down
10 changes: 4 additions & 6 deletions contracts/tasks/deploy/maci/05-poseidon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ deployment
return;
}

const [PoseidonT3Contract, PoseidonT4Contract, PoseidonT5Contract, PoseidonT6Contract] = await Promise.all([
deployment.deployContract(EContracts.PoseidonT3, deployer),
deployment.deployContract(EContracts.PoseidonT4, deployer),
deployment.deployContract(EContracts.PoseidonT5, deployer),
deployment.deployContract(EContracts.PoseidonT6, deployer),
]);
const PoseidonT3Contract = await deployment.deployContract(EContracts.PoseidonT3, deployer);
const PoseidonT4Contract = await deployment.deployContract(EContracts.PoseidonT4, deployer);
const PoseidonT5Contract = await deployment.deployContract(EContracts.PoseidonT5, deployer);
const PoseidonT6Contract = await deployment.deployContract(EContracts.PoseidonT6, deployer);

await Promise.all([
storage.register({
Expand Down
21 changes: 20 additions & 1 deletion contracts/tasks/helpers/ContractStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import type { EContracts, IRegisterContract, IStorageInstanceEntry, IStorageName

type TStorage = Record<
string,
Partial<{ named: Record<string, IStorageNamedEntry>; instance: Record<string, IStorageInstanceEntry> }>
Partial<{
named: Record<string, IStorageNamedEntry>;
instance: Record<string, IStorageInstanceEntry>;
verified: Record<string, boolean>;
}>
>;

export class ContractStorage {
Expand Down Expand Up @@ -68,6 +72,21 @@ export class ContractStorage {
.write();
}

getInstances(network: string): [string, IStorageInstanceEntry][] {
const collection = this.db.get(`${network}.instance`);
const value = collection.value() as IStorageInstanceEntry[] | undefined;

return Object.entries<IStorageInstanceEntry>(value || []);
}

getVerified(address: string, network: string): boolean {
return this.db.get(`${network}.verified.${address}`).value() as unknown as boolean;
}

setVerified = (address: string, network: string, verified: boolean): void => {
this.db.set(`${network}.verified.${address}`, verified).write();
};

getAddress(id: EContracts, network: string): string | undefined {
const collection = this.db.get(`${network}.named.${id}`);
const namedEntry = collection.value() as IStorageNamedEntry | undefined;
Expand Down
45 changes: 45 additions & 0 deletions contracts/tasks/helpers/ContractVerifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { IVerificationSubtaskArgs } from "./types";
import type { HardhatRuntimeEnvironment, Libraries } from "hardhat/types";

export class ContractVerifier {
private hre: HardhatRuntimeEnvironment;

constructor(hre: HardhatRuntimeEnvironment) {
this.hre = hre;
}

async verify(address: string, constructorArguments: string, libraries?: string): Promise<[boolean, string]> {
const params: IVerificationSubtaskArgs = {
address,
constructorArguments: JSON.parse(constructorArguments) as unknown[],
};

if (libraries) {
params.libraries = JSON.parse(libraries) as Libraries;
}

// Run etherscan task
const error = await this.hre
.run("verify:verify", params)
.then(() => "")
.catch((err: Error) => {
if (err.message === "Contract source code already verified") {
return "";
}

return err.message;
});

return [!error, error];
}

async verifyProxy(proxyAddress: string): Promise<[boolean, string]> {
try {
await this.verify(proxyAddress, JSON.stringify([]));
} catch (error) {
return [false, (error as Error).message];
}

return [true, ""];
}
}
38 changes: 37 additions & 1 deletion contracts/tasks/helpers/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { BaseContract } from "ethers";
import type { TaskArguments } from "hardhat/types";
import type { Libraries, TaskArguments } from "hardhat/types";

/**
* Interface that represents deploy params
Expand Down Expand Up @@ -162,3 +162,39 @@ export enum EContracts {
PoseidonT5 = "PoseidonT5",
PoseidonT6 = "PoseidonT6",
}

/**
* Interface that represents verify arguments
*/
export interface IVerifyFullArgs {
/**
* Ignore verified status
*/
force?: boolean;
}

/**
* Interface that represents verification subtaks arguments
* This is extracted from hardhat etherscan plugin
*/
export interface IVerificationSubtaskArgs {
/**
* Contract address
*/
address: string;

/**
* Constructor arguments
*/
constructorArguments: unknown[];

/**
* Fully qualified name of the contract
*/
contract?: string;

/**
* Libraries
*/
libraries?: Libraries;
}
8 changes: 4 additions & 4 deletions contracts/tasks/runner/deployFull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ task("deploy-full", "Deploy environment")

console.log("\nDeployment has finished");

// if (verify) {
// console.log("Verify all contracts");
// await hre.run("verify:verify-all-contracts");
// }
if (verify) {
console.log("Verify all contracts");
await hre.run("verify-full");
}
});
75 changes: 75 additions & 0 deletions contracts/tasks/runner/verifyFull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable no-console */
import { task } from "hardhat/config";

import type { IStorageInstanceEntry, IVerifyFullArgs } from "../helpers/types";

import { ContractStorage } from "../helpers/ContractStorage";
import { ContractVerifier } from "../helpers/ContractVerifier";

task("verify-full", "Verify contracts listed in storage")
.addFlag("force", "Ignore verified status")
.setAction(async ({ force = false }: IVerifyFullArgs, hre) => {
const storage = ContractStorage.getInstance();
const verifier = new ContractVerifier(hre);
const addressList: string[] = [];
const entryList: IStorageInstanceEntry[] = [];
let index = 0;

const addEntry = (address: string, entry: IStorageInstanceEntry) => {
if (!entry.verify) {
return;
}

addressList.push(address);
entryList.push(entry);
index += 1;
};

const instances = storage.getInstances(hre.network.name);

instances.forEach(([key, entry]) => {
if (entry.id.includes("Poseidon")) {
return;
}

addEntry(key, entry);
});

console.log("======================================================================");
console.log("======================================================================");
console.log(`Verification batch with ${addressList.length} entries of ${index} total.`);
console.log("======================================================================");

const summary: string[] = [];
for (let i = 0; i < addressList.length; i += 1) {
const address = addressList[i];
const entry = entryList[i];

const params = entry.verify;

console.log("\n======================================================================");
console.log(`[${i}/${addressList.length}] Verify contract: ${entry.id} ${address}`);
console.log("\tArgs:", params?.args);

const verifiedEntity = storage.getVerified(address, hre.network.name);

if (!force && verifiedEntity) {
console.log("Already verified");
} else {
// eslint-disable-next-line no-await-in-loop
const [ok, err] = await verifier.verify(address, params?.args ?? "");

if (ok) {
storage.setVerified(address, hre.network.name, true);
} else {
summary.push(`${address} ${entry.id}: ${err}`);
}
}
}

console.log("\n======================================================================");
console.log(`Verification batch has finished with ${summary.length} issue(s).`);
console.log("======================================================================");
console.log(summary.join("\n"));
console.log("======================================================================");
});

0 comments on commit f78b86c

Please sign in to comment.