Barebones test works
Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
parent
0e5cb316b0
commit
14f9e379ec
@ -1,3 +1,4 @@
|
|||||||
ETHERSCAN_KEY=
|
ETHERSCAN_KEY=
|
||||||
ALCHEMY_KEY=
|
RPC_URL=
|
||||||
|
PK=
|
||||||
use_latest_block=false
|
use_latest_block=false
|
||||||
|
@ -2,5 +2,4 @@ module.exports = {
|
|||||||
governance: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
governance: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||||
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||||
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
|
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
|
||||||
forkBlockNumber: 14352372,
|
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,11 @@ contract GovernancePatchUpgrade is GovernanceStakingUpgrade {
|
|||||||
if (proposalCodehash == proposalCodehashes[proposalId]) {
|
if (proposalCodehash == proposalCodehashes[proposalId]) {
|
||||||
super.execute(proposalId);
|
super.execute(proposalId);
|
||||||
} else {
|
} else {
|
||||||
|
// Note that this is the easiest way to block further execution
|
||||||
proposal.executed = true;
|
proposal.executed = true;
|
||||||
emit CodehashDifferent(proposal.target, proposalCodehashes[proposalId], proposalCodehash);
|
|
||||||
|
// Let the event signify it was broken
|
||||||
|
emit CodehashDifferent(target, proposalCodehashes[proposalId], proposalCodehash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,17 +10,48 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol";
|
|||||||
|
|
||||||
import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol";
|
import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol";
|
||||||
import { TornadoStakingRewards } from "./TornadoStakingRewards.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 SafeMath for uint256;
|
||||||
using Address for address;
|
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);
|
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||||
|
|
||||||
address public immutable registry;
|
ProposalContractFactory public immutable proposalContractFactory;
|
||||||
|
|
||||||
constructor(address _registry) public {
|
constructor(address _proposalContractFactory) public {
|
||||||
registry = _registry;
|
proposalContractFactory = ProposalContractFactory(_proposalContractFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aight lets do this sirs
|
// Aight lets do this sirs
|
||||||
@ -35,11 +66,25 @@ contract RelayerRegistryProposal {
|
|||||||
// Get the old staking contract
|
// Get the old staking contract
|
||||||
TornadoStakingRewards oldStaking = TornadoStakingRewards(address(GovernancePatchUpgrade(governance).Staking()));
|
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)));
|
oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking)));
|
||||||
|
|
||||||
// And create a new staking contract
|
// 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
|
// Now upgrade the governance to the latest stuff
|
||||||
LoopbackProxy(payable(governance)).upgradeTo(address(new GovernancePatchUpgrade(address(newStaking), gasComp, vault)));
|
LoopbackProxy(payable(governance)).upgradeTo(address(new GovernancePatchUpgrade(address(newStaking), gasComp, vault)));
|
||||||
|
@ -139,14 +139,14 @@ contract RelayerRegistry is Initializable, EnsResolve {
|
|||||||
address _torn,
|
address _torn,
|
||||||
address _governance,
|
address _governance,
|
||||||
address _ens,
|
address _ens,
|
||||||
bytes32 _staking,
|
address _staking,
|
||||||
bytes32 _feeManager
|
address _feeManager
|
||||||
) public {
|
) public {
|
||||||
torn = TORN(_torn);
|
torn = TORN(_torn);
|
||||||
governance = _governance;
|
governance = _governance;
|
||||||
ens = IENS(_ens);
|
ens = IENS(_ens);
|
||||||
staking = TornadoStakingRewards(resolve(_staking));
|
staking = TornadoStakingRewards(_staking);
|
||||||
feeManager = IFeeManager(resolve(_feeManager));
|
feeManager = IFeeManager(_feeManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,22 +25,13 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
version: '0.8.20',
|
|
||||||
settings: {
|
|
||||||
optimizer: {
|
|
||||||
enabled: true,
|
|
||||||
runs: 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
networks: {
|
networks: {
|
||||||
hardhat: {
|
hardhat: {
|
||||||
forking: {
|
forking: {
|
||||||
url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
url: `${process.env.RPC_URL}`,
|
||||||
blockNumber: config.forkBlockNumber,
|
timeout: 2147483647,
|
||||||
},
|
},
|
||||||
initialBaseFeePerGas: 5,
|
initialBaseFeePerGas: 5,
|
||||||
},
|
},
|
||||||
@ -49,8 +40,8 @@ module.exports = {
|
|||||||
timeout: 120000,
|
timeout: 120000,
|
||||||
},
|
},
|
||||||
mainnet: {
|
mainnet: {
|
||||||
url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
url: `${process.env.RPC_URL}`,
|
||||||
accounts: ['900e9f0e8ce24c022026649c48a059fb6ffa0a2523811d797b47d789bf106def'], // random pk off keys.lol
|
//accounts: [`${process.env.PK}`],
|
||||||
timeout: 2147483647,
|
timeout: 2147483647,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,7 @@ async function propose(proposalArgs) {
|
|||||||
'contracts/v1/Governance.sol:Governance',
|
'contracts/v1/Governance.sol:Governance',
|
||||||
'0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
'0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||||
)
|
)
|
||||||
GovernanceContract = await GovernanceContract.connect(proposer)
|
GovernanceContract = GovernanceContract.connect(proposer)
|
||||||
|
|
||||||
const response = await GovernanceContract.propose(ProposalContract.address, proposalArgs[2])
|
const response = await GovernanceContract.propose(ProposalContract.address, proposalArgs[2])
|
||||||
|
|
||||||
|
7
scripts/layout.js
Normal file
7
scripts/layout.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const hre = require('hardhat')
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await hre.storageLayout.export()
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
188
test/patch/patch.test.js
Normal file
188
test/patch/patch.test.js
Normal file
@ -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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
Loading…
Reference in New Issue
Block a user