-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add governance proposal simulation tests on polygon mainnet fork
- Loading branch information
Showing
7 changed files
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 | ||
RINKEBY_URL=https://eth-rinkeby.alchemyapi.io/v2/<YOUR ALCHEMY KEY> | ||
POLYGON_URL=https://polygon-rpc.com | ||
PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"deployer": "0x3d67109E0200abD4D39Cb38377C9f81573f9F191", | ||
"token": "0x6847D3A4c80a82e1fb26f1fC6F09F3Ad5BEB5222", | ||
"tokenLock": "0xB17828789280C77C17B02fc8E6F20Ddc5721f2C2", | ||
"timelock": "0xdFB26381aFBc37f0Fae4A77D385b91B90347aA12", | ||
"governor": "0xc6eaDcC36aFcf1C430962506ad79145aD5140E58" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import chai, {expect} from 'chai'; | ||
import {waffle} from 'hardhat'; | ||
import {ZERO} from './shared/Constants'; | ||
import {getPolygonContracts} from './shared/Forking'; | ||
import {createAndExecuteProposal} from './shared/Governance'; | ||
|
||
const {solidity} = waffle; | ||
chai.use(solidity); | ||
|
||
// can simulate poylgon mainnet governance proposals here, enable fork object in hardhat.config.ts | ||
describe.skip('Governance - Polygon mainnet proposal simulations', async () => { | ||
const [user] = waffle.provider.getWallets(); | ||
const deployment = getPolygonContracts(user); | ||
const {arenaToken, timeLock} = deployment; | ||
|
||
it('should allow governance to move tokens in timeLock contract', async () => { | ||
const treasuryAmount = await arenaToken.balanceOf(timeLock.address); | ||
expect(treasuryAmount).to.be.gt(ZERO, `Treasury currently does not have any ARENA tokens`); | ||
|
||
let targets: string[] = [arenaToken.address]; | ||
let values: string[] = [`0`]; | ||
let calldatas: string[] = [arenaToken.interface.encodeFunctionData('transfer', [user.address, treasuryAmount])]; | ||
await createAndExecuteProposal({targets, values, calldatas, user, ...deployment}); | ||
|
||
expect(await arenaToken.balanceOf(timeLock.address)).to.eq(ZERO); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import {Signer} from 'ethers'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import { | ||
ArenaGovernor, | ||
ArenaGovernor__factory, | ||
ArenaToken, | ||
ArenaToken__factory, | ||
TimelockController, | ||
TimelockController__factory, | ||
TokenLock, | ||
TokenLock__factory, | ||
} from '../../typechain'; | ||
|
||
export type DeployedContracts = { | ||
governor: ArenaGovernor; | ||
timeLock: TimelockController; | ||
tokenLock: TokenLock; | ||
arenaToken: ArenaToken; | ||
}; | ||
export const getPolygonContracts = (signer: Signer): DeployedContracts => { | ||
const deploymentFilePath = path.join(`deployments`, `polygonAddresses.json`); | ||
if (!fs.existsSync(deploymentFilePath)) throw new Error(`File '${path.resolve(deploymentFilePath)}' does not exist.`); | ||
|
||
const contents = fs.readFileSync(deploymentFilePath, `utf8`); | ||
let governorAddress; | ||
let arenaAddress; | ||
let timelockAddress; | ||
let tokenLockAddress; | ||
try { | ||
({ | ||
governor: governorAddress, | ||
token: arenaAddress, | ||
tokenLock: tokenLockAddress, | ||
timelock: timelockAddress, | ||
} = JSON.parse(contents)); | ||
} catch (error) { | ||
throw new Error(`Cannot parse deployment config at '${path.resolve(deploymentFilePath)}'.`); | ||
} | ||
if (!governorAddress) throw new Error(`Deployment file did not include governor address '${deploymentFilePath}'.`); | ||
if (!arenaAddress) throw new Error(`Deployment file did not include arena token address '${deploymentFilePath}'.`); | ||
if (!timelockAddress) throw new Error(`Deployment file did not include timelock address '${deploymentFilePath}'.`); | ||
if (!tokenLockAddress) throw new Error(`Deployment file did not include tokenLock address '${deploymentFilePath}'.`); | ||
|
||
return { | ||
governor: ArenaGovernor__factory.connect(governorAddress, signer), | ||
arenaToken: ArenaToken__factory.connect(arenaAddress, signer), | ||
timeLock: TimelockController__factory.connect(timelockAddress, signer), | ||
tokenLock: TokenLock__factory.connect(tokenLockAddress, signer), | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import {Signer} from 'ethers'; | ||
import {ethers} from 'hardhat'; | ||
import {impersonateAccountWithFunds, stopImpersonateAccount} from '../shared/AccountManipulation'; | ||
import {increaseNextBlockTime, setNextBlockNumber} from '../shared/TimeManipulation'; | ||
import {DeployedContracts} from './Forking'; | ||
|
||
export const createAndExecuteProposal = async ({ | ||
governor, | ||
timeLock, | ||
arenaToken, | ||
user, | ||
targets, | ||
values, | ||
calldatas, | ||
}: { | ||
user: Signer; | ||
targets: string[]; | ||
values: string[]; | ||
calldatas: string[]; | ||
} & DeployedContracts) => { | ||
// we need address with min. proposalThreshold tokens to propose | ||
// 1. borrow some treasury tokens to user | ||
const quorumAmount = await governor.quorumVotes(); | ||
// careful, this sends ETH to timelock which might break real-world simulation for proposals involving Timelock ETH | ||
const timeLockSigner = await impersonateAccountWithFunds(timeLock.address); | ||
await arenaToken.connect(timeLockSigner).transfer(await user.getAddress(), quorumAmount); | ||
// set voting delay & duration to 2 blocks, otherwise need to simulate 302,400 blocks | ||
await governor.connect(timeLockSigner).setVotingDelay(`2`); | ||
await governor.connect(timeLockSigner).setVotingPeriod(`2`); | ||
await stopImpersonateAccount(timeLock.address); | ||
|
||
await arenaToken.connect(user).delegate(await user.getAddress()); | ||
const descriptionHash = ethers.utils.keccak256([]); // keccak(``) | ||
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) | ||
const proposalId: string = events![0].args![0].toString(); | ||
|
||
// 2. vote on proposal | ||
const voteStartBlock = await governor.proposalSnapshot(proposalId); | ||
await setNextBlockNumber(voteStartBlock.toNumber() + 1); // is a blocknumber which fits in Number | ||
tx = await governor.connect(user)['castVote'](proposalId, `1`); | ||
|
||
// 3. return borrowed tokens | ||
tx = await arenaToken.connect(user).transfer(timeLock.address, quorumAmount); | ||
|
||
// 4. advance time past voting period and queue proposal calls to Timelock via GovernorTimelockControl.queue | ||
const voteEndBlock = await governor.proposalDeadline(proposalId); | ||
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); | ||
await tx.wait(); | ||
|
||
// 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); | ||
|
||
return proposalId; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters