Skip to content

Commit

Permalink
Merge branch 'pzelazko/deployed-contract-checkers' into 'develop'
Browse files Browse the repository at this point in the history
OCT-794 Add deployed contract simple checkers

See merge request wildland/governance/octant!562
  • Loading branch information
Piotr Żelazko committed Aug 28, 2023
2 parents 3398188 + 23cad21 commit bdcd47f
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 1 deletion.
2 changes: 1 addition & 1 deletion contracts-v1/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const IS_GAS_REPORTING_ENABLED = Boolean(process.env.IS_GAS_REPORTING_ENABLED) |
// CONTRACTS ADDRESSES
// ----------------
const GNT_ADDRESS = process.env.GNT_ADDRESS || '0xE6de13D64F6036E4E3f5fC84B5EB620C5C7c1050';
const GLM_ADDRESS = process.env.GLM_ADDRESS || '0x71432DD1ae7DB41706ee6a22148446087BdD0906';
const GLM_ADDRESS = process.env.GLM_ADDRESS || '0x7DD9c5Cba05E151C895FDe1CF355C9A1D5DA6429';
const GLM_FAUCET_ADDRESS =
process.env.GLM_FAUCET_ADDRESS || '0xD380d54df4993FC2Cae84F3ADB77fB97694933A8';

Expand Down
4 changes: 4 additions & 0 deletions contracts-v1/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ import './tasks/mine';
import './tasks/prepare-local-test-env';
import './tasks/send-glm';
import './tasks/target-check';
import './tasks/auth-check';
import './tasks/deposits-check';
import './tasks/proposals-check';
import './tasks/target-deploy';
import './tasks/target-upgrade';
import './tasks/target-withdraw';
import './tasks/status';
import './tasks/verify-deployment';

const config: HardhatUserConfig = {
docgen: {
Expand Down
61 changes: 61 additions & 0 deletions contracts-v1/tasks/auth-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { subtask, task } from 'hardhat/config';
import { exit } from 'process';

import { verify } from './verification/verifiable';

/* eslint no-console: 0 */
import { AUTH, ZERO_ADDRESS } from '../helpers/constants';
import { Auth } from '../typechain';

task('auth-check', 'Check auth at particular addres')
.addParam('address', 'Auth contract address')
.addParam('contractName', 'Name of the contract', AUTH)
.addFlag('verify', 'Verifies contract storage against expected values')
.addOptionalParam('multisig', 'Expected multisig address')
.addOptionalParam('pendingowner', 'Expected pending owner', ZERO_ADDRESS)
.setAction(async (taskArgs, { ethers, getNamedAccounts, run }) => {
console.log('Querying contract deployed at:', taskArgs.address);
const { deployer } = await getNamedAccounts();
const target: Auth = await ethers.getContractAt(
taskArgs.contractName,
taskArgs.address,
deployer,
);

const multisig = await target.multisig();
const pendingOwner = await target.pendingOwner();

console.log('multisig: ', multisig);
console.log('pendingOwner: ', pendingOwner);

if (taskArgs.verify) {
const result = await run('auth-check:verify');
exit(result);
}
});

subtask('auth-check:verify', 'Verify auth contract')
.addParam('address', 'Auth contract address')
.addParam('multisig', 'Expected multisig address')
.addParam('pendingowner', 'Expected pending owner', ZERO_ADDRESS)
.setAction(async (taskArgs, hre) => {
console.log('Veryfing Auth contract');

const res = await verify(
{
address: taskArgs.address,
contractName: AUTH,
properties: [
['multisig', taskArgs.multisig],
['pendingOwner', taskArgs.pendingowner],
],
},
hre,
);

if (res === 0) {
console.log('Auth contract successfully verified! 👍');
}

return res;
});
62 changes: 62 additions & 0 deletions contracts-v1/tasks/deposits-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { subtask, task } from 'hardhat/config';
import { exit } from 'process';

/* eslint no-console: 0 */
import { verify } from './verification/verifiable';

import { GLM_ADDRESS } from '../env';
import { DEPOSITS } from '../helpers/constants';
import { Deposits } from '../typechain';

task('deposits-check', 'Check deposits at particular addres')
.addParam('address', 'Deposits contract address')
.addParam('contractName', 'Name of the contract', DEPOSITS)
.addFlag('verify', 'Verifies contract storage against expected values')
.addOptionalParam('auth', 'Expected Auth contract address')
.addOptionalParam('glm', 'Expected GLM contract address')
.setAction(async (taskArgs, { ethers, getNamedAccounts, run }) => {
console.log('Querying contract deployed at:', taskArgs.address);
const { deployer } = await getNamedAccounts();
const target: Deposits = await ethers.getContractAt(
taskArgs.contractName,
taskArgs.address,
deployer,
);

const auth = await target.auth();
const glm = await target.glm();

console.log('auth: ', auth);
console.log('glm: ', glm);

if (taskArgs.verify) {
const result = await run('deposits-check:verify', taskArgs);
exit(result);
}
});

subtask('deposits-check:verify', 'Verify deposits contract')
.addParam('address', 'Deposits contract address')
.addParam('auth', 'Expected Auth contract address')
.addParam('glm', 'Expected GLM contract address', GLM_ADDRESS)
.setAction(async (taskArgs, hre) => {
console.log('Veryfing Deposits contract');

const res = await verify(
{
address: taskArgs.address,
contractName: DEPOSITS,
properties: [
['auth', taskArgs.auth],
['glm', taskArgs.glm],
],
},
hre,
);

if (res === 0) {
console.log('Deposits contract successfully verified! 👍');
}

return res;
});
87 changes: 87 additions & 0 deletions contracts-v1/tasks/proposals-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* eslint no-console: 0 */
import { subtask, task, types } from 'hardhat/config';
import { exit } from 'process';

import { verify, arraysEqualPredicate } from './verification/verifiable';

import { PROPOSALS_CID } from '../env';
import { PROPOSALS, ZERO_ADDRESS } from '../helpers/constants';
import { Proposals } from '../typechain';

task('proposals-check', 'Check proposals at particular addres')
.addParam('address', 'Proposals contracts address')
.addParam('contractName', 'Name of the contract', PROPOSALS)
.addFlag('verify', 'Verifies contract storage against expected values')
.addOptionalParam('auth', 'Expected Auth contract address')
.addOptionalParam('cid', 'Expected cid', PROPOSALS_CID)
.addOptionalParam('epochs', 'Expected epochs contract address', ZERO_ADDRESS)
.addOptionalParam(
'epoch',
'Epoch for which to query for proposal addresses',
undefined,
types.int,
)
.addParam('proposals', 'Comma separated list of expected proposals')
.setAction(async (taskArgs, { ethers, getNamedAccounts, run }) => {
console.log('Querying contract deployed at:', taskArgs.address);
const { deployer } = await getNamedAccounts();
const target: Proposals = await ethers.getContractAt(
taskArgs.contractName,
taskArgs.address,
deployer,
);

const auth = await target.auth();
const cid = await target.cid();
const epochs = await target.epochs();

console.log('auth: ', auth);
console.log('cid: ', cid);
console.log('epochs: ', epochs);

if (taskArgs.epoch !== undefined) {
console.log(
`proposals at epoch ${taskArgs.epoch}: `,
await target.getProposalAddresses(taskArgs.epoch),
);
}

if (taskArgs.verify) {
const result = await run('proposals-check:verify', taskArgs);
exit(result);
}
});

subtask('proposals-check:verify', 'Verify Proposals contract')
.addParam('address', 'proposals contract address')
.addParam('auth', 'Expected Auth contract address')
.addParam('epochs', 'Expected Epochs contract address')
.addParam('cid', 'Expected cid')
.addParam('epoch', 'Epoch at which to query for proposals', 100, types.int)
.addParam('proposals', 'Comma separated list of expected proposals')
.setAction(async (taskArgs, hre) => {
console.log('Veryfing Proposals contract');

const res = await verify(
{
address: taskArgs.address,
contractName: PROPOSALS,
properties: [
['auth', taskArgs.auth],
['cid', taskArgs.cid],
['epochs', taskArgs.epochs],
[
['getProposalAddresses', [taskArgs.epoch]],
arraysEqualPredicate(taskArgs.proposals.split(',')),
],
],
},
hre,
);

if (res === 0) {
console.log('Proposals contract successfully verified! 👍');
}

return res;
});
96 changes: 96 additions & 0 deletions contracts-v1/tasks/verification/verifiable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* eslint no-console: 0 */
import { Contract } from 'ethers';
import { HardhatRuntimeEnvironment } from 'hardhat/types';

type PropertyPredicateT = (value: any) => boolean;
type VerifiablePropertyT = [string | [string, [any]], PropertyPredicateT | any];

interface Verifiable {
address: string;
contractName: string;
properties: Array<VerifiablePropertyT>;
}

async function getContract<ContractT extends Contract>(
verifiable: Verifiable,
hre: HardhatRuntimeEnvironment,
): Promise<ContractT> {
return hre.ethers.getContractAt(verifiable.contractName, verifiable.address);
}

function propertyOf<ContractT>(property: keyof ContractT) {
return property;
}

function defaultPredicate(expected: any) {
return (actual: any) => {
return actual === expected;
};
}

export function arraysEqualPredicate(expected: [any], shouldCheckOrder = false) {
return (a: [any]): boolean => {
const actual = [...a]; // make a copy

if (expected.length !== actual.length) return false;

if (!shouldCheckOrder) {
expected.sort();
actual.sort();
}

return expected.every((elem, index) => {
console.debug('Expected: %s, actual: %s', elem, actual[index]);
return elem === actual[index];
});
};
}

function predicateToStr(p: PropertyPredicateT | any): string {
if (p instanceof Function) {
return '<custom predicate>';
}

return p.toString();
}

/* eslint-disable no-console */
export async function verify<ContractT extends Contract>(
verifiable: Verifiable,
hre: HardhatRuntimeEnvironment,
): Promise<number> {
const contract: ContractT = await getContract(verifiable, hre);

let errors = 0;

for await (const [property, predicateOrVal] of verifiable.properties) {
let propertyFunc;
let propertyName: string;
if (typeof property === 'string') {
propertyName = property as string;
propertyFunc = async () => contract[propertyOf<ContractT>(propertyName)]();
} else {
let args: [any];
[propertyName, args] = property as [string, [any]];
propertyFunc = async () => contract[propertyOf<ContractT>(propertyName)](...args);
}

const propertyValue = await propertyFunc();
const predicate =
predicateOrVal instanceof Function ? predicateOrVal : defaultPredicate(predicateOrVal);

console.debug(
'Checking %s. Expected: %s. Value: %s',
propertyName,
predicateToStr(predicateOrVal),
propertyValue,
);

if (!predicate(propertyValue)) {
console.error('Verification failed for %s.', propertyName);
errors += 1;
}
}

return errors;
}
Loading

0 comments on commit bdcd47f

Please sign in to comment.