proposal-46/test/Governance.js
2024-01-31 10:12:30 +00:00

353 lines
16 KiB
JavaScript

const { ethers, network } = require("hardhat");
const { time } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
const { expect } = require("chai");
const {
deployAndExecuteProposal,
getProxyImplAddr,
governanceAddr,
resetStateBeforeProposal,
getManyEth,
resolveAddr,
getTorn,
getGovernance,
getPermitSignature,
tornAddr,
stakingAddr,
executeNewProposal,
signerLockInGov,
} = require("./utils");
// comment some checks if your proposal changes something
describe("All Governance checks", function () {
beforeEach(resetStateBeforeProposal);
it("Governance implementation should be a valid contract", async function () {
await deployAndExecuteProposal();
const governanceImplAddr = await getProxyImplAddr(governanceAddr);
const code = await ethers.provider.getCode(governanceImplAddr);
expect(code).to.not.be.equal("0x");
});
async function createValidProposal() {
// this proposal changes governance impl addr to old governance impl 0xBa178126C28F50Ee60322a82f5EbCd6b3711e101
const proposalBytecode =
"0x608060405234801561001057600080fd5b50610161806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063373058b814610030575b600080fd5b61003861003a565b005b735efda50f22d34f262c29268506c5fa42cb56a1ce73ffffffffffffffffffffffffffffffffffffffff16633659cfe673ba178126c28f50ee60322a82f5ebcd6b3711e1016040518263ffffffff1660e01b815260040161009b9190610110565b600060405180830381600087803b1580156100b557600080fd5b505af11580156100c9573d6000803e3d6000fd5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100fa826100cf565b9050919050565b61010a816100ef565b82525050565b60006020820190506101256000830184610101565b9291505056fea264697066735822122071ee22910809ebd1174acbe4a8ce386abb553bbc1cf37fd7c9abb166b18ca6f664736f6c63430008140033";
const signer = (await ethers.getSigners())[0];
const proposalAbi = ["function executeProposal()"];
const proposalFactory = new ethers.ContractFactory(proposalAbi, proposalBytecode, signer);
const proposalContract = await proposalFactory.deploy();
const proposalAddr = await proposalContract.getAddress();
return proposalAddr;
}
it("Governance contract should be updatable", async function () {
await deployAndExecuteProposal();
const someOldGovernanceImpl = "0xBa178126C28F50Ee60322a82f5EbCd6b3711e101";
const proposalAddr = await createValidProposal();
await executeNewProposal(proposalAddr);
const governance = await getGovernance();
const lastProposalId = await governance.proposalCount();
expect(await governance.state(lastProposalId)).to.be.equal(5); // executed
expect(await getProxyImplAddr(governanceAddr)).to.be.equal(someOldGovernanceImpl);
});
it("Proposal count should change by one", async function () {
const governance = await getGovernance();
const lastProposal = await governance.proposalCount();
await deployAndExecuteProposal();
expect(await governance.proposalCount()).to.be.equal(lastProposal + 1n);
});
it("Stakers should be able to lock and unlock", async function () {
const signer = (await ethers.getSigners())[0];
const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr);
const governance = await getGovernance(signer);
const torn = await getTorn(governanceSigner);
const thousandTorns = 1000n * 10n ** 18n;
const balanceBefore = await torn.balanceOf(signer.address);
const lockedBalanceBefore = await governance.lockedBalance(signer.address);
await deployAndExecuteProposal();
await torn.transfer(signer.address, thousandTorns);
const deadline = ethers.MaxUint256;
const { v, r, s } = await getPermitSignature(signer, torn, governanceAddr, thousandTorns, deadline);
await governance.lock(signer.address, thousandTorns, deadline, v, r, s);
const lockedBalanceAfter = await governance.lockedBalance(signer.address);
expect(lockedBalanceAfter - lockedBalanceBefore).to.be.equal(thousandTorns);
await governance.unlock(thousandTorns);
const balanceAfter = await torn.balanceOf(signer.address);
expect(balanceAfter - balanceBefore).to.be.equal(thousandTorns);
});
it("Staker locked balance should not change", async function () {
const me = await resolveAddr("butterfly-attractor.eth");
const governance = await getGovernance();
const lockedBalanceBefore = await governance.lockedBalance(me);
await deployAndExecuteProposal();
expect(await governance.lockedBalance(me)).to.be.equal(lockedBalanceBefore);
});
it("Proposals info should not change", async function () {
const governance = await getGovernance();
const lastProposal = await governance.proposalCount();
const proposalsBefore = await Promise.all(
Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.proposals(i)),
);
await deployAndExecuteProposal();
const proposalsAfter = await Promise.all(
Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.proposals(i)),
);
expect(proposalsAfter).to.deep.equal(proposalsBefore);
});
// it("Proposals state should not change", async function(){
// const governance = await getGovernance();
// const lastProposal = await governance.proposalCount();
// const proposalStatesBefore = await Promise.all(
// Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.state(i)),
// );
// await deployAndExecuteProposal();
// const proposalStatesAfter = await Promise.all(
// Array.from({ length: Number(lastProposal) - 1 }, (_, i) => i + 1).map((i) => governance.state(i)),
// );
// expect(proposalStatesAfter).to.deep.equal(proposalStatesBefore);
// })
it("Quorum votes should not change", async function () {
const governance = await getGovernance();
const quorum = await governance.QUORUM_VOTES();
await deployAndExecuteProposal();
expect(await governance.QUORUM_VOTES()).to.be.equal(quorum);
});
it("Closing period should not change", async function () {
const governance = await getGovernance();
const closingPeriod = await governance.CLOSING_PERIOD();
await deployAndExecuteProposal();
expect(await governance.CLOSING_PERIOD()).to.be.equal(closingPeriod);
});
it("Execution delay should not change", async function () {
const governance = await getGovernance();
const executionDelay = await governance.EXECUTION_DELAY();
await deployAndExecuteProposal();
expect(await governance.EXECUTION_DELAY()).to.be.equal(executionDelay);
});
it("Execution expiration should not change", async function () {
const governance = await getGovernance();
const expirationPeriod = await governance.EXECUTION_EXPIRATION();
await deployAndExecuteProposal();
expect(await governance.EXECUTION_EXPIRATION()).to.be.equal(expirationPeriod);
});
it("Proposal initiation threshold should not change", async function () {
const governance = await getGovernance();
const proposalThreshold = await governance.PROPOSAL_THRESHOLD();
await deployAndExecuteProposal();
expect(await governance.PROPOSAL_THRESHOLD()).to.be.equal(proposalThreshold);
});
it("Voting extend time should not change", async function () {
const governance = await getGovernance();
const votingExtendTime = await governance.VOTE_EXTEND_TIME();
await deployAndExecuteProposal();
expect(await governance.VOTE_EXTEND_TIME()).to.be.equal(votingExtendTime);
});
it("Voting initiation delay should not change", async function () {
const governance = await getGovernance();
const proposalVotingDelay = await governance.VOTING_DELAY();
await deployAndExecuteProposal();
expect(await governance.VOTING_DELAY()).to.be.equal(proposalVotingDelay);
});
it("Voting period should not change", async function () {
const governance = await getGovernance();
const votingPeriod = await governance.VOTING_PERIOD();
await deployAndExecuteProposal();
expect(await governance.VOTING_PERIOD()).to.be.equal(votingPeriod);
});
it("User vault address should not change", async function () {
const governance = await getGovernance();
const vaultAddr = await governance.userVault();
const vaultCode = await ethers.provider.getCode(vaultAddr);
expect(vaultCode).to.be.not.equal("0x");
await deployAndExecuteProposal();
expect(await governance.userVault()).to.be.equal(vaultAddr);
expect(await ethers.provider.getCode(vaultAddr)).to.be.equal(vaultCode);
});
it("Torn address should not be reinitialized", async function () {
const { governanceContract } = await deployAndExecuteProposal();
expect(await governanceContract.torn()).to.be.equal(tornAddr);
});
it("Staking address should not change", async function () {
const { governanceContract } = await deployAndExecuteProposal();
expect(await governanceContract.Staking()).to.be.equal(stakingAddr);
});
it("Gas compensator address should not change", async function () {
const governance = await getGovernance();
const gasCompensatorAddr = await governance.gasCompensationVault();
await deployAndExecuteProposal();
expect(await governance.gasCompensationVault()).to.be.equal(gasCompensatorAddr);
});
async function delegate(tornAmount, signer) {
const governance = await getGovernance();
if (!tornAmount) tornAmount = await governance.QUORUM_VOTES();
if (!signer) signer = (await ethers.getSigners())[0];
const governanceContract = await getGovernance(signer);
const zer0 = "0x000000000000000000000000000000000000dEaD";
await signerLockInGov(tornAmount, signer);
await governanceContract.delegate(zer0);
return { signer, delegated: await ethers.getImpersonatedSigner(zer0), governance: governanceContract };
}
it("Delegators state should not change", async function () {
await deployAndExecuteProposal();
const { signer, governance, delegated } = await delegate();
expect(await governance.delegatedTo(signer.address)).to.be.equal(delegated.address);
});
it("Delegation should work", async function () {
await deployAndExecuteProposal();
const { signer, governance, delegated } = await delegate();
expect(await governance.delegatedTo(signer.address)).to.be.equal(delegated.address);
});
it("Delegator should be able to cast vote", async function () {
await deployAndExecuteProposal();
const proposalAddr = await createValidProposal();
const gov = await getGovernance();
await gov.propose(proposalAddr, "");
const { governance, delegated, signer } = await delegate();
const proposalId = await governance.proposalCount();
await time.increase(60 * 60);
const governanceDelegated = governance.connect(delegated);
await governanceDelegated.castDelegatedVote([signer.address], proposalId, true);
await time.increase(60 * 60 * 24 * 7 + 60);
expect(await governance.state(proposalId)).to.be.equal(4); // awaiting execution
});
it("Delegator should be able to propose", async function () {
await deployAndExecuteProposal();
const { governance, delegated, signer } = await delegate();
const proposalAddr = await createValidProposal();
const governanceDelegated = governance.connect(delegated);
await governanceDelegated.proposeByDelegate(signer.address, proposalAddr, "");
const proposalId = await governance.proposalCount();
await time.increase(60 * 60);
await expect(governanceDelegated.castVote(proposalId, true)).to.be.revertedWith("Governance: balance is 0");
await governanceDelegated.castDelegatedVote([signer.address], proposalId, true);
await time.increase(60 * 60 * 24 * 7 + 60);
await governanceDelegated.execute(proposalId);
expect(await governance.state(proposalId)).to.be.equal(5); // executed
});
it("Staking rewards should be distributed without problem", async function () {
const { governanceContract } = await deployAndExecuteProposal();
const stakingVault = await governanceContract.userVault();
const torn = await getTorn();
const manyTorns = 100n * 1000n * 10n ** 18n;
const signer = await signerLockInGov(manyTorns);
const signerLockedBalance = await governanceContract.lockedBalance(signer.address);
const sumStaking = await torn.balanceOf(stakingVault);
const toDistibute = 10000n * 18n ** 18n;
const stakingAddr = await governanceContract.Staking();
const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr);
const stakingContract = await ethers.getContractAt(
require("./abi/staking.abi.json"),
stakingAddr,
governanceSigner,
);
const ratioConstant = await stakingContract.ratioConstant();
const expectedReward = (toDistibute * ((signerLockedBalance * ratioConstant) / sumStaking)) / ratioConstant;
const rewardsBeforeDistribute = await stakingContract.checkReward(signer.address);
await stakingContract.addBurnRewards(toDistibute);
const rewardsAfterDistibute = await stakingContract.checkReward(signer.address);
expect((rewardsAfterDistibute - rewardsBeforeDistribute) / 1000n).to.be.equal(expectedReward / 1000n);
});
it("Check if account voted in proposal should work correct", async function () {
await deployAndExecuteProposal();
const proposalAddr = await createValidProposal();
const signer = await signerLockInGov("quorum");
const governance = await getGovernance(signer);
let proposalId = await governance.proposalCount();
await governance.propose(proposalAddr, "");
expect(await governance.hasAccountVoted(proposalId + 1n, signer.address)).to.be.equal(false);
proposalId = await governance.proposalCount();
expect(await governance.hasAccountVoted(proposalId, signer.address)).to.be.equal(false);
await time.increase(60 * 60);
expect(await governance.hasAccountVoted(proposalId, signer.address)).to.be.equal(false);
await governance.castVote(proposalId, true);
expect(await governance.hasAccountVoted(proposalId, signer.address)).to.be.equal(true);
});
it("Staker should be able to change vote", async function () {
const { governanceContract } = await deployAndExecuteProposal();
const halfQuorum = (await governanceContract.QUORUM_VOTES()) / 2n;
const proposalAddr = await createValidProposal();
const signer = await signerLockInGov(halfQuorum);
const lockedBalance = await governanceContract.lockedBalance(signer.address);
const governance = await getGovernance(signer);
await governance.propose(proposalAddr, "");
const proposalId = await governance.proposalCount();
await time.increase(60 * 60);
await governance.castVote(proposalId, true);
let proposalInfo = await governance.proposals(proposalId);
expect(proposalInfo.forVotes).to.be.equal(lockedBalance);
expect(proposalInfo.againstVotes).to.be.equal(0n);
await governance.castVote(proposalId, false);
proposalInfo = await governance.proposals(proposalId);
expect(proposalInfo.forVotes).to.be.equal(0n);
expect(proposalInfo.againstVotes).to.be.equal(lockedBalance);
});
it("Delegator should not be able to vote twice in one proposal", async function () {
const { governanceContract } = await deployAndExecuteProposal();
const proposalAddr = await createValidProposal();
const halfQuorum = (await governanceContract.QUORUM_VOTES()) / 2n;
const { signer, governance, delegated } = await delegate(halfQuorum);
await governance.propose(proposalAddr, "");
const proposalId = await governance.proposalCount();
await time.increase(60 * 60);
await governance.castVote(proposalId, true);
const governanceDelegated = governance.connect(delegated);
await expect(governanceDelegated.castDelegatedVote([signer.address], proposalId, true)).to.be.revertedWith(
"Governance: voted already",
);
});
it("If staker change vote in last hour voting should prolong", async function () {
await deployAndExecuteProposal();
const proposalAddr = await createValidProposal();
const signer = await signerLockInGov("quorum");
const governance = await getGovernance(signer);
await governance.propose(proposalAddr, "");
const proposalId = await governance.proposalCount();
await time.increase(60 * 10);
await governance.castVote(proposalId, true);
await time.increase(60 * 60 * 24 * 4 + 60 * 60 * 23 + 60 * 30);
let proposalInfo = await governance.proposals(proposalId);
expect(proposalInfo.extended).to.be.equal(false);
await governance.castVote(proposalId, false);
proposalInfo = await governance.proposals(proposalId);
expect(proposalInfo.extended).to.be.equal(true);
});
});