Skip to content

Commit

Permalink
Merge pull request #42 from code-423n4/feature/simProposal
Browse files Browse the repository at this point in the history
Add script for simulating an existing proposal
  • Loading branch information
HickupHH3 authored Mar 4, 2022
2 parents 17c2025 + 98a565e commit bae376b
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 23 deletions.
2 changes: 1 addition & 1 deletion scripts/deploy/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {BigNumber as BN, constants} from 'ethers';
import {ONE_18, ONE_DAY, ONE_YEAR} from '../../test/shared/Constants';
import {ONE_18, ONE_DAY, ONE_YEAR} from '../../shared/Constants';

type Config = {
FREE_SUPPLY: BN;
Expand Down
7 changes: 7 additions & 0 deletions scripts/proposals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ task('propose', 'propose transfer')
const {transferProposal} = await import('./transfer');
await transferProposal(json, hre);
});

task('simulateExistingProposal', 'simulates an existing proposal (by cloning it)')
.addParam('id', 'Proposal ID')
.setAction(async ({id}, hre) => {
const {simulateExistingProposal} = await import('./simulateExistingProposal');
await simulateExistingProposal(id, hre);
});
28 changes: 28 additions & 0 deletions scripts/proposals/simulateExistingProposal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {getPolygonContracts, getForkParams} from '../../shared/Forking';
import {createAndExecuteProposal} from '../../shared/Governance';

export async function simulateExistingProposal(proposalId: string, hre: HardhatRuntimeEnvironment) {
const [user] = await hre.ethers.getSigners();
const deployment = getPolygonContracts(user);

// attempt mainnet forking
await hre.network.provider.request({
method: 'hardhat_reset',
params: [getForkParams()],
});

const proposalActions = await deployment.governor.getActions(proposalId);
let valuesArray = proposalActions[1].map((value) => value.toString());
console.log(`proposal targets: ${proposalActions.targets}`);
console.log(`proposal values: ${valuesArray}`);
console.log(`proposal calldatas: ${proposalActions.calldatas}`);
console.log(`cloning proposal...`);
await createAndExecuteProposal({
user,
targets: proposalActions.targets,
values: valuesArray,
calldatas: proposalActions.calldatas,
...deployment,
});
}
File renamed without changes.
File renamed without changes.
16 changes: 15 additions & 1 deletion test/shared/Forking.ts → shared/Forking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
TimelockController__factory,
TokenLock,
TokenLock__factory,
} from '../../typechain';
} from '../typechain';

export type DeployedContracts = {
governor: ArenaGovernor;
Expand Down Expand Up @@ -49,3 +49,17 @@ export const getPolygonContracts = (signer: Signer): DeployedContracts => {
tokenLock: TokenLock__factory.connect(tokenLockAddress, signer),
};
};

export function getForkParams() {
if (process.env.POLYGON_URL == undefined) {
console.log(`Missing POLYGON_URL in .env`);
process.exit(1);
}
let forkParams: any = {
forking: {
jsonRpcUrl: process.env.POLYGON_URL
}
};
if (process.env.FORK_BLOCK) forkParams['forking']['blockNumber'] = Number(process.env.FORK_BLOCK);
return forkParams;
}
47 changes: 39 additions & 8 deletions test/shared/Governance.ts → shared/Governance.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {Signer} from 'ethers';
import {ethers} from 'hardhat';
import {impersonateAccountWithFunds, stopImpersonateAccount} from '../shared/AccountManipulation';
import {increaseNextBlockTime, setNextBlockNumber} from '../shared/TimeManipulation';
import {impersonateAccountWithFunds, stopImpersonateAccount} from './AccountManipulation';
import {increaseNextBlockTime, setNextBlockNumber} from './TimeManipulation';
import {POLYGON_AVERAGE_BLOCK_TIME} from './Constants';
import {DeployedContracts} from './Forking';
import {getABIFromPolygonscan} from './Polygonscan';

export const createAndExecuteProposal = async ({
governor,
Expand All @@ -23,15 +24,18 @@ export const createAndExecuteProposal = async ({
const timeLockSigner = await impersonateAccountWithFunds(timeLock.address);
let originalVotingDelay = await governor.votingDelay();
let originalVotingPeriod = await governor.votingPeriod();
console.log('setting voting delay and duration to 2 blocks...');
await governor.connect(timeLockSigner).setVotingDelay(`2`);
await governor.connect(timeLockSigner).setVotingPeriod(`2`);

// 1. borrow some treasury tokens to user as we need signer with min. proposalThreshold tokens to propose
const quorumAmount = await governor.quorumVotes();
// careful, this sends ETH to timelock which might break real-world simulation for proposals involving Timelock ETH
console.log('transferring tokens to user for proposal creation...');
await arenaToken.connect(timeLockSigner).transfer(await user.getAddress(), quorumAmount);
await arenaToken.connect(user).delegate(await user.getAddress());
const descriptionHash = ethers.utils.keccak256([]); // keccak(``)
console.log('creating proposal...');
let tx = await governor.connect(user)['propose(address[],uint256[],bytes[],string)'](targets, values, calldatas, ``);
let {events} = await tx.wait();
// get first event (ProposalCreated), then get first arg of that event (proposalId)
Expand All @@ -42,6 +46,7 @@ export const createAndExecuteProposal = async ({
// simulate elapsed time close to original voting delay
await increaseNextBlockTime(Math.floor(POLYGON_AVERAGE_BLOCK_TIME * originalVotingDelay.toNumber()));
await setNextBlockNumber(voteStartBlock.toNumber() + 1); // is a blocknumber which fits in Number
console.log('casting vote...');
tx = await governor.connect(user)['castVote'](proposalId, `1`);

// 3. return borrowed tokens
Expand All @@ -52,22 +57,48 @@ export const createAndExecuteProposal = async ({
// simulate elapsed time close to original voting delay
await increaseNextBlockTime(Math.floor(POLYGON_AVERAGE_BLOCK_TIME * originalVotingPeriod.toNumber()));
await setNextBlockNumber(voteEndBlock.toNumber() + 1); // is a blocknumber which fits in Number
tx = await governor
.connect(user)
['queue(address[],uint256[],bytes[],bytes32)'](targets, values, calldatas, descriptionHash);
console.log('queueing proposal...');
tx = await governor.connect(user)['queue(uint256)'](proposalId);
await tx.wait();

// can revert Governor changes now
console.log('resetting voting delay and period...');
await governor.connect(timeLockSigner).setVotingDelay(originalVotingDelay);
await governor.connect(timeLockSigner).setVotingPeriod(originalVotingPeriod);
await stopImpersonateAccount(timeLock.address);

// 5. advance time past timelock delay and then execute
const timeLockMinDelaySeconds = await timeLock.getMinDelay();
await increaseNextBlockTime(timeLockMinDelaySeconds.toNumber());
await governor
.connect(user)
['execute(address[],uint256[],bytes[],bytes32)'](targets, values, calldatas, descriptionHash);
console.log('executing proposal...');
tx = await governor.connect(user)['execute(uint256)'](proposalId);

let result = await tx.wait(1);

for (let i = 0; i < targets.length; i++) {
let abi = await getABIFromPolygonscan(targets[i]);
let iface = new ethers.utils.Interface(abi);
let events = result.logs.map((log) => {
try {
return iface.parseLog(log);
} catch (e) {
// no matching event
}
});
console.log(`### TARGET ${targets[i]} EVENTS ###`);
console.log(events);
console.log(`###################################`);
}

let timelockEvents = result.logs.map((log) => {
try {
return timeLock.interface.parseLog(log);
} catch (e) {
// no matching event
}
});
console.log(`### TIMELOCK EVENTS ###`);
console.log(timelockEvents);

return proposalId;
};
20 changes: 20 additions & 0 deletions shared/Polygonscan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import fetch from 'node-fetch';

export async function getABIFromPolygonscan(address: string) {
if (process.env.POLYGONSCAN_API_KEY == undefined) {
console.log('Require polygonscan key, exiting...');
process.exit(1);
}

let abiRequest = await fetch(
`https://api.polygonscan.com/api?module=contract&action=getabi` +
`&address=${address}` +
`&apikey=${process.env.POLYGONSCAN_API_KEY}`
);
let abi = await abiRequest.json();
if (abi.status == '0') {
console.log(abi.result);
process.exit(1);
}
return abi.result;
}
File renamed without changes.
6 changes: 3 additions & 3 deletions test/ArenaToken.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import * as fs from 'fs';
import {ethers, waffle} from 'hardhat';
import * as path from 'path';
import {ArenaToken, RevokableTokenLock} from '../typechain';
import {impersonateAccountWithFunds, stopImpersonateAccount} from './shared/AccountManipulation';
import {ONE_18, ONE_DAY, ONE_YEAR} from './shared/Constants';
import {resetNetwork, setNextBlockTimeStamp} from './shared/TimeManipulation';
import {impersonateAccountWithFunds, stopImpersonateAccount} from '../shared/AccountManipulation';
import {ONE_18, ONE_DAY, ONE_YEAR} from '../shared/Constants';
import {resetNetwork, setNextBlockTimeStamp} from '../shared/TimeManipulation';
import {MerkleDistributorInfo} from '../src/parse-balance-map';

const {solidity, loadFixture} = waffle;
Expand Down
6 changes: 3 additions & 3 deletions test/GovernanceSim.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import chai, {expect} from 'chai';
import {waffle} from 'hardhat';
import {ZERO} from './shared/Constants';
import {getPolygonContracts} from './shared/Forking';
import {createAndExecuteProposal} from './shared/Governance';
import {ZERO} from '../shared/Constants';
import {getPolygonContracts} from '../shared/Forking';
import {createAndExecuteProposal} from '../shared/Governance';

const {solidity} = waffle;
chai.use(solidity);
Expand Down
4 changes: 2 additions & 2 deletions test/RevokableTokenLock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {expect} from 'chai';
import {BigNumber} from 'ethers';
import {ethers, waffle} from 'hardhat';
import {IERC20, RevokableTokenLock} from '../typechain';
import {ZERO_ADDRESS, ONE_HOUR} from './shared/Constants';
import {setNextBlockTimeStamp, mineBlockAt} from './shared/TimeManipulation';
import {ZERO_ADDRESS, ONE_HOUR} from '../shared/Constants';
import {setNextBlockTimeStamp, mineBlockAt} from '../shared/TimeManipulation';

const {loadFixture} = waffle;

Expand Down
4 changes: 2 additions & 2 deletions test/TokenLock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {expect} from 'chai';
import chai from 'chai';
import hre, {ethers, waffle} from 'hardhat';
import {IERC20, TokenLock} from '../typechain';
import {MAX_UINT, ONE, ONE_DAY, ONE_YEAR, ONE_18, ZERO, ZERO_ADDRESS} from './shared/Constants';
import {mineBlockAt, resetNetwork, setNextBlockTimeStamp} from './shared/TimeManipulation';
import {MAX_UINT, ONE, ONE_DAY, ONE_YEAR, ONE_18, ZERO, ZERO_ADDRESS} from '../shared/Constants';
import {mineBlockAt, resetNetwork, setNextBlockTimeStamp} from '../shared/TimeManipulation';

const {solidity, loadFixture} = waffle;
chai.use(solidity);
Expand Down
4 changes: 2 additions & 2 deletions test/TokenSale.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import chai from 'chai';
import {ethers, waffle} from 'hardhat';
import {BigNumber as BN} from 'ethers';
import {IERC20, RevokableTokenLock, TokenSale} from '../typechain';
import {ONE_DAY, ONE_18, MAX_UINT, ONE_YEAR} from './shared/Constants';
import {setNextBlockTimeStamp, resetNetwork} from './shared/TimeManipulation';
import {ONE_DAY, ONE_18, MAX_UINT, ONE_YEAR} from '../shared/Constants';
import {setNextBlockTimeStamp, resetNetwork} from '../shared/TimeManipulation';

const {solidity, loadFixture} = waffle;
chai.use(solidity);
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"outDir": "dist",
"declaration": true
},
"include": ["./scripts", "./test", "./typechain"],
"include": ["./scripts", "./test", "./typechain", "shared"],
"files": ["./hardhat.config.ts"]
}

0 comments on commit bae376b

Please sign in to comment.