UNPKG

@vechain/vebetterdao-contracts

Version:

Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.

583 lines (582 loc) 38.5 kB
import { setupGovernanceFixtureWithEmissions, setupProposer, setupVoter, STANDARD_PROPOSAL_TYPE } from "./fixture.test"; import { ethers } from "hardhat"; import { moveBlocks, startNewAllocationRound, getProposalIdFromTx, getVot3Tokens, waitForCurrentRoundToEnd, waitForNextBlock, waitForProposalToBeActive, } from "../helpers/common"; import { endorseApp } from "../helpers/xnodes"; import { describe, it, beforeEach } from "mocha"; import { expect } from "chai"; import { catchRevert } from "../helpers"; describe("Voting power with proposal deposit - @shard4b", function () { let vot3; let b3tr; let minterAccount; let proposer; let creator; let x2EarnApps; let xAllocationVoting; let voter; let veBetterPassport; let owner; let emissions; let governor; let endorser1; beforeEach(async function () { const fixture = await setupGovernanceFixtureWithEmissions(); vot3 = fixture.vot3; b3tr = fixture.b3tr; minterAccount = fixture.minterAccount; proposer = fixture.proposer; creator = fixture.creators; x2EarnApps = fixture.x2EarnApps; xAllocationVoting = fixture.xAllocationVoting; voter = fixture.voter; veBetterPassport = fixture.veBetterPassport; owner = fixture.owner; emissions = fixture.emissions; governor = fixture.governor; endorser1 = fixture.otherAccounts[5]; // Setup proposer for all tests await setupProposer(proposer, b3tr, vot3, minterAccount); }); describe("Backward compatibility", function () { it("Should be able to vote in xApps allocation without proposal deposit if no deposit is done", async function () { await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); // submit app const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); await endorseApp(app1Id, voter); await vot3.connect(voter).delegate(voter.address); await moveBlocks(2); const round1 = await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await xAllocationVoting.connect(voter).castVote(round1, [app1Id], [ethers.parseEther("100")]); // Check the voting power const roundSnapshot = await xAllocationVoting.roundSnapshot(round1); const votingPower = await xAllocationVoting.getVotes(voter.address, roundSnapshot); expect(votingPower).to.equal(ethers.parseEther("10000")); // 10000 because we had 10000 vot3 tokens at snapshot }); it("Should be able to vote on allocation with the proposal deposit", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); const expectedVot3BalanceBeforeDeposit = expectedVot3Balance + depositThreshold; expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3BalanceBeforeDeposit); //Start emissions await emissions.connect(minterAccount).start(); //Round 2 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); // Create the proposal const tx = await governor .connect(voter) .propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), 0, { gasLimit: 10000000, }); //Deposit into the proposal the exact threshold await tx.wait(); const proposalId = await getProposalIdFromTx(tx, false, { governor }); const txForDeposit = await governor.connect(voter).deposit(depositThreshold, proposalId); await txForDeposit.wait(); //Wait for round to end await waitForCurrentRoundToEnd({ xAllocationVoting }); await waitForNextBlock(); await waitForNextBlock(); //Start new round await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const roundIdAfterVotesDeposit = await xAllocationVoting.currentRoundId(); const roundSnapshotBeforeVotes = await xAllocationVoting.roundSnapshot(roundIdBeforeVotesDeposit); const roundSnapshotAfterVotes = await xAllocationVoting.roundSnapshot(roundIdAfterVotesDeposit); const votingPowerForAllocationAfterVotes = await xAllocationVoting.getVotes(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInAllocationBeforeVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInGovernorBeforeVotes = await governor.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInAllocationAfterVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInGovernorAfter = await governor.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); //Both deposit voting power should be 0 because no deposit was done expect(depositvotingpowerInAllocationBeforeVotes).to.equal(0); expect(depositvotingpowerInGovernorBeforeVotes).to.equal(0); expect(depositvotingpowerInAllocationBeforeVotes).to.equal(depositvotingpowerInGovernorBeforeVotes); //Both deposit voting power should be equal the threshold and same for the governor expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositThreshold); expect(depositvotingpowerInGovernorAfter).to.equal(depositThreshold); expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositvotingpowerInGovernorAfter); // getVotes already includes deposit voting power for non-delegated users const txForVote = await xAllocationVoting .connect(voter) .castVote(roundIdAfterVotesDeposit, [app1Id], [votingPowerForAllocationAfterVotes]); await txForVote.wait(); const appVotes = await xAllocationVoting.getAppVotes(roundIdAfterVotesDeposit, app1Id); expect(votingPowerForAllocationAfterVotes).to.equal(appVotes); }); it("Should be able to vote on allocation with the proposal deposit if deposit twice", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); const expectedVot3BalanceBeforeDeposit = expectedVot3Balance + depositThreshold; expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3BalanceBeforeDeposit); //Start emissions await emissions.connect(minterAccount).start(); //Round 2 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); // Create the proposal const tx = await governor .connect(voter) .propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), 0, { gasLimit: 10000000, }); //Deposit into the proposal the exact threshold in 2 operations await tx.wait(); const proposalId = await getProposalIdFromTx(tx, false, { governor }); const txForDeposit = await governor.connect(voter).deposit(depositThreshold / 2n, proposalId); await txForDeposit.wait(); //Wait few blocks to simulate the time between the 2 deposits await moveBlocks(10); const txForDeposit2 = await governor.connect(voter).deposit(depositThreshold / 2n, proposalId); await txForDeposit2.wait(); //Wait for round to end await waitForCurrentRoundToEnd({ xAllocationVoting }); await waitForNextBlock(); await waitForNextBlock(); //Start new round await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const roundIdAfterVotesDeposit = await xAllocationVoting.currentRoundId(); const roundSnapshotBeforeVotes = await xAllocationVoting.roundSnapshot(roundIdBeforeVotesDeposit); const roundSnapshotAfterVotes = await xAllocationVoting.roundSnapshot(roundIdAfterVotesDeposit); const votingPowerForAllocationAfterVotes = await xAllocationVoting.getVotes(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInAllocationBeforeVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInGovernorBeforeVotes = await governor.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInAllocationAfterVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInGovernorAfter = await governor.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); //Both deposit voting power should be 0 because no deposit was done expect(depositvotingpowerInAllocationBeforeVotes).to.equal(0); expect(depositvotingpowerInGovernorBeforeVotes).to.equal(0); expect(depositvotingpowerInAllocationBeforeVotes).to.equal(depositvotingpowerInGovernorBeforeVotes); //Both deposit voting power should be equal the threshold and same for the governor expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositThreshold); expect(depositvotingpowerInGovernorAfter).to.equal(depositThreshold); expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositvotingpowerInGovernorAfter); // getVotes already includes deposit voting power for non-delegated users const txForVote = await xAllocationVoting .connect(voter) .castVote(roundIdAfterVotesDeposit, [app1Id], [votingPowerForAllocationAfterVotes]); await txForVote.wait(); const appVotes = await xAllocationVoting.getAppVotes(roundIdAfterVotesDeposit, app1Id); expect(votingPowerForAllocationAfterVotes).to.equal(appVotes); }); it("Should be able to vote on allocation with the proposal deposit if the proposal is created already with a deposit", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); const expectedVot3BalanceBeforeDeposit = expectedVot3Balance + depositThreshold; expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3BalanceBeforeDeposit); //Start emissions await emissions.connect(minterAccount).start(); //Round 2 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); // Create the proposal already supporting the deposit threshold const tx = await governor .connect(voter) .propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), depositThreshold, { gasLimit: 10000000, }); await tx.wait(); //Wait for round to end await waitForCurrentRoundToEnd({ xAllocationVoting }); await waitForNextBlock(); await waitForNextBlock(); //Start new round await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const roundIdAfterVotesDeposit = await xAllocationVoting.currentRoundId(); const roundSnapshotBeforeVotes = await xAllocationVoting.roundSnapshot(roundIdBeforeVotesDeposit); const roundSnapshotAfterVotes = await xAllocationVoting.roundSnapshot(roundIdAfterVotesDeposit); const votingPowerForAllocationAfterVotes = await xAllocationVoting.getVotes(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInAllocationBeforeVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInGovernorBeforeVotes = await governor.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInAllocationAfterVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInGovernorAfter = await governor.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); //Both deposit voting power should be 0 because no deposit was done expect(depositvotingpowerInAllocationBeforeVotes).to.equal(0); expect(depositvotingpowerInGovernorBeforeVotes).to.equal(0); expect(depositvotingpowerInAllocationBeforeVotes).to.equal(depositvotingpowerInGovernorBeforeVotes); //Both deposit voting power should be equal the threshold and same for the governor expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositThreshold); expect(depositvotingpowerInGovernorAfter).to.equal(depositThreshold); expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositvotingpowerInGovernorAfter); // getVotes already includes deposit voting power for non-delegated users const txForVote = await xAllocationVoting .connect(voter) .castVote(roundIdAfterVotesDeposit, [app1Id], [votingPowerForAllocationAfterVotes]); await txForVote.wait(); const appVotes = await xAllocationVoting.getAppVotes(roundIdAfterVotesDeposit, app1Id); expect(votingPowerForAllocationAfterVotes).to.equal(appVotes); }); it("Should NOT be able to vote on allocation with the proposal deposit if the value is higher than the total voting power", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); const expectedVot3BalanceBeforeDeposit = expectedVot3Balance + depositThreshold; expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3BalanceBeforeDeposit); //Start emissions await emissions.connect(minterAccount).start(); //Round 2 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); // Create the proposal already supporting the deposit threshold const tx = await governor .connect(voter) .propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), depositThreshold, { gasLimit: 10000000, }); await tx.wait(); //Wait for round to end await waitForCurrentRoundToEnd({ xAllocationVoting }); await waitForNextBlock(); await waitForNextBlock(); //Start new round await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const roundIdAfterVotesDeposit = await xAllocationVoting.currentRoundId(); const roundSnapshotBeforeVotes = await xAllocationVoting.roundSnapshot(roundIdBeforeVotesDeposit); const roundSnapshotAfterVotes = await xAllocationVoting.roundSnapshot(roundIdAfterVotesDeposit); const votingPowerForAllocationAfterVotes = await xAllocationVoting.getVotes(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInAllocationBeforeVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInGovernorBeforeVotes = await governor.getDepositVotingPower(voter.address, roundSnapshotBeforeVotes); const depositvotingpowerInAllocationAfterVotes = await xAllocationVoting.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); const depositvotingpowerInGovernorAfter = await governor.getDepositVotingPower(voter.address, roundSnapshotAfterVotes); //Both deposit voting power should be 0 because no deposit was done expect(depositvotingpowerInAllocationBeforeVotes).to.equal(0); expect(depositvotingpowerInGovernorBeforeVotes).to.equal(0); expect(depositvotingpowerInAllocationBeforeVotes).to.equal(depositvotingpowerInGovernorBeforeVotes); //Both deposit voting power should be equal the threshold and same for the governor expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositThreshold); expect(depositvotingpowerInGovernorAfter).to.equal(depositThreshold); expect(depositvotingpowerInAllocationAfterVotes).to.equal(depositvotingpowerInGovernorAfter); //Sum of the voting power and the deposit voting power to get the total vote available for the voter to vote on the allocation const totalVoteWithDeposit = votingPowerForAllocationAfterVotes + depositvotingpowerInAllocationAfterVotes; await catchRevert(xAllocationVoting.connect(voter).castVote(roundIdAfterVotesDeposit, [app1Id], [totalVoteWithDeposit + 1n])); const appVotes = await xAllocationVoting.getAppVotes(roundIdAfterVotesDeposit, app1Id); //App should not have any votes because the total vote with the deposit is higher than the total voting power and tx reverted expect(appVotes).to.equal(0); }); it("Should be able to vote on allocation even if no deposit was done", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Start emissions await emissions.connect(minterAccount).start(); //Wait for round to end await waitForCurrentRoundToEnd({ xAllocationVoting }); await waitForNextBlock(); await waitForNextBlock(); //Start new round await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const currentRoundId = await xAllocationVoting.currentRoundId(); const currentRoundSnapshot = await xAllocationVoting.roundSnapshot(currentRoundId); const votingPowerForAllocationAfterVotes = await xAllocationVoting.getVotes(voter.address, currentRoundSnapshot); const depositVotingPower = await xAllocationVoting.getDepositVotingPower(voter.address, currentRoundSnapshot); expect(votingPowerForAllocationAfterVotes).to.equal(expectedVot3Balance); expect(depositVotingPower).to.equal(0); const txForVote = await xAllocationVoting .connect(voter) .castVote(currentRoundId, [app1Id], [votingPowerForAllocationAfterVotes]); await txForVote.wait(); const appVotes = await xAllocationVoting.getAppVotes(currentRoundId, app1Id); //The app should have the total vote with the deposit as voting power as well expect(votingPowerForAllocationAfterVotes).to.equal(appVotes); }); }); describe("Voting with deposits VP", function () { it("Should NOT count the deposit VP in the proposal VP", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); const totalVot3 = expectedVot3Balance + depositThreshold; // 10 000 vot3 mint + expectedvot3 expect(await vot3.balanceOf(voter.address)).to.equal(totalVot3); //Start emissions await emissions.connect(minterAccount).start(); //Round 1 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); // Create the proposal already supporting the deposit threshold const tx = await governor.connect(voter).propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), // In 2 round possible to withdraw, meanwhile VP is either consumed by castVote, or stored depositThreshold, { gasLimit: 10000000, }); await tx.wait(); await waitForCurrentRoundToEnd({ xAllocationVoting }); await waitForNextBlock(); await waitForNextBlock(); await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); await waitForNextBlock(); const proposalVP = await xAllocationVoting.getVotes(voter.address, await xAllocationVoting.roundSnapshot(await xAllocationVoting.currentRoundId())); const proposalDepositVP = await xAllocationVoting.getDepositVotingPower(voter.address, await xAllocationVoting.roundSnapshot(await xAllocationVoting.currentRoundId())); // getVotes now includes deposit VP for allocation voting expect(proposalVP).to.equal(totalVot3); expect(proposalDepositVP).to.equal(depositThreshold); }); }); describe("Withdrawing(claim back) deposits", function () { it("Deposits VP should be transferred to the VOT3 balance VP when withdrawing", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); const totalVot3 = expectedVot3Balance + depositThreshold; // 10 000 vot3 mint + expectedvot3 expect(await vot3.balanceOf(voter.address)).to.equal(totalVot3); //Start emissions await emissions.connect(minterAccount).start(); //Round 1 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); // Create the proposal already supporting the deposit threshold const tx = await governor.connect(voter).propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), // In 2 round possible to withdraw, meanwhile VP is either consumed by castVote, or stored depositThreshold, { gasLimit: 10000000, }); await tx.wait(); const proposalId = await getProposalIdFromTx(tx, true, { governor }); await waitForProposalToBeActive(proposalId, { governor }); // ROUND 3 -> withdraw possible await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const currentRoundId = await xAllocationVoting.currentRoundId(); const currentRoundSnapshot = await xAllocationVoting.roundSnapshot(currentRoundId); await waitForNextBlock(); const votingPowerAfterVotes = await xAllocationVoting.getVotes(voter.address, currentRoundSnapshot); const depositVPAfterVotes = await xAllocationVoting.getDepositVotingPower(voter.address, currentRoundSnapshot); // getVotes already includes deposits, so it equals the total expect(votingPowerAfterVotes).to.equal(totalVot3); expect(depositVPAfterVotes).to.equal(depositThreshold); // claim the deposit back await governor.withdraw(proposalId, voter.address); // it goes back to the balance (getvotes is increased) of the voter // get the clock const now = await governor.clock(); await waitForNextBlock(); const vpAfterClaimBack_now = await xAllocationVoting.getVotes(voter.address, now); const depositVPAfterClaimBackDeposit_now = await xAllocationVoting.getDepositVotingPower(voter.address, now); expect(depositVPAfterClaimBackDeposit_now).to.equal(0); // withdraw the deposits expect(vpAfterClaimBack_now).to.equal(totalVot3); // deposits goes back in the principal balance await waitForCurrentRoundToEnd({ xAllocationVoting }); await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const currentRoundIdAfterWithdraw = await xAllocationVoting.currentRoundId(); const txForVote = await xAllocationVoting .connect(voter) .castVote(currentRoundIdAfterWithdraw, [app1Id], [vpAfterClaimBack_now]); await txForVote.wait(); const appVotes = await xAllocationVoting.getAppVotes(currentRoundIdAfterWithdraw, app1Id); expect(vpAfterClaimBack_now).to.equal(appVotes); await waitForCurrentRoundToEnd({ xAllocationVoting }); await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); const roundAtSnapshot = await xAllocationVoting.currentRoundId(); const atSnapshot = await xAllocationVoting.roundSnapshot(roundAtSnapshot); const vpAfterSpendingVOT3 = await xAllocationVoting.getVotes(voter.address, atSnapshot); const depositsVPAfterSpendingVOT3 = await xAllocationVoting.getDepositVotingPower(voter.address, atSnapshot); expect(vpAfterSpendingVOT3).to.equal(totalVot3); expect(depositsVPAfterSpendingVOT3).to.equal(0); }); it("Should be able to deposit multiple times, increasing the deposit VP", async function () { //Submit the app await x2EarnApps .connect(owner) .submitApp(creator[0].address, creator[0].address, creator[0].address, "metadataURI"); const app1Id = ethers.keccak256(ethers.toUtf8Bytes(creator[0].address)); //Endorse App await endorseApp(app1Id, endorser1); //Setup voter + start new round await setupVoter(voter, b3tr, vot3, minterAccount, owner, veBetterPassport); const expectedVot3Balance = ethers.parseEther("10000"); expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3Balance); //Get the deposit threshold for the proposal type const depositThreshold = await governor.depositThresholdByProposalType(STANDARD_PROPOSAL_TYPE); //Get user the balance of deposit await getVot3Tokens(voter, ethers.formatEther(depositThreshold)); await getVot3Tokens(owner, ethers.formatEther(depositThreshold)); const expectedVot3BalanceBeforeDeposits = expectedVot3Balance + depositThreshold; // 10 000 vot3 mint + expectedvot3 expect(await vot3.balanceOf(voter.address)).to.equal(expectedVot3BalanceBeforeDeposits); expect(await vot3.balanceOf(owner.address)).to.equal(depositThreshold); //Start emissions await emissions.connect(minterAccount).start(); //Round 1 const roundIdBeforeVotesDeposit = await xAllocationVoting.currentRoundId(); //Allowance for the deposit await vot3.connect(voter).approve(await governor.getAddress(), depositThreshold); const depositThresholdReduced = depositThreshold / BigInt(2); // 2 times less const remainingDivided = (depositThreshold - depositThresholdReduced) / BigInt(2); const voterDepositSecondTime = remainingDivided; const depositor2Deposit = remainingDivided; await vot3.connect(owner).approve(await governor.getAddress(), depositor2Deposit); // Create the proposal already supporting the deposit threshold const tx = await governor.connect(voter).propose([await b3tr.getAddress()], [0], [(await ethers.getContractFactory("B3TR")).interface.encodeFunctionData("tokenDetails", [])], `${this?.test?.title}`, (Number(roundIdBeforeVotesDeposit) + 2).toString(), // In 2 round possible to withdraw, meanwhile VP is either consumed by castVote, or stored depositThresholdReduced, { gasLimit: 10000000, }); await tx.wait(); const proposalId = await getProposalIdFromTx(tx, true, { governor }); // remaing vot3 deposited by the voter and the depositor2 await governor.connect(voter).deposit(voterDepositSecondTime, proposalId, { gasLimit: 10000000 }); await governor.connect(owner).deposit(depositor2Deposit, proposalId, { gasLimit: 10000000 }); const voterBalance = await vot3.balanceOf(voter.address); const depositor2Balance = await vot3.balanceOf(owner.address); await waitForProposalToBeActive(proposalId, { governor }); await waitForCurrentRoundToEnd({ xAllocationVoting }); await startNewAllocationRound({ emissions, xAllocationVoting, minterAccount, }); await waitForNextBlock(); await waitForNextBlock(); const nowSnapshot = await xAllocationVoting.roundSnapshot(await xAllocationVoting.currentRoundId()); const vpVoter = await xAllocationVoting.getVotes(voter.address, nowSnapshot); const depositVPVoter = await xAllocationVoting.getDepositVotingPower(voter.address, nowSnapshot); // getVotes includes deposits for allocation voting const voterTotalDeposits = voterDepositSecondTime + depositThresholdReduced; expect(vpVoter).to.equal(voterBalance + voterTotalDeposits); expect(depositVPVoter).to.equal(voterTotalDeposits); const vpDepositor2 = await xAllocationVoting.getVotes(owner.address, nowSnapshot); const depositVPDepositor2 = await xAllocationVoting.getDepositVotingPower(owner.address, nowSnapshot); expect(vpDepositor2).to.equal(depositor2Balance + depositor2Deposit); expect(depositVPDepositor2).to.equal(depositor2Deposit); await governor.withdraw(proposalId, voter.address); await governor.withdraw(proposalId, owner.address); const afterWithrawSnapshot = await xAllocationVoting.roundSnapshot(await governor.clock()); const depositVPAfterWithdrawVoter = await xAllocationVoting.getDepositVotingPower(voter.address, afterWithrawSnapshot); const depositVPAfterWithdrawDepositor2 = await xAllocationVoting.getDepositVotingPower(owner.address, afterWithrawSnapshot); expect(depositVPAfterWithdrawVoter).to.equal(0); // after withdraw the voter has no deposit expect(depositVPAfterWithdrawDepositor2).to.equal(0); // after withdraw the depositor2 has no deposit }); }); });