tornado-governance/test/governance.test.js

723 lines
31 KiB
JavaScript

/* global artifacts, web3, contract */
require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should()
const util = require('ethereumjs-util')
const Governance = artifacts.require('./MockGovernance.sol')
const Dummy = artifacts.require('./Dummy.sol')
const Proposal = artifacts.require('./Proposal.sol')
const Torn = artifacts.require('./TORNMock.sol')
const TransparentUpgradeableProxy = artifacts.require('./MockProxy.sol')
const ProposalStateChangeGovernance = artifacts.require('./ProposalStateChangeGovernance.sol')
const NewImplementation = artifacts.require('./NewImplementation.sol')
const ProposalUpgrade = artifacts.require('./ProposalUpgrade.sol')
const { PermitSigner } = require('../lib/Permit')
const { toBN, toChecksumAddress } = require('web3-utils')
const { takeSnapshot, revertSnapshot } = require('../scripts/ganacheHelper')
const BN = require('bn.js')
const tornConfig = require('torn-token')
const RLP = require('rlp')
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)
},
}
async function getNextAddr(sender, offset = 0) {
const nonce = await web3.eth.getTransactionCount(sender)
return (
'0x' +
web3.utils
.sha3(RLP.encode([sender, Number(nonce) + Number(offset)]))
.slice(12)
.substring(14)
)
}
contract('Governance', (accounts) => {
let governance, dummy
let proposer = accounts[3]
let secondProposer = accounts[8]
let snapshotId
let timestamp = 1577836800 // 01/01/2020 00:00
let torn
let chainId
let domain
let votingDelay
let votingPeriod
let executionExpiration
let executionDelay
let extendTime
let proposalStartTime
let proposalEndTime
let lockingPeriod
let balanceProposer
const cap = toBN(tornConfig.torn.cap)
const tenThousandTorn = toBN(10).pow(toBN(18)).mul(toBN(10000))
const miningPrivateKey = '0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
const miningPublicKey = toChecksumAddress(
'0x' + util.privateToAddress(Buffer.from(miningPrivateKey.slice(2), 'hex')).toString('hex'),
)
before(async () => {
chainId = await web3.eth.net.getId()
const governanceExpectedAddr = await getNextAddr(accounts[0], 2)
torn = await Torn.new(governanceExpectedAddr, duration.days(30), [
{ to: miningPublicKey, amount: cap.toString() },
])
const governanceImplementation = await Governance.new()
const calldata = governanceImplementation.contract.methods.initialize(torn.address).encodeABI()
const proxy = await TransparentUpgradeableProxy.new(governanceImplementation.address, calldata)
governance = await Governance.at(proxy.address)
dummy = await Dummy.new()
balanceProposer = cap.div(toBN(4))
await torn.transfer(secondProposer, balanceProposer.div(toBN(2)), { from: miningPublicKey })
await torn.transfer(proposer, balanceProposer, { from: miningPublicKey })
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 = new BN(timestamp).add(votingDelay)
proposalEndTime = votingPeriod.add(toBN(proposalStartTime))
lockingPeriod = Number(extendTime) + Number(executionExpiration) + Number(executionDelay)
domain = {
name: await torn.name(),
version: '1',
chainId,
verifyingContract: torn.address,
}
snapshotId = await takeSnapshot()
})
beforeEach(async () => {
await torn.approve(governance.address, cap.div(toBN(4)), { from: proposer })
await governance.lockWithApproval(cap.div(toBN(4)), { from: proposer })
const balance = await governance.lockedBalance(proposer)
balance.should.be.eq.BN(cap.div(toBN(4)))
})
describe('#constructor', () => {
it('should work', async () => {
const proposalCount = await governance.proposalCount()
proposalCount.should.be.eq.BN(0)
const p = await governance.proposals(0)
p.proposer.should.be.equal(governance.address)
p.target.should.be.equal('0x000000000000000000000000000000000000dEaD')
p.endTime.should.be.eq.BN(toBN(0))
p.forVotes.should.be.eq.BN(toBN(0))
p.againstVotes.should.be.eq.BN(toBN(0))
p.executed.should.be.equal(true)
p.extended.should.be.equal(false)
})
})
describe('#propose', () => {
it('should work', async () => {
const { logs } = await governance.propose(dummy.address, 'dummy', { from: proposer })
const id = await governance.latestProposalIds(proposer)
const proposalCount = await governance.proposalCount()
proposalCount.should.be.eq.BN(1)
const proposal = await governance.proposals(id)
proposal.proposer.should.be.equal(proposer)
proposal.startTime.should.be.eq.BN(proposalStartTime)
proposal.endTime.should.be.eq.BN(proposalEndTime)
proposal.forVotes.should.be.eq.BN(0)
proposal.againstVotes.should.be.eq.BN(0)
proposal.executed.should.be.equal(false)
// emit ProposalCreated(newProposal.id, msg.sender, target, startBlock, endBlock, description);
logs[0].event.should.be.equal('ProposalCreated')
logs[0].args.id.should.be.eq.BN(id)
logs[0].args.proposer.should.be.eq.BN(proposer)
logs[0].args.target.should.be.eq.BN(dummy.address)
logs[0].args.description.should.be.eq.BN('dummy')
logs[0].args.startTime.should.be.eq.BN(proposalStartTime)
logs[0].args.endTime.should.be.eq.BN(votingPeriod.add(toBN(proposalStartTime)))
let state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Pending)
await governance.setTimestamp(proposalEndTime)
state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
const accountLock = await governance.canWithdrawAfter(proposer)
accountLock.should.be.eq.BN(proposalEndTime.add(toBN(lockingPeriod)))
})
it('fails if target is not a contract', async () => {
await governance
.propose(accounts[9], 'dummy', { from: proposer })
.should.be.rejectedWith('not a contract')
})
it('fails if proposer has already pending proposal', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
await governance
.propose(dummy.address, 'dummy', { from: proposer })
.should.be.rejectedWith(
'Governance::propose: one live proposal per proposer, found an already active proposal',
)
await governance.setTimestamp(proposalEndTime)
await governance
.propose(dummy.address, 'dummy', { from: proposer })
.should.be.rejectedWith(
'Governance::propose: one live proposal per proposer, found an already active proposal',
)
})
it('fails if proposer does not have voting power', async () => {
const voterBob = accounts[5]
const tenThousandTorn = toBN(10).pow(toBN(18)).mul(toBN(999))
await torn.transfer(voterBob, tenThousandTorn, { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterBob })
await governance.lockWithApproval(tenThousandTorn, { from: voterBob })
await governance
.propose(dummy.address, 'dummy', { from: voterBob })
.should.be.rejectedWith('Governance::propose: proposer votes below proposal threshold.')
})
})
describe('#castVote', () => {
it('should work if support is true', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
const votesCount = balanceProposer
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
const { logs } = await governance.castVote(id, true, { from: proposer })
logs[0].event.should.be.equal('Voted')
logs[0].args.voter.should.be.equal(proposer)
logs[0].args.proposalId.should.be.eq.BN(id)
logs[0].args.support.should.be.equal(true)
logs[0].args.votes.should.be.eq.BN(votesCount)
await governance.getReceipt(id, proposer)
const proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(votesCount)
proposal.againstVotes.should.be.eq.BN(0)
})
it('should work if support is false', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
const votesCount = balanceProposer
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
const { logs } = await governance.castVote(id, false, { from: proposer })
logs[0].event.should.be.equal('Voted')
logs[0].args.voter.should.be.equal(proposer)
logs[0].args.proposalId.should.be.eq.BN(id)
logs[0].args.support.should.be.equal(false)
logs[0].args.votes.should.be.eq.BN(votesCount)
const proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(0)
proposal.againstVotes.should.be.eq.BN(votesCount)
})
it('should be able to change the choice later if already voted before', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
const votesCount = balanceProposer
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, false, { from: proposer })
await governance.castVote(id, true, { from: proposer })
const { logs } = await governance.castVote(id, false, { from: proposer })
logs[0].event.should.be.equal('Voted')
logs[0].args.voter.should.be.equal(proposer)
logs[0].args.proposalId.should.be.eq.BN(id)
logs[0].args.support.should.be.equal(false)
logs[0].args.votes.should.be.eq.BN(votesCount)
const proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(0)
proposal.againstVotes.should.be.eq.BN(votesCount)
})
it('should work if there are multiple voters', async () => {
const voterBob = accounts[5]
const voterAlice = accounts[7]
const tenThousandTorn = toBN(10).pow(toBN(18)).mul(toBN(10000)) // todo
await torn.transfer(voterBob, tenThousandTorn, { from: miningPublicKey })
await torn.transfer(voterAlice, tenThousandTorn.mul(toBN(2)), { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterBob })
await torn.approve(governance.address, tenThousandTorn.mul(toBN(2)), { from: voterAlice })
await governance.lockWithApproval(tenThousandTorn, { from: voterBob })
await governance.lockWithApproval(tenThousandTorn.mul(toBN(2)), { from: voterAlice })
await governance.propose(dummy.address, 'dummy', { from: proposer })
const votesCount = balanceProposer
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, false, { from: proposer })
await governance.castVote(id, false, { from: voterBob })
await governance.castVote(id, true, { from: voterAlice })
const proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(tenThousandTorn.mul(toBN(2)))
proposal.againstVotes.should.be.eq.BN(votesCount.add(tenThousandTorn))
})
it('fails if voter does not have voting power', async () => {
const voterBob = accounts[5]
await governance.propose(dummy.address, 'dummy', { from: proposer })
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance
.castVote(id, false, { from: voterBob })
.should.be.rejectedWith('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 = accounts[5]
const tenThousandTorn = toBN(10).pow(toBN(18)).mul(toBN(10000)) // todo
const fiveThousandTorn = tenThousandTorn.div(toBN(2))
await torn.transfer(voterBob, tenThousandTorn, { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterBob })
await governance.lockWithApproval(fiveThousandTorn, { from: voterBob })
await governance.propose(dummy.address, 'dummy', { from: proposer })
const votesCount = balanceProposer
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, false, { from: proposer })
await governance.castVote(id, false, { from: voterBob })
let proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(toBN(0))
proposal.againstVotes.should.be.eq.BN(votesCount.add(fiveThousandTorn))
await governance.lockWithApproval(fiveThousandTorn, { from: voterBob })
await governance.castVote(id, false, { from: voterBob })
proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(toBN(0))
proposal.againstVotes.should.be.eq.BN(votesCount.add(tenThousandTorn))
})
it('extends time if the vote changes the outcome during the CLOSING_PERIOD', async () => {
const voterBob = accounts[5]
const voterAlice = accounts[7]
await torn.transfer(voterBob, tenThousandTorn, { from: miningPublicKey })
await torn.transfer(voterAlice, tenThousandTorn.mul(toBN(2)), { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterBob })
await torn.approve(governance.address, tenThousandTorn.mul(toBN(2)), { from: voterAlice })
await governance.lockWithApproval(tenThousandTorn, { from: voterBob })
await governance.lockWithApproval(tenThousandTorn.mul(toBN(2)), { from: voterAlice })
await governance.propose(dummy.address, 'dummy', { from: proposer })
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalStartTime.add(toBN(1)))
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, false, { from: voterBob })
await governance.castVote(id, true, { from: voterAlice })
let MAX_EXTENDED_TIME = await governance.VOTE_EXTEND_TIME()
let proposal = await governance.proposals(id)
proposal.endTime.should.be.eq.BN(proposalEndTime)
await governance.setTimestamp(proposalEndTime)
await governance.castVote(id, false, { from: proposer })
proposal = await governance.proposals(id)
proposal.endTime.should.be.eq.BN(proposalEndTime.add(MAX_EXTENDED_TIME))
await governance.setTimestamp(proposalEndTime.add(toBN(duration.hours(5))))
const stateAfter = await governance.state(id)
stateAfter.should.be.eq.BN(ProposalState.Active)
})
it('locks tokens after vote', async () => {
const voterAlice = accounts[7]
await torn.transfer(voterAlice, tenThousandTorn, { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterAlice })
await governance.lockWithApproval(tenThousandTorn, { from: voterAlice })
await governance.propose(dummy.address, 'dummy', { from: proposer })
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalStartTime.add(toBN(1)))
const state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
const lockBefore = await governance.canWithdrawAfter(voterAlice)
lockBefore.should.be.eq.BN(toBN(0))
await governance.castVote(id, true, { from: voterAlice })
const lockAfter = await governance.canWithdrawAfter(voterAlice)
lockAfter.should.be.eq.BN(proposalEndTime.add(toBN(lockingPeriod)))
})
it('does not reduce lock time', async () => {
const voterAlice = accounts[7]
await torn.transfer(voterAlice, tenThousandTorn, { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterAlice })
await governance.lockWithApproval(tenThousandTorn, { from: voterAlice })
await torn.approve(governance.address, balanceProposer.div(toBN(2)), { from: secondProposer })
await governance.lockWithApproval(balanceProposer.div(toBN(2)), { from: secondProposer })
await governance.propose(dummy.address, 'dummy', { from: proposer })
const id1 = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime.sub(votingDelay).sub(toBN(1)))
await governance.propose(dummy.address, 'dummy2', { from: secondProposer })
const id2 = await governance.latestProposalIds(secondProposer)
await governance.setTimestamp(proposalEndTime)
const state1 = await governance.state(id1)
state1.should.be.eq.BN(ProposalState.Active)
const state2 = await governance.state(id2)
state2.should.be.eq.BN(ProposalState.Active)
const lockBefore = await governance.canWithdrawAfter(voterAlice)
lockBefore.should.be.eq.BN(toBN(0))
await governance.castVote(id2, true, { from: voterAlice })
const lockAfter1 = await governance.canWithdrawAfter(voterAlice)
await governance.castVote(id1, true, { from: voterAlice })
const lockAfter2 = await governance.canWithdrawAfter(voterAlice)
lockAfter1.should.be.eq.BN(lockAfter2)
})
})
describe('#execute', () => {
let proposal
before(async () => {
proposal = await Proposal.new()
})
it('should work', async () => {
await governance.propose(proposal.address, 'proposal', { from: proposer })
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalStartTime.add(toBN(1)))
let state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, true, { from: proposer })
await governance.setTimestamp(proposalEndTime.add(toBN(executionDelay).add(toBN(duration.days(1)))))
const receipt = await governance.execute(id)
const debugLog = receipt.receipt.rawLogs[0]
const decodedLog = web3.eth.abi.decodeLog(
[
{
type: 'address',
name: 'output',
},
],
debugLog.data,
debugLog.topics[0],
)
const newDummy = await Dummy.at(decodedLog.output)
const dummyText = await newDummy.text()
dummyText.should.be.equal('dummy')
receipt.logs[0].event.should.be.equal('ProposalExecuted')
})
})
describe('#lock', () => {
let owner = miningPublicKey
let tokensAmount = toBN(10).pow(toBN(21)).mul(toBN(1337))
it('permitClass works', async () => {
const args = {
owner,
spender: governance.address,
value: tokensAmount,
nonce: '0x00',
deadline: new BN('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)
address.should.be.equal(signer)
})
it('calls approve if signature is valid', async () => {
const chainIdFromContract = await torn.chainId()
chainIdFromContract.should.be.eq.BN(new BN(domain.chainId))
const args = {
owner,
spender: governance.address,
value: tokensAmount,
nonce: 0,
deadline: new BN('5609459200'),
}
const permitSigner = new PermitSigner(domain, args)
const signature = await permitSigner.getSignature(miningPrivateKey)
const signer = await permitSigner.getSignerAddress(args, signature.hex)
signer.should.be.equal(miningPublicKey)
const balanceBefore = await torn.balanceOf(governance.address)
const lockedBalanceBefore = await governance.lockedBalance(owner)
await governance.lock(
args.owner,
// args.spender,
args.value.toString(),
args.deadline.toString(),
signature.v,
signature.r,
signature.s,
{ from: owner },
)
const balanceAfter = await torn.balanceOf(governance.address)
const lockedBalanceAfter = await governance.lockedBalance(owner)
balanceAfter.should.be.eq.BN(balanceBefore.add(args.value))
lockedBalanceAfter.should.be.eq.BN(lockedBalanceBefore.add(args.value))
})
it('adds up tokens if already existing', async () => {
const voterBob = accounts[5]
const tenThousandTorn = toBN(10).pow(toBN(18)).mul(toBN(10000)) // todo
await torn.transfer(voterBob, tenThousandTorn, { from: miningPublicKey })
await torn.approve(governance.address, tenThousandTorn, { from: voterBob })
await governance.lockWithApproval(tenThousandTorn.div(toBN(2)), { from: voterBob })
await governance.lockWithApproval(tenThousandTorn.div(toBN(2)), { from: voterBob })
const balanceAfter = await torn.balanceOf(voterBob)
const lockedBalanceAfter = await governance.lockedBalance(voterBob)
balanceAfter.should.be.eq.BN(toBN(0))
lockedBalanceAfter.should.be.eq.BN(tenThousandTorn)
})
})
describe('#unlock', () => {
it('should work if there is no activity made', async () => {
const balanceBeforeTorn = await torn.balanceOf(proposer)
const balanceBefore = await governance.lockedBalance(proposer)
await governance.unlock(balanceProposer, { from: proposer })
const balanceAfterTorn = await torn.balanceOf(proposer)
const balanceAfter = await governance.lockedBalance(proposer)
balanceBefore.should.be.eq.BN(balanceAfter.add(balanceProposer))
balanceAfterTorn.should.be.eq.BN(balanceBeforeTorn.add(balanceProposer))
})
it('fails if asking more than balance', async () => {
await governance
.unlock(balanceProposer + 1, { from: proposer })
.should.be.rejectedWith('Governance: insufficient balance')
//todo check lockedBalance
})
it('fail if there is active proposal', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
await governance
.unlock(balanceProposer, { from: proposer })
.should.be.rejectedWith('Governance: tokens are locked')
})
it('unlock if there proposals expired', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
await governance.setTimestamp(proposalEndTime.add(toBN(lockingPeriod + duration.minutes(1))))
await governance.unlock(balanceProposer, { from: proposer })
})
})
describe('#undelegate', () => {
it('should work', async () => {
let delegatee = accounts[5]
await governance.delegate(delegatee, { from: proposer })
const { logs } = await governance.undelegate({ from: proposer })
logs[0].args.account.should.be.equal(proposer)
logs[0].args.from.should.be.equal(delegatee)
})
})
describe('#delegate', () => {
it('should work', async () => {
let delegatee = accounts[5]
let vp = await governance.delegatedTo(proposer)
vp.should.be.equal('0x0000000000000000000000000000000000000000')
await governance.delegate(delegatee, { from: proposer })
vp = await governance.delegatedTo(proposer)
vp.should.be.equal(delegatee)
})
it('emits undelegate event if delegate called with non empty delegateTo', async () => {
let delegatee = accounts[5]
let delegateeSecond = accounts[6]
const receipt = await governance.delegate(delegatee, { from: proposer })
receipt.logs.length.should.be.equal(1)
await governance
.delegate(delegatee, { from: proposer })
.should.be.rejectedWith('Governance: invalid delegatee')
const receiptTwo = await governance.delegate(delegateeSecond, { from: proposer })
receiptTwo.logs.length.should.be.equal(2)
receiptTwo.logs[0].event.should.be.equal('Undelegated')
receiptTwo.logs[0].args.account.should.be.equal(proposer)
receiptTwo.logs[0].args.from.should.be.equal(delegatee)
receiptTwo.logs[1].event.should.be.equal('Delegated')
receiptTwo.logs[1].args.account.should.be.equal(proposer)
receiptTwo.logs[1].args.to.should.be.equal(delegateeSecond)
const vp = await governance.delegatedTo(proposer)
vp.should.be.equal(delegateeSecond)
})
it('can propose with delegated votes', async () => {
let delegatee = accounts[5]
await governance.delegate(delegatee, { from: proposer })
await governance.proposeByDelegate(proposer, dummy.address, 'dummy', { from: delegatee })
const proposalCount = await governance.proposalCount()
proposalCount.should.be.eq.BN(1)
const latestProposalId = await governance.latestProposalIds(proposer)
latestProposalId.should.be.eq.BN(1)
const proposal = await governance.proposals(1)
proposal.proposer.should.be.equal(proposer)
})
it('can vote with delegated votes', async () => {
let delegatee = accounts[5]
await governance.delegate(delegatee, { from: proposer })
await governance.propose(dummy.address, 'dummy', { from: proposer })
const votesCount = balanceProposer
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalEndTime)
await governance.castDelegatedVote([proposer], id, true, { from: delegatee })
await governance.getReceipt(id, proposer)
let proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(votesCount)
proposal.againstVotes.should.be.eq.BN(0)
await governance.castVote(id, false, { from: proposer })
await governance.getReceipt(id, proposer)
proposal = await governance.proposals(id)
proposal.forVotes.should.be.eq.BN(0)
proposal.againstVotes.should.be.eq.BN(votesCount)
})
})
describe.skip('#getAllProposals', () => {
it('fetches proposals', async () => {
await governance.propose(dummy.address, 'dummy', { from: proposer })
await governance.setTimestamp(proposalEndTime)
const proposals = await governance.getAllProposals(0, 0)
const proposal = proposals[0]
proposal.id.should.be.eq.BN(1)
proposal.proposer.should.be.equal(proposer)
proposal.startTime.should.be.eq.BN(proposalStartTime)
proposal.endTime.should.be.eq.BN(proposalEndTime)
proposal.forVotes.should.be.eq.BN(0)
proposal.againstVotes.should.be.eq.BN(0)
proposal.executed.should.be.equal(false)
proposal.state.should.be.eq.BN(ProposalState.Active)
})
})
describe.skip('#getBalances', () => {
it('fetches lockedBalance', async () => {
const lockedBalanceOne = await governance.getBalances([proposer, secondProposer])
lockedBalanceOne.should.be.eq.BN([balanceProposer, toBN('0')])
await torn.approve(governance.address, balanceProposer.div(toBN(2)), { from: secondProposer })
await governance.lockWithApproval(balanceProposer.div(toBN(2)), { from: secondProposer })
const lockedBalance = await governance.getBalances([proposer, secondProposer])
lockedBalance.should.be.eq.BN([balanceProposer, balanceProposer.div(toBN(2))])
})
})
describe('#upgrades', () => {
it('allows to change variable state', async () => {
const proposal = await ProposalStateChangeGovernance.new()
await governance.propose(proposal.address, 'proposal', { from: proposer })
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalStartTime.add(toBN(1)))
let state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, true, { from: proposer })
await governance.setTimestamp(proposalEndTime.add(toBN(executionDelay).add(toBN(duration.days(1)))))
const EXECUTION_DELAY_BEFORE = await governance.EXECUTION_DELAY()
EXECUTION_DELAY_BEFORE.should.be.eq.BN(duration.days(2))
const receipt = await governance.execute(id)
const EXECUTION_DELAY_AFTER = await governance.EXECUTION_DELAY()
EXECUTION_DELAY_AFTER.should.be.eq.BN(duration.days(3))
receipt.logs[0].event.should.be.equal('ProposalExecuted')
})
it('upgrades implementation with variables change', async () => {
await NewImplementation.new({ from: accounts[9] })
const proposal = await ProposalUpgrade.new()
// console.log(newImpl.address) // 0xF7E3e47e06F1bDDecb1b2F3a7F60b6b25fd2e233
await governance.propose(proposal.address, 'proposal', { from: proposer })
const id = await governance.latestProposalIds(proposer)
await governance.setTimestamp(proposalStartTime.add(toBN(1)))
let state = await governance.state(id)
state.should.be.eq.BN(ProposalState.Active)
await governance.castVote(id, true, { from: proposer })
await governance.setTimestamp(proposalEndTime.add(toBN(executionDelay).add(toBN(duration.days(1)))))
const newGovernance = await NewImplementation.at(governance.address)
const receipt = await governance.execute(id)
let newVariable = await newGovernance.newVariable()
newVariable.should.be.eq.BN(0)
const receiptExecute = await newGovernance.execute(123)
newVariable = await newGovernance.newVariable()
newVariable.should.be.eq.BN(999)
receipt.logs[0].event.should.be.equal('ProposalExecuted')
receiptExecute.logs[0].event.should.be.equal('Overriden')
})
it('cannot initialize implementation contract', async () => {
const impl = await NewImplementation.new({ from: accounts[9] })
await impl
.initialize(accounts[9])
.should.be.rejectedWith('Contract instance has already been initialized')
})
it('cannot destroy implementation contract')
})
afterEach(async () => {
await revertSnapshot(snapshotId.result)
// eslint-disable-next-line require-atomic-updates
snapshotId = await takeSnapshot()
})
})