tornado-governance/test/v1/governance.v1.test.js

943 lines
33 KiB
JavaScript

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 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('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 : 14042331,
},
},
])
})
})