diff --git a/.env.example b/.env.example index 4a64149..3bd1b87 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ ETHERSCAN_KEY= -ALCHEMY_KEY= +RPC_URL= +PK= use_latest_block=false diff --git a/config.js b/config.js index c1eb5a5..65e461f 100644 --- a/config.js +++ b/config.js @@ -2,5 +2,4 @@ module.exports = { governance: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce', TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C', tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC', - forkBlockNumber: 14352372, } diff --git a/contracts/v4-patch/GovernancePatchUpgrade.sol b/contracts/v4-patch/GovernancePatchUpgrade.sol index 7f705b1..92ddbc0 100644 --- a/contracts/v4-patch/GovernancePatchUpgrade.sol +++ b/contracts/v4-patch/GovernancePatchUpgrade.sol @@ -35,8 +35,11 @@ contract GovernancePatchUpgrade is GovernanceStakingUpgrade { if (proposalCodehash == proposalCodehashes[proposalId]) { super.execute(proposalId); } else { + // Note that this is the easiest way to block further execution proposal.executed = true; - emit CodehashDifferent(proposal.target, proposalCodehashes[proposalId], proposalCodehash); + + // Let the event signify it was broken + emit CodehashDifferent(target, proposalCodehashes[proposalId], proposalCodehash); } } diff --git a/contracts/v4-patch/PatchProposal.sol b/contracts/v4-patch/PatchProposal.sol index 5d0e208..cd9e8f1 100644 --- a/contracts/v4-patch/PatchProposal.sol +++ b/contracts/v4-patch/PatchProposal.sol @@ -10,17 +10,48 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol"; import { TornadoStakingRewards } from "./TornadoStakingRewards.sol"; +import { RelayerRegistry } from "./RelayerRegistry.sol"; -contract RelayerRegistryProposal { +interface Proxy { + function upgradeTo(address newImplementation) external; +} + +// We will have to do this because of the contract size limit + +contract ProposalContractFactory { + function createStakingRewards( + address governance, + address torn, + address registry + ) external returns (address) { + return address(new TornadoStakingRewards(governance, torn, registry)); + } + + function createRegistryContract( + address torn, + address governance, + address ens, + address staking, + address feeManager + ) external returns (address) { + return address(new RelayerRegistry(torn, governance, ens, staking, feeManager)); + } +} + +contract PatchProposal { using SafeMath for uint256; using Address for address; + address public constant feeManagerAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7; + address public constant ensAddress = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e; + address public immutable registry = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2; + IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); - address public immutable registry; + ProposalContractFactory public immutable proposalContractFactory; - constructor(address _registry) public { - registry = _registry; + constructor(address _proposalContractFactory) public { + proposalContractFactory = ProposalContractFactory(_proposalContractFactory); } // Aight lets do this sirs @@ -35,11 +66,25 @@ contract RelayerRegistryProposal { // Get the old staking contract TornadoStakingRewards oldStaking = TornadoStakingRewards(address(GovernancePatchUpgrade(governance).Staking())); - // Get all of the TORN out cuz broken + // Get the small amount of TORN left oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking))); // And create a new staking contract - TornadoStakingRewards newStaking = new TornadoStakingRewards(governance, address(TORN), address(registry)); + TornadoStakingRewards newStaking = TornadoStakingRewards( + proposalContractFactory.createStakingRewards(address(governance), address(TORN), registry) + ); + + // And a new registry + address newRegistryAddress = proposalContractFactory.createRegistryContract( + address(TORN), + address(governance), + ensAddress, + address(newStaking), + feeManagerAddress + ); + + // Upgrade the registry proxy + Proxy(registry).upgradeTo(newRegistryAddress); // Now upgrade the governance to the latest stuff LoopbackProxy(payable(governance)).upgradeTo(address(new GovernancePatchUpgrade(address(newStaking), gasComp, vault))); diff --git a/contracts/v4-patch/RelayerRegistry.sol b/contracts/v4-patch/RelayerRegistry.sol index 3f3b248..43ecce9 100644 --- a/contracts/v4-patch/RelayerRegistry.sol +++ b/contracts/v4-patch/RelayerRegistry.sol @@ -139,14 +139,14 @@ contract RelayerRegistry is Initializable, EnsResolve { address _torn, address _governance, address _ens, - bytes32 _staking, - bytes32 _feeManager + address _staking, + address _feeManager ) public { torn = TORN(_torn); governance = _governance; ens = IENS(_ens); - staking = TornadoStakingRewards(resolve(_staking)); - feeManager = IFeeManager(resolve(_feeManager)); + staking = TornadoStakingRewards(_staking); + feeManager = IFeeManager(_feeManager); } /** diff --git a/hardhat.config.js b/hardhat.config.js index 0ac5761..06998e9 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -25,22 +25,13 @@ module.exports = { }, }, }, - { - version: '0.8.20', - settings: { - optimizer: { - enabled: true, - runs: 1000, - }, - }, - }, ], }, networks: { hardhat: { forking: { - url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`, - blockNumber: config.forkBlockNumber, + url: `${process.env.RPC_URL}`, + timeout: 2147483647, }, initialBaseFeePerGas: 5, }, @@ -49,8 +40,8 @@ module.exports = { timeout: 120000, }, mainnet: { - url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`, - accounts: ['900e9f0e8ce24c022026649c48a059fb6ffa0a2523811d797b47d789bf106def'], // random pk off keys.lol + url: `${process.env.RPC_URL}`, + //accounts: [`${process.env.PK}`], timeout: 2147483647, }, }, diff --git a/scripts/helper/propose_proposal.js b/scripts/helper/propose_proposal.js index 6ef6b0f..37ca6e8 100644 --- a/scripts/helper/propose_proposal.js +++ b/scripts/helper/propose_proposal.js @@ -9,7 +9,7 @@ async function propose(proposalArgs) { 'contracts/v1/Governance.sol:Governance', '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce', ) - GovernanceContract = await GovernanceContract.connect(proposer) + GovernanceContract = GovernanceContract.connect(proposer) const response = await GovernanceContract.propose(ProposalContract.address, proposalArgs[2]) diff --git a/scripts/layout.js b/scripts/layout.js new file mode 100644 index 0000000..953a32a --- /dev/null +++ b/scripts/layout.js @@ -0,0 +1,7 @@ +const hre = require('hardhat') + +async function main() { + await hre.storageLayout.export() +} + +main() diff --git a/test/patch/patch.test.js b/test/patch/patch.test.js new file mode 100644 index 0000000..d44d2c0 --- /dev/null +++ b/test/patch/patch.test.js @@ -0,0 +1,188 @@ +const { expect } = require('chai') +const { ethers } = require('hardhat') +const { BigNumber } = require('@ethersproject/bignumber') +const { propose } = require('../../scripts/helper/propose_proposal.js') + +const config = require('../../config') + +const { getSignerFromAddress, takeSnapshot, revertSnapshot, advanceTime } = require('../utils') + +function printBalanceWithDescription(descr, bn) { + console.log( + `\n ${descr} => ${ + bn.div(BigNumber.from(10).pow(18)).toString() + + '.' + + bn.div(BigNumber.from(10).pow(14)).toString().slice(-4) + }`, + ) +} + +describe('V3 governance tests', () => { + const zero = BigNumber.from(0) + + const ProposalState = { + Pending: 0, + Active: 1, + Defeated: 2, + Timelocked: 3, + AwaitingExecution: 4, + Executed: 5, + Expired: 6, + } + + let periods = { + EXECUTION_DELAY: zero, + EXECUTION_EXPIRATION: zero, + QUORUM_VOTES: zero, + PROPOSAL_THRESHOLD: zero, + VOTING_DELAY: zero, + VOTING_PERIOD: zero, + CLOSING_PERIOD: zero, + VOTE_EXTEND_TIME: zero, + } + + async function setPeriods(_periods, govc) { + _periods.EXECUTION_DELAY = await govc.EXECUTION_DELAY() + _periods.EXECUTION_EXPIRATION = await govc.EXECUTION_EXPIRATION() + _periods.QUORUM_VOTES = await govc.QUORUM_VOTES() + _periods.PROPOSAL_THRESHOLD = await govc.PROPOSAL_THRESHOLD() + _periods.VOTING_DELAY = await govc.VOTING_DELAY() + _periods.VOTING_PERIOD = await govc.VOTING_PERIOD() + _periods.CLOSING_PERIOD = await govc.CLOSING_PERIOD() + _periods.VOTE_EXTEND_TIME = await govc.VOTE_EXTEND_TIME() + return _periods + } + + let quorumVotes + + let GovernanceContract + let TornToken + + let provider + + let tornwhale + let proposer + + let propfacfac + let propfac + let torn + let gov + + // From other tests + + let getToken = async (tokenAddress) => { + return await ethers.getContractAt('@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20', tokenAddress) + } + + let minewait = async (time) => { + await ethers.provider.send('evm_increaseTime', [time]) + await ethers.provider.send('evm_mine', []) + } + + let sendr = async (method, params) => { + return await ethers.provider.send(method, params) + } + + let clog = (...x) => { + console.log(x) + } + + let pE = (x) => { + return ethers.utils.parseEther(`${x}`) + } + + let snapshotId + + before(async function () { + // Pick our signer + proposer = (await ethers.getSigners())[2] + + // Ok get current gov + gov = (await ethers.getContractAt('GovernanceStakingUpgrade', config.governance)).connect(proposer) + + // Impersonate + await sendr('hardhat_impersonateAccount', [config.governance]) + + // Pick whale + tornwhale = ethers.provider.getSigner(config.governance) + + // Connect to above + torn = (await getToken(config.TORN)).connect(tornwhale) + + // Set balance of governance contract + await ethers.provider.send('hardhat_setBalance', [proposer.address, pE(10).toHexString()]) + + // Take gov balance + const govbal = await torn.balanceOf(gov.address) + + // Transfer + await torn.transfer(proposer.address, govbal) + + // Check bal was allocated + expect(await torn.balanceOf(proposer.address)).to.equal(govbal) + + // Connect + torn = torn.connect(proposer) + + periods = await setPeriods(periods, gov) + + // Factory of the proposal + propfacfac = await ethers.getContractFactory('ProposalContractFactory') + propfac = await ethers.getContractFactory('PatchProposal') + + snapshotId = await takeSnapshot() + }) + + afterEach(async () => { + await revertSnapshot(snapshotId) + snapshotId = await takeSnapshot() + }) + + describe('Patch: Main integrative', () => { + it('Should be able to pass all predicates concerning patch update', async () => { + // Load these vars + + const oldVaultAddr = await gov.userVault() + const oldGasCompAddr = await gov.gasCompensationVault() + const oldStaking = await gov.Staking() + + const govBalBef = await torn.balanceOf(gov.address) + const propBal = await torn.balanceOf(proposer.address) + + // Start proposing + + const proposal = await propfac.deploy((await propfacfac.deploy()).address) + + await torn.approve(gov.address, propBal) + await gov.connect(proposer).lockWithApproval(propBal) + + await gov.propose(proposal.address, 'PATCH') + + const proposalId = await gov.latestProposalIds(proposer.address) + + let proposalData = await gov.proposals(proposalId) + + await minewait(periods.VOTING_DELAY.add(1).toNumber()) + + await gov.castVote(proposalId, true) + + await ethers.provider.send('evm_setNextBlockTimestamp', [ + proposalData.endTime.add(periods.EXECUTION_DELAY).add(BigNumber.from(1000)).toNumber(), + ]) + + await ethers.provider.send('evm_mine', []) + + await gov.execute(proposalId) + + const newVaultAddr = await gov.userVault() + const newGasCompAddr = await gov.gasCompensationVault() + const newStaking = await gov.Staking() + + expect(oldGasCompAddr).to.equal(newGasCompAddr) + expect(newVaultAddr).to.equal(oldVaultAddr) + expect(newStaking) + .to.not.equal(oldStaking) + .and.to.not.equal('0x0000000000000000000000000000000000000000') + }) + }) +}) diff --git a/test/v1/governance.v1.test.js b/test/v1/governance.v1.test.js deleted file mode 100644 index 95d38fe..0000000 --- a/test/v1/governance.v1.test.js +++ /dev/null @@ -1,943 +0,0 @@ -const { ethers } = require('hardhat') -const { expect } = require('chai') -const { BigNumber } = require('@ethersproject/bignumber') -const { PermitSigner } = require('../../scripts/v1/Permit.js') -const tornConfig = require('torn-token') -const config = require('../../config') - -const ProposalState = { - Pending: 0, - Active: 1, - Defeated: 2, - Timelocked: 3, - AwaitingExecution: 4, - Executed: 5, - Expired: 6, -} - -const duration = { - seconds: function (val) { - return val - }, - minutes: function (val) { - return val * this.seconds(60) - }, - hours: function (val) { - return val * this.minutes(60) - }, - days: function (val) { - return val * this.hours(24) - }, - weeks: function (val) { - return val * this.days(7) - }, - years: function (val) { - return val * this.days(365) - }, -} - -describe('V1 governance tests', () => { - /// NETWORK && DOMAIN - let chainId - let domain - - //// SIGNERS - let signerArray - let proposer // = accounts[3] #TODO: set this - let secondProposer // = accounts[8] #TODO: set this - let proxy - - /// CONTRACTS - let governance, dummy - let snapshotId - let timestamp = 1577836800 // 01/01/2020 00:00 - let torn - - /// GOVERNANCE VARS - let votingDelay - let votingPeriod - let executionExpiration - let executionDelay - let extendTime - let proposalStartTime - let proposalEndTime - let lockingPeriod - - /// ON-CHAIN - let balanceProposer - const cap = BigNumber.from(tornConfig.torn.cap) - const tenThousandTorn = BigNumber.from(10).pow(BigNumber.from(18)).mul(BigNumber.from(10000)) - const miningPrivateKey = '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3' - let miningPublicKey = '0x' + ethers.utils.computeAddress(Buffer.from(miningPrivateKey.slice(2), 'hex')) - - before(async function () { - signerArray = await ethers.getSigners() - proposer = signerArray[3] - secondProposer = signerArray[8] - - chainId = (await signerArray[0].provider.getNetwork()).chainId - - governance = await ethers.getContractFactory('MockGovernance') - governance = await governance.deploy() - - torn = await ethers.getContractFactory('TORNMock2') - - miningPublicKey = miningPublicKey.slice(2) - - proxy = await ethers.getContractFactory('MockProxy') - - proxy = await proxy.deploy(governance.address, []) - - governance = await ethers.getContractAt('MockGovernance', proxy.address) - - torn = await torn.deploy(proxy.address, duration.days(30), [ - { to: miningPublicKey, amount: cap.toString() }, - ]) - - await governance.initialize(torn.address + '000000000000000000000000') - - expect(await governance.torn()).to.equal(torn.address) - - dummy = await ethers.getContractFactory('Dummy') - dummy = await dummy.deploy() - - balanceProposer = cap.div(BigNumber.from(4)) - - await ethers.provider.send('hardhat_impersonateAccount', [miningPublicKey]) - miningPublicKey = await ethers.getSigner(miningPublicKey) - - await signerArray[0].sendTransaction({ value: ethers.utils.parseEther('3'), to: miningPublicKey.address }) - - torn = await torn.connect(miningPublicKey) - - await torn.transfer(secondProposer.address, balanceProposer.div(BigNumber.from(2))) - - await torn.transfer(proposer.address, balanceProposer) - - await torn.setChainId(chainId) - await governance.setTimestamp(timestamp) - - votingDelay = await governance.VOTING_DELAY() - votingPeriod = await governance.VOTING_PERIOD() - executionExpiration = await governance.EXECUTION_EXPIRATION() - executionDelay = await governance.EXECUTION_DELAY() - extendTime = await governance.VOTE_EXTEND_TIME() - - proposalStartTime = BigNumber.from(timestamp).add(votingDelay) - proposalEndTime = votingPeriod.add(BigNumber.from(proposalStartTime)) - - lockingPeriod = Number(extendTime) + Number(executionExpiration) + Number(executionDelay) - - domain = { - name: await torn.name(), - version: '1', - chainId, - verifyingContract: torn.address, - } - - snapshotId = await ethers.provider.send('evm_snapshot', []) - }) - - beforeEach(async function () { - torn = await torn.connect(proposer) - await torn.approve(governance.address, balanceProposer) - - governance = await governance.connect(proposer) - await governance.lockWithApproval(balanceProposer) - - const balance = await governance.lockedBalance(proposer.address) - expect(balance).to.equal(balanceProposer) - }) - - describe('#contructor', () => { - it('should work', async () => { - const proposalCount = await governance.proposalCount() - expect(proposalCount).to.equal(BigNumber.from(0)) - - const p = await governance.proposals(0) - - expect(p.proposer).to.equal(governance.address) - expect(p.target).to.equal('0x000000000000000000000000000000000000dEaD') - expect(p.endTime).to.equal(BigNumber.from(0)) - expect(p.forVotes).to.equal(BigNumber.from(0)) - expect(p.againstVotes).to.equal(BigNumber.from(0)) - expect(p.executed).to.equal(true) - expect(p.extended).to.equal(false) - }) - }) - - describe('#propose', () => { - it('should work', async () => { - const response = await governance.propose(dummy.address, 'dummy') - const receipt = await response.wait() - const logs = receipt.events - - const id = await governance.latestProposalIds(proposer.address) - const proposalCount = await governance.proposalCount() - - expect(proposalCount).to.equal(1) - - const proposal = await governance.proposals(id) - - expect(proposal.proposer).to.equal(proposer.address) - expect(proposal.startTime).to.equal(proposalStartTime) - expect(proposal.endTime).to.equal(proposalEndTime) - expect(proposal.forVotes).to.equal(0) - expect(proposal.againstVotes).to.equal(0) - expect(proposal.executed).to.equal(false) - - // emit ProposalCreated(newProposal.id, msg.sender, target, startBlock, endBlock, description); - expect(logs[0].event).to.equal('ProposalCreated') - expect(logs[0].args.id).to.equal(id) - expect(logs[0].args.proposer).to.equal(proposer.address) - expect(logs[0].args.target).to.equal(dummy.address) - expect(logs[0].args.description).to.equal('dummy') - expect(logs[0].args.startTime).to.equal(proposalStartTime) - expect(logs[0].args.endTime).to.equal(votingPeriod.add(BigNumber.from(proposalStartTime))) - - let state = await governance.state(id) - expect(state).to.equal(ProposalState.Pending) - await governance.setTimestamp(proposalEndTime) - state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - - const accountLock = await governance.canWithdrawAfter(proposer.address) - - expect(accountLock).to.equal(proposalEndTime.add(BigNumber.from(lockingPeriod))) - }) - it('fails if target is not a contract', async () => { - governance = await governance.connect(proposer) - await expect(governance.propose(signerArray[9].address, 'dummy')).to.be.revertedWith('not a contract') - }) - it('fails if proposer has already pending proposal', async () => { - await governance.propose(dummy.address, 'dummy') - await expect(governance.propose(dummy.address, 'dummy')).to.be.revertedWith( - 'Governance::propose: one live proposal per proposer, found an already active proposal', - ) - await governance.setTimestamp(proposalEndTime) - await expect(governance.propose(dummy.address, 'dummy')).to.be.revertedWith( - 'Governance::propose: one live proposal per proposer, found an already active proposal', - ) - }) - it('fails if proposer does not have voting power', async function () { - const voterBob = signerArray[5] - const oneThousandTorn = ethers.utils.parseEther('1000') - - torn = await torn.connect(miningPublicKey) - - await torn.transfer(voterBob.address, oneThousandTorn) - - torn = await torn.connect(voterBob) - - await torn.approve(governance.address, oneThousandTorn) - - expect(await governance.torn()).to.equal(torn.address) - - governance = await governance.connect(voterBob) - - await governance.lockWithApproval(oneThousandTorn.sub(1)) - - await expect(governance.propose(dummy.address, 'dummy')).to.be.revertedWith( - 'Governance::propose: proposer votes below proposal threshold', - ) - }) - }) - - describe('#castVote', () => { - it('should work if support is true', async () => { - await governance.propose(dummy.address, 'dummy') - const votesCount = balanceProposer - const id = await governance.latestProposalIds(proposer.address) - await governance.setTimestamp(proposalEndTime) - - const state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - const response = await governance.castVote(id, true) - const receipt = await response.wait() - const logs = await receipt.events - - expect(logs[0].event).to.equal('Voted') - expect(logs[0].args.voter).to.equal(proposer.address) - expect(logs[0].args.proposalId).to.equal(id) - expect(logs[0].args.support).to.equal(true) - expect(logs[0].args.votes).to.equal(votesCount) - - await governance.getReceipt(id, proposer.address) - - const proposal = await governance.proposals(id) - expect(proposal.forVotes).to.equal(votesCount) - expect(proposal.againstVotes).to.equal(0) - }) - it('should work if support is false', async () => { - await governance.propose(dummy.address, 'dummy') - - const votesCount = balanceProposer - const id = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalEndTime) - - const state = await governance.state(id) - - expect(state).to.equal(ProposalState.Active) - - const response = await governance.castVote(id, false) - const receipt = await response.wait() - const logs = await receipt.events - - expect(logs[0].event).to.equal('Voted') - expect(logs[0].args.voter).to.equal(proposer.address) - expect(logs[0].args.proposalId).to.equal(id) - expect(logs[0].args.support).to.equal(false) - expect(logs[0].args.votes).to.equal(votesCount) - - const proposal = await governance.proposals(id) - - expect(proposal.forVotes).to.equal(0) - expect(proposal.againstVotes).to.equal(votesCount) - }) - - it('should be able to change the choice later if already voted before', async () => { - await governance.propose(dummy.address, 'dummy') - - const votesCount = balanceProposer - const id = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalEndTime) - const state = await governance.state(id) - - expect(state).to.equal(ProposalState.Active) - - await governance.castVote(id, false) - await governance.castVote(id, true) - - const response = await governance.castVote(id, false) - const receipt = await response.wait() - const logs = await receipt.events - - expect(logs[0].event).to.equal('Voted') - expect(logs[0].args.voter).to.equal(proposer.address) - expect(logs[0].args.proposalId).to.equal(id) - expect(logs[0].args.support).to.equal(false) - expect(logs[0].args.votes).to.equal(votesCount) - - const proposal = await governance.proposals(id) - - expect(proposal.forVotes).to.equal(0) - expect(proposal.againstVotes).to.equal(votesCount) - }) - - it('should work if there are multiple voters', async () => { - const voterBob = signerArray[5] - const voterAlice = signerArray[7] - const tenThousandTorn = ethers.utils.parseEther('10000') - - torn = await torn.connect(miningPublicKey) - - await torn.transfer(voterBob.address, tenThousandTorn) - - await torn.transfer(voterAlice.address, tenThousandTorn.mul(BigNumber.from(2))) - - torn = await torn.connect(voterBob) - - await torn.approve(governance.address, tenThousandTorn) - - torn = await torn.connect(voterAlice) - - await torn.approve(governance.address, tenThousandTorn.mul(BigNumber.from(2))) - - governance = await governance.connect(voterBob) - await governance.lockWithApproval(tenThousandTorn) - - governance = await governance.connect(voterAlice) - await governance.lockWithApproval(tenThousandTorn.mul(BigNumber.from(2))) - - governance = await governance.connect(proposer) - await expect(governance.propose(dummy.address, 'dummy')).to.not.be.reverted - - const votesCount = balanceProposer - const id = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalEndTime) - - const state = await governance.state(id) - - expect(state).to.equal(ProposalState.Active) - - await governance.castVote(id, false) - - governance = await governance.connect(voterBob) - await governance.castVote(id, false) - - governance = await governance.connect(voterAlice) - await governance.castVote(id, true) - - const proposal = await governance.proposals(id) - expect(proposal.forVotes).to.equal(tenThousandTorn.mul(BigNumber.from(2))) - expect(proposal.againstVotes).to.equal(votesCount.add(tenThousandTorn)) - }) - - it('fails if voter does not have voting power', async () => { - const voterBob = signerArray[5] - - governance = await governance.connect(proposer) - await governance.propose(dummy.address, 'dummy') - - const id = await governance.latestProposalIds(proposer.address) - await governance.setTimestamp(proposalEndTime) - - const state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - - governance = await governance.connect(voterBob) - - await expect(governance.castVote(id, false)).to.be.revertedWith('Governance: balance is 0') - }) - - it('should be able to update number of votes count if the same decision is chosen after more tokens are locked', async () => { - const voterBob = signerArray[5] - - const tenThousandTorn = ethers.utils.parseEther('10000') - const fiveThousandTorn = tenThousandTorn.div(BigNumber.from(2)) - - torn = await torn.connect(miningPublicKey) - await torn.transfer(voterBob.address, tenThousandTorn) - - torn = await torn.connect(voterBob) - await torn.approve(governance.address, tenThousandTorn) - - governance = await governance.connect(voterBob) - await governance.lockWithApproval(fiveThousandTorn) - - governance = await governance.connect(proposer) - await governance.propose(dummy.address, 'dummy') - - const votesCount = balanceProposer - const id = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalEndTime) - const state = await governance.state(id) - - expect(state).to.equal(ProposalState.Active) - governance = await governance.connect(proposer) - await governance.castVote(id, false) - - governance = await governance.connect(voterBob) - await governance.castVote(id, false) - - let proposal = await governance.proposals(id) - - expect(proposal.forVotes).to.equal(BigNumber.from(0)) - expect(proposal.againstVotes).to.equal(votesCount.add(fiveThousandTorn)) - - await governance.lockWithApproval(fiveThousandTorn) - await governance.castVote(id, false) - - proposal = await governance.proposals(id) - - expect(proposal.forVotes).to.equal(BigNumber.from(0)) - expect(proposal.againstVotes).to.equal(votesCount.add(tenThousandTorn)) - }) - - it('extends time if the vote changes the outcome during the CLOSING_PERIOD', async () => { - const voterBob = signerArray[5] - const voterAlice = signerArray[7] - - torn = await torn.connect(miningPublicKey) - - await torn.transfer(voterBob.address, tenThousandTorn) - - await torn.transfer(voterAlice.address, tenThousandTorn.mul(BigNumber.from(2))) - - torn = await torn.connect(voterBob) - await torn.approve(governance.address, tenThousandTorn) - - torn = await torn.connect(voterAlice) - await torn.approve(governance.address, tenThousandTorn.mul(BigNumber.from(2))) - - governance = await governance.connect(voterBob) - await governance.lockWithApproval(tenThousandTorn) - - governance = await governance.connect(voterAlice) - await governance.lockWithApproval(tenThousandTorn.mul(BigNumber.from(2))) - - governance = await governance.connect(proposer) - await governance.propose(dummy.address, 'dummy') - - const id = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalStartTime.add(BigNumber.from(1))) - - const state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - - governance = await governance.connect(voterBob) - await governance.castVote(id, false) - - governance = await governance.connect(voterAlice) - await governance.castVote(id, true) - - let MAX_EXTENDED_TIME = await governance.VOTE_EXTEND_TIME() - let proposal = await governance.proposals(id) - expect(proposal.endTime).to.equal(proposalEndTime) - await governance.setTimestamp(proposalEndTime) - - governance = await governance.connect(proposer) - await governance.castVote(id, false) - - proposal = await governance.proposals(id) - - expect(proposal.endTime).to.equal(proposalEndTime.add(MAX_EXTENDED_TIME)) - - await governance.setTimestamp(proposalEndTime.add(BigNumber.from(duration.hours(5)))) - - const stateAfter = await governance.state(id) - - expect(stateAfter).to.equal(ProposalState.Active) - }) - - it('locks tokens after vote', async () => { - const voterAlice = signerArray[7] - - torn = await torn.connect(miningPublicKey) - await torn.transfer(voterAlice.address, tenThousandTorn) - - torn = await torn.connect(voterAlice) - await torn.approve(governance.address, tenThousandTorn) - - governance = await governance.connect(voterAlice) - await governance.lockWithApproval(tenThousandTorn) - - governance = await governance.connect(proposer) - await governance.propose(dummy.address, 'dummy') - - const id = await governance.latestProposalIds(proposer.address) - await governance.setTimestamp(proposalStartTime.add(BigNumber.from(1))) - - const state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - - const lockBefore = await governance.canWithdrawAfter(voterAlice.address) - expect(lockBefore).to.equal(BigNumber.from(0)) - - governance = await governance.connect(voterAlice) - await governance.castVote(id, true) - - const lockAfter = await governance.canWithdrawAfter(voterAlice.address) - expect(lockAfter).to.equal(proposalEndTime.add(BigNumber.from(lockingPeriod))) - }) - - it('does not reduce lock time', async () => { - const voterAlice = signerArray[7] - - torn = await torn.connect(miningPublicKey) - await torn.transfer(voterAlice.address, tenThousandTorn) - - torn = await torn.connect(voterAlice) - await torn.approve(governance.address, tenThousandTorn) - - governance = await governance.connect(voterAlice) - await governance.lockWithApproval(tenThousandTorn) - - torn = await torn.connect(secondProposer) - await torn.approve(governance.address, balanceProposer.div(BigNumber.from(2))) - - governance = await governance.connect(secondProposer) - await governance.lockWithApproval(balanceProposer.div(BigNumber.from(2))) - - governance = await governance.connect(proposer) - await governance.propose(dummy.address, 'dummy') - - const id1 = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalEndTime.sub(votingDelay).sub(BigNumber.from(1))) - - governance = await governance.connect(secondProposer) - await governance.propose(dummy.address, 'dummy2') - const id2 = await governance.latestProposalIds(secondProposer.address) - await governance.setTimestamp(proposalEndTime) - - const state1 = await governance.state(id1) - expect(state1).to.equal(ProposalState.Active) - - const state2 = await governance.state(id2) - expect(state2).to.equal(ProposalState.Active) - - const lockBefore = await governance.canWithdrawAfter(voterAlice.address) - expect(lockBefore).to.equal(BigNumber.from(0)) - - governance = await governance.connect(voterAlice) - await governance.castVote(id2, true) - - const lockAfter1 = await governance.canWithdrawAfter(voterAlice.address) - - await governance.castVote(id1, true) - const lockAfter2 = await governance.canWithdrawAfter(voterAlice.address) - - expect(lockAfter1).to.equal(lockAfter2) - }) - }) - - describe('#lock', () => { - let owner = miningPublicKey - let tokensAmount = BigNumber.from(10).pow(BigNumber.from(21)).mul(BigNumber.from(1337)) - - it('permitClass works', async () => { - owner = owner.slice(2) - owner = await ethers.getSigner(owner) - - const args = { - owner, - spender: governance.address, - value: tokensAmount, - nonce: '0x00', - deadline: BigNumber.from('123123123123123'), - } - - const permitSigner = new PermitSigner(domain, args) - - permitSigner.getPayload() - - // Generate the signature in place - const privateKey = '0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c' - - const address = '0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b' - - const signature = await permitSigner.getSignature(privateKey) - - const signer = await permitSigner.getSignerAddress(args, signature.hex) - - expect(address).to.equal(signer) - }) - - it('calls approve if signature is valid', async () => { - const chainIdFromContract = await torn.chainId() - expect(chainIdFromContract).to.equal(new BigNumber.from(domain.chainId)) - const args = { - owner, - spender: governance.address, - value: tokensAmount, - nonce: 0, - deadline: BigNumber.from('5609459200'), - } - - const permitSigner = new PermitSigner(domain, args) - const signature = await permitSigner.getSignature(miningPrivateKey) - const signer = await permitSigner.getSignerAddress(args, signature.hex) - - expect(signer).to.equal(miningPublicKey.address) - - const balanceBefore = await torn.balanceOf(governance.address) - - const lockedBalanceBefore = await governance.lockedBalance(owner.address) - - governance = await governance.connect(owner) - - await governance.lock( - args.owner, - // args.spender, - args.value.toString(), - args.deadline.toString(), - signature.v, - signature.r, - signature.s, - ) - - const balanceAfter = await torn.balanceOf(governance.address) - const lockedBalanceAfter = await governance.lockedBalance(owner.address) - - expect(balanceAfter).to.equal(balanceBefore.add(args.value)) - expect(lockedBalanceAfter).to.equal(lockedBalanceBefore.add(args.value)) - }) - - it('adds up tokens if already existing', async () => { - const voterBob = signerArray[5] - const tenThousandTorn = ethers.utils.parseEther('10000') - - torn = await torn.connect(miningPublicKey) - await torn.transfer(voterBob.address, tenThousandTorn) - - torn = await torn.connect(voterBob) - await torn.approve(governance.address, tenThousandTorn) - - governance = await governance.connect(voterBob) - - await governance.lockWithApproval(tenThousandTorn.div(BigNumber.from(2))) - await governance.lockWithApproval(tenThousandTorn.div(BigNumber.from(2))) - - const balanceAfter = await torn.balanceOf(voterBob.address) - const lockedBalanceAfter = await governance.lockedBalance(voterBob.address) - - expect(balanceAfter).to.equal(BigNumber.from(0)) - expect(lockedBalanceAfter).to.equal(tenThousandTorn) - }) - }) - - describe('#unlock', () => { - it('should work if there is no activity made', async () => { - const balanceBeforeTorn = await torn.balanceOf(proposer.address) - const balanceBefore = await governance.lockedBalance(proposer.address) - - governance = await governance.connect(proposer) - await governance.unlock(balanceProposer) - - const balanceAfterTorn = await torn.balanceOf(proposer.address) - const balanceAfter = await governance.lockedBalance(proposer.address) - - expect(balanceBefore).to.equal(balanceAfter.add(balanceProposer)) - expect(balanceAfterTorn).to.equal(balanceBeforeTorn.add(balanceProposer)) - }) - it('fails if asking more than balance', async () => { - governance = await governance.connect(proposer) - await expect(governance.unlock(balanceProposer + 1)).to.be.revertedWith( - 'Governance: insufficient balance', - ) - }) - it('fail if there is active proposal', async () => { - await governance.propose(dummy.address, 'dummy') - await expect(governance.unlock(balanceProposer)).to.be.revertedWith('Governance: tokens are locked') - }) - it('unlock if there proposals expired', async () => { - await governance.propose(dummy.address, 'dummy') - await governance.setTimestamp(proposalEndTime.add(BigNumber.from(lockingPeriod + duration.minutes(1)))) - await governance.unlock(balanceProposer) - }) - }) - - describe('#undelegate', () => { - it('should work', async () => { - let delegatee = signerArray[5] - await governance.delegate(delegatee.address) - const response = await governance.undelegate() - const receipt = await response.wait() - const logs = receipt.events - expect(logs[0].args.account).to.equal(proposer.address) - expect(logs[0].args[1]).to.equal(delegatee.address) - }) - }) - - describe('#delegate', () => { - it('should work', async () => { - let delegatee = signerArray[5] - - let vp = await governance.delegatedTo(proposer.address) - expect(String(vp)).to.equal('0x0000000000000000000000000000000000000000') - - await governance.delegate(delegatee.address) - vp = await governance.delegatedTo(proposer.address) - expect(String(vp)).to.equal(delegatee.address) - }) - - it('emits undelegate event if delegate called with non empty delegateTo', async () => { - let delegatee = signerArray[5] - let delegateeSecond = signerArray[6] - - const response = await governance.delegate(delegatee.address) - const receipt = await response.wait() - - expect(receipt.logs.length).to.equal(1) - - await expect(governance.delegate(delegatee.address)).to.be.revertedWith('Governance: invalid delegatee') - - const responseTwo = await governance.delegate(delegateeSecond.address) - let receiptTwo = await responseTwo.wait() - receiptTwo.logs = receiptTwo.events - - expect(receiptTwo.logs.length).to.equal(2) - expect(receiptTwo.logs[0].event).to.equal('Undelegated') - expect(receiptTwo.logs[0].args.account).to.equal(proposer.address) - expect(receiptTwo.logs[0].args.from).to.equal(delegatee.address) - - expect(receiptTwo.logs[1].event).to.equal('Delegated') - expect(receiptTwo.logs[1].args.account).to.equal(proposer.address) - expect(receiptTwo.logs[1].args[1]).to.equal(delegateeSecond.address) - - const vp = await governance.delegatedTo(proposer.address) - - expect(vp).to.equal(delegateeSecond.address) - }) - it('can propose with delegated votes', async () => { - let delegatee = signerArray[5] - await governance.delegate(delegatee.address) - - governance = await governance.connect(delegatee) - await governance.proposeByDelegate(proposer.address, dummy.address, 'dummy') - - const proposalCount = await governance.proposalCount() - expect(proposalCount).to.equal(1) - - const latestProposalId = await governance.latestProposalIds(proposer.address) - expect(latestProposalId).to.equal(1) - - const proposal = await governance.proposals(1) - expect(proposal.proposer).to.equal(proposer.address) - }) - - it('can vote with delegated votes', async () => { - let delegatee = signerArray[5] - - governance = await governance.connect(proposer) - await governance.delegate(delegatee.address) - - await governance.propose(dummy.address, 'dummy') - - const votesCount = balanceProposer - - const id = await governance.latestProposalIds(proposer.address) - - await governance.setTimestamp(proposalEndTime) - - governance = await governance.connect(delegatee) - await governance.castDelegatedVote([proposer.address], id, true) - - await governance.getReceipt(id, proposer.address) - - let proposal = await governance.proposals(id) - - expect(proposal.forVotes).to.equal(votesCount) - expect(proposal.againstVotes).to.equal(0) - - governance = await governance.connect(proposer) - await governance.castVote(id, false) - await governance.getReceipt(id, proposer.address) - - proposal = await governance.proposals(id) - - expect(proposal.forVotes).to.equal(0) - expect(proposal.againstVotes).to.equal(votesCount) - }) - }) - - describe.skip('#getAllProposals', () => { - it('fetches proposals', async () => { - await governance.propose(dummy.address, 'dummy') - await governance.setTimestamp(proposalEndTime) - - const proposals = await governance.getAllProposals(0, 0) - const proposal = proposals[0] - - expect(proposal.id).to.equal(1) - expect(proposal.proposer).to.equal(proposer.address) - expect(proposal.startTime).to.equal(proposalStartTime) - expect(proposal.endTime).to.equal(proposalEndTime) - expect(proposal.forVotes).to.equal(0) - expect(proposal.againstVotes).to.equal(0) - expect(proposal.executed).to.equal(false) - expect(proposal.state).to.equal(ProposalState.Active) - }) - }) - - describe.skip('#getBalances', () => { - it('fetches lockedBalance', async () => { - const lockedBalanceOne = await governance.getBalances([proposer.address, secondProposer.address]) - - lockedBalanceOne.to.equal([balanceProposer, BigNumber.from('0')]) - - torn = await torn.connect(secondProposer) - await torn.approve(governance.address, balanceProposer.div(BigNumber.from(2))) - - governance = await governance.connect(secondProposer) - await governance.lockWithApproval(balanceProposer.div(BigNumber.from(2))) - - const lockedBalance = await governance.getBalances([proposer.address, secondProposer.address]) - - expect(lockedBalance).to.equal([balanceProposer, balanceProposer.div(BigNumber.from(2))]) - }) - }) - - describe('#upgrades', () => { - it('allows to change variable state', async () => { - let proposal = await ethers.getContractFactory('ProposalStateChangeGovernance') - proposal = await proposal.deploy() - - governance = await governance.connect(proposer) - await governance.propose(proposal.address, 'proposal') - - const id = await governance.latestProposalIds(proposer.address) - await governance.setTimestamp(proposalStartTime.add(BigNumber.from(1))) - - let state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - - await governance.castVote(id, true) - - await governance.setTimestamp( - proposalEndTime.add(BigNumber.from(executionDelay).add(BigNumber.from(duration.days(1)))), - ) - - const EXECUTION_DELAY_BEFORE = await governance.EXECUTION_DELAY() - expect(EXECUTION_DELAY_BEFORE).to.equal(duration.days(2)) - - const response = await governance.execute(id) - let receipt = await response.wait() - receipt.logs = receipt.events - - const EXECUTION_DELAY_AFTER = await governance.EXECUTION_DELAY() - - expect(EXECUTION_DELAY_AFTER).to.equal(duration.days(3)) - expect(receipt.logs[0].event).to.equal('ProposalExecuted') - }) - it('upgrades implementation with variables change', async () => { - let NewImplementation = await ethers.getContractFactory('NewImplementation') - NewImplementation = await NewImplementation.deploy() - - let proposal = await ethers.getContractFactory('ProposalUpgrade') - proposal = await proposal.deploy(NewImplementation.address) - - governance = await governance.connect(proposer) - await governance.propose(proposal.address, 'proposal') - - const id = await governance.latestProposalIds(proposer.address) - await governance.setTimestamp(proposalStartTime.add(BigNumber.from(1))) - - let state = await governance.state(id) - expect(state).to.equal(ProposalState.Active) - - governance = await governance.connect(proposer) - await governance.castVote(id, true) - - await governance.setTimestamp( - proposalEndTime.add(BigNumber.from(executionDelay).add(BigNumber.from(duration.days(1)))), - ) - - const newGovernance = await ethers.getContractAt('NewImplementation', governance.address) - const response = await governance.execute(id) - let receipt = await response.wait() - receipt.logs = receipt.events - - let newVariable = await newGovernance.newVariable() - expect(newVariable).to.equal(0) - - const responseExecute = await newGovernance.execute(123) - let receiptExecute = await responseExecute.wait() - receiptExecute.logs = receiptExecute.events - - newVariable = await newGovernance.newVariable() - expect(newVariable).to.equal(999) - - expect(receipt.logs[1].event).to.equal('ProposalExecuted') - expect(receiptExecute.logs[0].event).to.equal('Overriden') - }) - it('cannot initialize implementation contract', async () => { - const impl = await (await ethers.getContractFactory('NewImplementation')).deploy() - await expect(impl.initialize(signerArray[0].address + '000000000000000000000000')).to.be.revertedWith( - 'Contract instance has already been initialized', - ) - }) - }) - - afterEach(async () => { - await ethers.provider.send('evm_revert', [snapshotId]) - snapshotId = await ethers.provider.send('evm_snapshot', []) - }) - - after(async function () { - await ethers.provider.send('hardhat_reset', [ - { - forking: { - jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`, - blockNumber: process.env.use_latest_block == 'true' ? undefined : config.forkBlockNumber, - }, - }, - ]) - }) -}) diff --git a/test/v2/governance.v2.test.js b/test/v2/governance.v2.test.js deleted file mode 100644 index ff9f096..0000000 --- a/test/v2/governance.v2.test.js +++ /dev/null @@ -1,496 +0,0 @@ -const { expect } = require('chai') -const { ethers } = require('hardhat') -const { BigNumber } = require('@ethersproject/bignumber') -const { propose } = require('../../scripts/helper/propose_proposal.js') -const testcases = require('@ethersproject/testcases') -const seedbase = require('../../resources/hdnode.json') -const accountList = require('../../resources/accounts.json') -const config = require('../../config') - -describe('V2 governance tests', () => { - ///// ON-CHAIN CONSTANTS - let proxy_address = '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce' - let quorumVotes - - ///////////////////////////// CONTRACTS - let GovernanceContract - let TornToken - - //////////////////// IMPERSONATED - let tornadoMultisig - - //////////////////////////////// MOCK - let MockProposalFactory - - /////// GOV PARAMS - const ProposalState = { - Pending: 0, - Active: 1, - Defeated: 2, - Timelocked: 3, - AwaitingExecution: 4, - Executed: 5, - Expired: 6, - } - - ///// ACCOUNTS - let dore - let whale - let signerArray = [] - let whales = [] - - //////////////////////////////////// TESTING & UTILITY - let randN = Math.floor(Math.random() * 1023) - let testseed = seedbase[randN].seed - - let minewait = async (time) => { - await ethers.provider.send('evm_increaseTime', [time]) - await ethers.provider.send('evm_mine', []) - } - - let sendr = async (method, params) => { - return await ethers.provider.send(method, params) - } - - let clog = (...x) => { - console.log(x) - } - - let pE = (x) => { - return ethers.utils.parseEther(`${x}`) - } - - let rand = (l, u) => { - return testcases.randomNumber(testseed, l, u) - } - - let snapshotIdArray = [] - - ///////////////////////////////////////////////////////////////////////////7 - before(async function () { - signerArray = await ethers.getSigners() - dore = signerArray[0] - - MockProposalFactory = await ethers.getContractFactory('MockProposal') - - GovernanceContract = await ethers.getContractAt('GovernanceGasUpgrade', proxy_address) - - TornToken = await ethers.getContractAt( - '@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20', - '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C', - ) - - quorumVotes = await GovernanceContract.QUORUM_VOTES() - }) - - describe('#imitation block', () => { - it('Should successfully imitate tornado multisig', async function () { - await sendr('hardhat_impersonateAccount', ['0xb04E030140b30C27bcdfaafFFA98C57d80eDa7B4']) - tornadoMultisig = await ethers.getSigner('0xb04E030140b30C27bcdfaafFFA98C57d80eDa7B4') - }) - - it('Should successfully imitate whale', async function () { - await sendr('hardhat_impersonateAccount', ['0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3']) - whale = await ethers.getSigner('0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3') - GovernanceContract = await GovernanceContract.connect(whale) - - let balance = await TornToken.balanceOf(whale.address) - TornToken = await TornToken.connect(whale) - - await TornToken.approve(GovernanceContract.address, ethers.utils.parseEther('8000000000')) - await expect(GovernanceContract.lockWithApproval(balance)).to.not.be.reverted - - expect((await GovernanceContract.lockedBalance(whale.address)).toString()).to.equal(balance.toString()) - snapshotIdArray[0] = await sendr('evm_snapshot', []) - }) - }) - - describe('#mock rewards + proposal distribution with multiple accounts', () => { - let addrArray = [] - let signerArmy = [] - let delegatedSignerArmy = [] - let votingAddressArray = [] - const numberOfVoters = 80 - const numberOfDelegators = 30 - - it('Should create empty address array', () => { - for (let i = 0; i < 10; i++) { - votingAddressArray[i] = new Array(numberOfDelegators / 10 + 1) - } - }) - - it('Should impersonate and fund 80 accounts', async function () { - ////////// WRITE WHALE ADDRESSES AND PREPARE FOR TRANSFERS - addrArray = [ - '0x6cC5F688a315f3dC28A7781717a9A798a59fDA7b', - '0xF977814e90dA44bFA03b6295A0616a897441aceC', - '0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3', - '0x055AD5E56c11c0eF55818155c69ed9BA2f4b3e90', - ] - - for (let i = 0; i < 4; i++) { - await sendr('hardhat_impersonateAccount', [addrArray[i]]) - whales[i] = await ethers.getSigner(addrArray[i]) - } - - for (let i = 1; i < 4; i++) { - //last test really unnecessary - const torn = await TornToken.connect(whales[i]) - const whaleBalance = await torn.balanceOf(whales[i].address) - await torn.approve(addrArray[0], whaleBalance) - await expect(() => torn.transfer(addrArray[0], whaleBalance)).to.changeTokenBalance( - torn, - whales[0], - whaleBalance, - ) - } - - const whale0Balance = await TornToken.balanceOf(whales[0].address) - const toTransfer = whale0Balance.sub(pE(10000)).div(numberOfVoters * 3) - let torn0 = await TornToken.connect(whales[0]) - const oldBalance = await TornToken.balanceOf(await GovernanceContract.userVault()) - let lockedSum = BigNumber.from(0) - - ////////// TRANSFER TO 50 ACCOUNTS + DELEGATION TO 10 - - for (let i = 0; i < numberOfVoters; i++) { - /// PREPARE ACCOUNTS - const accAddress = accountList[i + 7].checksumAddress - await sendr('hardhat_impersonateAccount', [accAddress]) - - signerArmy[i] = await ethers.getSigner(accAddress) - const tx = { to: signerArmy[i].address, value: pE(1) } - - await signerArray[0].sendTransaction(tx) - - /// FILL WITH GAS FOR LATER - await expect(() => torn0.transfer(signerArmy[i].address, toTransfer)).to.changeTokenBalance( - torn0, - signerArmy[i], - toTransfer, - ) - let torn = await torn0.connect(signerArmy[i]) - - /// APPROVE TO GOVERNANCE FOR LOCK - await expect(torn.approve(GovernanceContract.address, toTransfer)).to.not.be.reverted - const gov = await GovernanceContract.connect(signerArmy[i]) - - ///// LOCK - if (i > numberOfVoters / 2) { - await expect(() => gov.lockWithApproval(toTransfer.div(i))).to.changeTokenBalance( - torn, - signerArmy[i], - BigNumber.from(0).sub(toTransfer.div(i)), - ) - lockedSum = lockedSum.add(toTransfer.div(i)) - } else { - await expect(() => gov.lockWithApproval(toTransfer)).to.changeTokenBalance( - torn, - signerArmy[i], - BigNumber.from(0).sub(toTransfer), - ) - lockedSum = lockedSum.add(toTransfer) - } - - if (i > numberOfVoters - numberOfDelegators - 1) { - delegatedSignerArmy[i - (numberOfVoters - numberOfDelegators)] = signerArmy[i] - } - - if (i < 10) { - votingAddressArray[i][0] = signerArmy[i].address - } - - const restBalance = await torn.balanceOf(signerArmy[i].address) - await torn.transfer(whale.address, restBalance) - } - - for (let i = 0; i < numberOfDelegators; i++) { - const gov = await GovernanceContract.connect(delegatedSignerArmy[i]) - /// DELEGATE TO 10 FIRST SIGNERS - await expect(gov.delegate(signerArmy[i % 10].address)).to.emit(gov, 'Delegated') - votingAddressArray[i % 10][Math.floor(i / 10) + 1] = delegatedSignerArmy[i].address - } - - const TornVault = await GovernanceContract.userVault() - expect(await TornToken.balanceOf(TornVault)).to.equal(lockedSum.add(oldBalance)) - - const gov = await GovernanceContract.connect(whales[0]) - await expect(torn0.approve(GovernanceContract.address, pE(10000))).to.not.be.reverted - await expect(() => gov.lockWithApproval(toTransfer)).to.changeTokenBalance( - torn0, - whales[0], - BigNumber.from(0).sub(toTransfer), - ) - - snapshotIdArray[1] = await sendr('evm_snapshot', []) - }) - - it('Test multiple accounts proposal', async function () { - let checkIfQuorumFulfilled = async function (proposalId) { - const proposalData = await GovernanceContract.proposals(proposalId) - const allVotes = proposalData[4].add(proposalData[5]) - return allVotes.gte(quorumVotes) - } - - const ProposalContract = await MockProposalFactory.deploy() - - clog( - 'Torn balance of governance contract: ', - (await TornToken.balanceOf(GovernanceContract.address)).toString(), - ) - - ////////////// STANDARD PROPOSAL ARGS TEST ////////////////////// - let response, id, state - ;[response, id, state] = await propose([whales[0], ProposalContract, 'LotteryUpgrade']) - const { events } = await response.wait() - const args = events.find(({ event }) => event == 'ProposalCreated').args - expect(args.id).to.be.equal(id) - expect(args.target).to.be.equal(ProposalContract.address) - expect(args.description).to.be.equal('LotteryUpgrade') - expect(state).to.be.equal(ProposalState.Pending) - - ////////////////////////INCREMENT TO VOTING TIME//////////////////////// - await minewait((await GovernanceContract.VOTING_DELAY()).add(1).toNumber()) - - /////////////////// PREPARE MULTISIG AND COMPENSATIONS - let multiGov = await GovernanceContract.connect(tornadoMultisig) - - await dore.sendTransaction({ to: tornadoMultisig.address, value: pE(1) }) - await expect(multiGov.setGasCompensations(pE(500))).to.not.be.reverted - ///////////////////////////// VOTE //////////////////////////// - const overrides = { - gasPrice: BigNumber.from(5), - } - - let signerArmyBalanceInitial = [] - let signerArmyBalanceDiff = [] - let gasUsedArray = [] - - snapshotIdArray[2] = await sendr('evm_snapshot', []) - - for (let i = 0; i < 10; i++) { - let gov = await GovernanceContract.connect(signerArmy[i]) - let randN = rand(i * 5, i * 6) - randN = randN % 2 - let response - - signerArmyBalanceInitial[i] = await signerArmy[i].getBalance() - - if (randN > 0) { - response = await gov.castDelegatedVote(votingAddressArray[i], id, true, overrides) - } else { - response = await gov.castDelegatedVote(votingAddressArray[i], id, false, overrides) - } - - signerArmyBalanceDiff[i] = !(await checkIfQuorumFulfilled(id)) - ? signerArmyBalanceInitial[i].sub(await signerArmy[i].getBalance()) - : signerArmyBalanceDiff[i - 1] - - const receipt = await response.wait() - gasUsedArray[i] = receipt.cumulativeGasUsed - } - - for (let i = 10; i < numberOfVoters - numberOfDelegators; i++) { - let gov = await GovernanceContract.connect(signerArmy[i]) - let randN = rand(i * 5, i * 6) - randN = randN % 2 - let response - - signerArmyBalanceInitial[i] = await signerArmy[i].getBalance() - - if (randN > 0) { - response = await gov.castVote(id, true, overrides) - } else { - response = await gov.castVote(id, false, overrides) - } - - signerArmyBalanceDiff[i] = !(await checkIfQuorumFulfilled(id)) - ? signerArmyBalanceInitial[i].sub(await signerArmy[i].getBalance()) - : signerArmyBalanceDiff[i - 1] - - const receipt = await response.wait() - gasUsedArray[i] = receipt.cumulativeGasUsed - } - - //////////////////////////////// GET STATE /////////////////////////////// - state = await GovernanceContract.state(id) - expect(state).to.be.equal(ProposalState.Active) - - ///////////////////////////// VOTER INFO /////////////////////////////////// - // (uncomment for more data) - /* - for (i = 0; i < numberOfVoters; i+=5) { - const j = BigNumber.from(i); - console.log( - `Voter ${i} sqrt: `, - ((await GovernanceLottery.lotteryUserData(id,j))[0]).toString(), - `Voter ${i+1} sqrt: `, - ((await GovernanceLottery.lotteryUserData(id,j.add(1)))[0]).toString(), - `Voter ${i+2} sqrt: `, - ((await GovernanceLottery.lotteryUserData(id,j.add(2)))[0]).toString(), - `Voter ${i+3} sqrt: `, - ((await GovernanceLottery.lotteryUserData(id,j.add(3)))[0]).toString(), - `Voter ${i+4} sqrt: `, - ((await GovernanceLottery.lotteryUserData(id,j.add(4)))[0]).toString(), - "\n", - ) - } - - for (i = 0; i < numberOfVoters; i+=5) { - console.log( - `Voter ${i} ether used: `, - gasUsedArray[i], - `Voter ${i+1} ether used: `, - gasUsedArray[i+1], - `Voter ${i+2} ether used: `, - gasUsedArray[i+2], - `Voter ${i+3} ether used: `, - gasUsedArray[i+3], - `Voter ${i+4} ether used: `, - gasUsedArray[i+4], - "\n", - ) - } - */ - - await sendr('evm_revert', [snapshotIdArray[2]]) - - ///////////////////////////////// VOTE WITHOUT COMPENSATION ////////////////////////////////////// - let gasUsedWithoutCompensation = [] - await multiGov.setGasCompensations(pE(100000)) - - for (let i = 0; i < 10; i++) { - let gov = await GovernanceContract.connect(signerArmy[i]) - let randN = rand(i * 5, i * 6) - randN = randN % 2 - let response - - if (randN > 0) { - response = await gov.castDelegatedVote(votingAddressArray[i], id, true, overrides) - } else { - response = await gov.castDelegatedVote(votingAddressArray[i], id, false, overrides) - } - - const receipt = await response.wait() - gasUsedWithoutCompensation[i] = receipt.cumulativeGasUsed - } - - for (let i = 10; i < numberOfVoters - numberOfDelegators; i++) { - let gov = await GovernanceContract.connect(signerArmy[i]) - let randN = rand(i * 5, i * 6) - randN = randN % 2 - let response - - if (randN > 0) { - response = await gov.castVote(id, true, overrides) - } else { - response = await gov.castVote(id, false, overrides) - } - - const receipt = await response.wait() - - gasUsedWithoutCompensation[i] = receipt.cumulativeGasUsed - } - - await multiGov.setGasCompensations(pE(100)) - //////////////////////////////// GET STATE /////////////////////////////// - state = await GovernanceContract.state(id) - expect(state).to.be.equal(ProposalState.Active) - - ///////////////////////////// VOTING GAS INFO /////////////////////////////////// - let gasUsedSumNoComp = BigNumber.from(0) - let gasUsedSum = BigNumber.from(0) - let gasSumDiff = BigNumber.from(0) - let gasUsedSumNoCompDel = BigNumber.from(0) - let gasUsedSumDel = BigNumber.from(0) - let gasSumDiffDel = BigNumber.from(0) - - for (let i = 0; i < 10; i++) { - gasUsedSumDel = gasUsedSumDel.add(gasUsedArray[i]) - gasUsedSumNoCompDel = gasUsedSumNoCompDel.add(gasUsedWithoutCompensation[i]) - gasSumDiffDel = gasSumDiffDel.add(signerArmyBalanceDiff[i]) - } - - for (let i = 10; i < numberOfVoters - numberOfDelegators; i++) { - gasUsedSum = gasUsedSum.add(gasUsedArray[i]) - gasUsedSumNoComp = gasUsedSumNoComp.add(gasUsedWithoutCompensation[i]) - gasSumDiff = gasSumDiff.add(signerArmyBalanceDiff[i]) - } - - const gasUsedAverageNoCompDel = gasUsedSumNoCompDel.div(10) - const gasUsedAverageDel = gasUsedSumDel.div(10) - const gasSumAverageDiffDel = gasSumDiffDel.div(10) - - const gasUsedAverageNoComp = gasUsedSumNoComp.div(numberOfVoters - 10) - const gasUsedAverage = gasUsedSum.div(numberOfVoters - 10) - const gasSumAverageDiff = gasSumDiff.div(numberOfVoters - 10) - - console.log( - '\n', - '----------------------------CAST VOTE INFO------------------------', - '\n', - 'Gas use average: ', - gasUsedAverage.toString(), - '\n', - 'Gas use without compensation average: ', - gasUsedAverageNoComp.toString(), - '\n', - 'Gas diff average: ', - gasSumAverageDiff.toString(), - '\n', - 'Gas compensated in average: ', - gasUsedAverage.sub(gasSumAverageDiff).toString(), - '\n', - '--------------------------------------------------------------------', - '\n', - ) - - console.log( - '\n', - '----------------------------CAST DELEGATED VOTE INFO------------------------', - '\n', - 'Gas use average: ', - gasUsedAverageDel.toString(), - '\n', - 'Gas use without compensation average: ', - gasUsedAverageNoCompDel.toString(), - '\n', - 'Gas diff average: ', - gasSumAverageDiffDel.toString(), - '\n', - 'Gas compensated in average: ', - gasUsedAverageDel.sub(gasSumAverageDiffDel).toString(), - '\n', - '--------------------------------------------------------------------', - '\n', - ) - /////////////////////////////// INCREMENT AGAIN ////////////////////////////////// - await minewait( - ( - await GovernanceContract.VOTING_PERIOD() - ) - .add(await GovernanceContract.EXECUTION_DELAY()) - .add(10000) - .toNumber(), - ) - - ////////////// EXECUTE - if (BigNumber.from(await GovernanceContract.state(id)).eq(ProposalState.Defeated)) { - await expect(GovernanceContract.execute(id)).to.be.reverted - } else { - await expect(GovernanceContract.execute(id)).to.not.be.reverted - } - }) - }) - - after(async function () { - await ethers.provider.send('hardhat_reset', [ - { - forking: { - jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`, - blockNumber: process.env.use_latest_block == 'true' ? undefined : config.forkBlockNumber, - }, - }, - ]) - }) -}) diff --git a/test/v3/governance.v3.test.js b/test/v3/governance.v3.test.js deleted file mode 100644 index 71680f8..0000000 --- a/test/v3/governance.v3.test.js +++ /dev/null @@ -1,71 +0,0 @@ -const { ethers } = require('hardhat') -const { expect } = require('chai') - -const config = require('../../config') -const { getSignerFromAddress, takeSnapshot, revertSnapshot } = require('../utils') - -describe('V3 governance tests', () => { - let snapshotId - - //// CONTRACTS - let torn = config.TORN - let gov - - //// IMPERSONATED ACCOUNTS - let tornWhale - - //// HELPER FN - let getToken = async (tokenAddress) => { - return await ethers.getContractAt('@openzeppelin/contracts/token/ERC20/ERC20.sol:ERC20', tokenAddress) - } - - before(async function () { - tornWhale = await getSignerFromAddress(config.tornWhale) - - gov = (await ethers.getContractAt('GovernanceStakingUpgrade', config.governance)).connect(tornWhale) - - snapshotId = await takeSnapshot() - }) - - describe('#lock functionality', () => { - it('should be able to lock/unlock torn in governance', async () => { - const [sender] = await ethers.getSigners() - const value = ethers.utils.parseEther('1000') - - const tornToken = await (await getToken(torn)).connect(tornWhale) - await tornToken.transfer(sender.address, value) - await tornToken.connect(sender).approve(gov.address, value) - - const ethBalanceBeforeLock = await ethers.provider.getBalance(sender.address) - const tokenBalanceBeforeLock = await tornToken.balanceOf(sender.address) - let tx = await gov.connect(sender).lockWithApproval(value) - - let receipt = await tx.wait() - let txFee = receipt.cumulativeGasUsed.mul(receipt.effectiveGasPrice) - const ethBalanceAfterLock = await ethers.provider.getBalance(sender.address) - const tokenBalanceAfterLock = await tornToken.balanceOf(sender.address) - expect(ethBalanceAfterLock).to.be.equal(ethBalanceBeforeLock.sub(txFee)) - expect(tokenBalanceAfterLock).to.be.equal(tokenBalanceBeforeLock.sub(value)) - - const lockedBalanceAfterLock = await gov.lockedBalance(sender.address) - expect(lockedBalanceAfterLock).to.be.equal(value) - - tx = await gov.connect(sender).unlock(value) - - receipt = await tx.wait() - txFee = receipt.cumulativeGasUsed.mul(receipt.effectiveGasPrice) - const ethBalanceAfterUnlock = await ethers.provider.getBalance(sender.address) - const tokenBalanceAfterUnlock = await tornToken.balanceOf(sender.address) - expect(ethBalanceAfterUnlock).to.be.equal(ethBalanceAfterLock.sub(txFee)) - expect(tokenBalanceAfterUnlock).to.be.equal(tokenBalanceBeforeLock) - - const lockedBalanceAfterUnlock = await gov.lockedBalance(sender.address) - expect(lockedBalanceAfterUnlock).to.be.equal(0) - }) - }) - - afterEach(async () => { - await revertSnapshot(snapshotId) - snapshotId = await takeSnapshot() - }) -})