UNPKG

@vechain/vebetterdao-contracts

Version:

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

736 lines 153 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const hardhat_1 = require("hardhat"); const chai_1 = require("chai"); const helpers_1 = require("./helpers"); const mocha_1 = require("mocha"); const upgrades_core_1 = require("@openzeppelin/upgrades-core"); const helpers_2 = require("../scripts/helpers"); const xnodes_1 = require("./helpers/xnodes"); const local_1 = require("@repo/config/contracts/envs/local"); const config_1 = require("./helpers/config"); const xAllocationVotingLibraries_1 = require("../scripts/libraries/xAllocationVotingLibraries"); (0, mocha_1.describe)("X-Allocation Voting - @shard14-core", function () { // Environment params let creator1; let creator2; let creator3; let creator4; (0, mocha_1.before)(async function () { const { creators } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true }); creator1 = creators[0]; creator2 = creators[1]; creator3 = creators[2]; creator4 = creators[3]; }); (0, mocha_1.describe)("Deployment", function () { (0, mocha_1.it)("Admins should be set correctly", async function () { const { xAllocationVoting, owner, timeLock } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); const ADMIN_ROLE = "0x0000000000000000000000000000000000000000000000000000000000000000"; (0, chai_1.expect)(await xAllocationVoting.hasRole(ADMIN_ROLE, await timeLock.getAddress())).to.eql(true); (0, chai_1.expect)(await xAllocationVoting.hasRole(ADMIN_ROLE, owner.address)).to.eql(true); }); (0, mocha_1.it)("Should not support invalid interface", async function () { const { xAllocationVoting } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); const INVALID_ID = "0xffffffff"; (0, chai_1.expect)(await xAllocationVoting.supportsInterface(INVALID_ID)).to.eql(false); }); (0, mocha_1.it)("Can set multiple admins during deployment", async function () { const { voterRewards, timeLock, emissions, x2EarnApps, vot3, otherAccounts, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false, }); const xAllocationVotingV1 = (await (0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: await vot3.getAddress(), quorumPercentage: 1, initialVotingPeriod: 2, timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), otherAccounts[2].address, otherAccounts[2].address], upgrader: owner.address, contractsAddressManager: otherAccounts[2].address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: 2, appSharesCap: 2, votingThreshold: BigInt(1), }, ])); // Upgrade XAllocationVoting V1 to XAllocationVoting V2 const xAllocationVoting = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV1", "XAllocationVotingV2", await xAllocationVotingV1.getAddress(), [], { version: 2 })); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.DEFAULT_ADMIN_ROLE(), await timeLock.getAddress())).to.eql(true); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.DEFAULT_ADMIN_ROLE(), otherAccounts[2].address)).to.eql(true); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.DEFAULT_ADMIN_ROLE(), otherAccounts[2].address)).to.eql(true); }); (0, mocha_1.it)("Should support ERC 165 interface", async () => { const { xAllocationVoting } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await xAllocationVoting.supportsInterface("0x01ffc9a7")).to.equal(true); // ERC165 }); (0, mocha_1.it)("Clock mode is set correctly", async function () { const { xAllocationVoting, vot3 } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false, }); (0, chai_1.expect)(await xAllocationVoting.CLOCK_MODE()).to.eql(await vot3.CLOCK_MODE()); }); (0, mocha_1.it)("Clock returns block number if token does not implement clock function", async function () { const { xAllocationVoting, timeLock, voterRewards, emissions, otherAccounts, x2EarnApps, b3tr, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false, }); let clock = await xAllocationVoting.clock(); (0, chai_1.expect)(parseInt(clock.toString())).to.eql(await hardhat_1.ethers.provider.getBlockNumber()); const xAllocationVotingWithB3TRV1 = (await (0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: await b3tr.getAddress(), quorumPercentage: 1, initialVotingPeriod: 2, timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), otherAccounts[2].address, otherAccounts[2].address], upgrader: owner.address, contractsAddressManager: otherAccounts[2].address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: 2, appSharesCap: 2, votingThreshold: BigInt(1), }, ])); // Upgrade XAllocationVoting V1 to XAllocationVoting V2 const xAllocationVotingWithB3TR = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV1", "XAllocationVotingV2", await xAllocationVotingWithB3TRV1.getAddress(), [], { version: 2 })); clock = await xAllocationVotingWithB3TR.clock(); (0, chai_1.expect)(parseInt(clock.toString())).to.eql(await hardhat_1.ethers.provider.getBlockNumber()); //CLOKC_MODE should return "mode=blocknumber&from=default" (0, chai_1.expect)(await xAllocationVotingWithB3TR.CLOCK_MODE()).to.eql("mode=blocknumber&from=default"); }); (0, mocha_1.it)("Should revert if VOT3 is set to zero address in initilisation", async () => { const config = (0, local_1.createLocalConfig)(); const { owner, x2EarnApps, timeLock, emissions, voterRewards } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config, }); await (0, chai_1.expect)((0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: helpers_1.ZERO_ADDRESS, quorumPercentage: config.X_ALLOCATION_VOTING_QUORUM_PERCENTAGE, // quorum percentage initialVotingPeriod: config.EMISSIONS_CYCLE_DURATION - 1, // X Alloc voting period timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), owner.address], upgrader: owner.address, contractsAddressManager: owner.address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: config.X_ALLOCATION_POOL_BASE_ALLOCATION_PERCENTAGE, appSharesCap: config.X_ALLOCATION_POOL_APP_SHARES_MAX_CAP, votingThreshold: config.X_ALLOCATION_VOTING_VOTING_THRESHOLD, }, ])).to.be.reverted; }); (0, mocha_1.it)("Should revert if VoterRewards is set to zero address in initilisation", async () => { const config = (0, local_1.createLocalConfig)(); const { owner, x2EarnApps, timeLock, emissions, vot3 } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config, }); await (0, chai_1.expect)((0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: await vot3.getAddress(), quorumPercentage: config.X_ALLOCATION_VOTING_QUORUM_PERCENTAGE, // quorum percentage initialVotingPeriod: config.EMISSIONS_CYCLE_DURATION - 1, // X Alloc voting period timeLock: await timeLock.getAddress(), voterRewards: helpers_1.ZERO_ADDRESS, emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), owner.address], upgrader: owner.address, contractsAddressManager: owner.address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: config.X_ALLOCATION_POOL_BASE_ALLOCATION_PERCENTAGE, appSharesCap: config.X_ALLOCATION_POOL_APP_SHARES_MAX_CAP, votingThreshold: config.X_ALLOCATION_VOTING_VOTING_THRESHOLD, }, ])).to.be.reverted; }); (0, mocha_1.it)("Should revert if Emissions is set to zero address in initilisation", async () => { const config = (0, local_1.createLocalConfig)(); const { owner, x2EarnApps, timeLock, vot3, voterRewards } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config, }); await (0, chai_1.expect)((0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: await vot3.getAddress(), quorumPercentage: config.X_ALLOCATION_VOTING_QUORUM_PERCENTAGE, // quorum percentage initialVotingPeriod: config.EMISSIONS_CYCLE_DURATION - 1, // X Alloc voting period timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: helpers_1.ZERO_ADDRESS, admins: [await timeLock.getAddress(), owner.address], upgrader: owner.address, contractsAddressManager: owner.address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: config.X_ALLOCATION_POOL_BASE_ALLOCATION_PERCENTAGE, appSharesCap: config.X_ALLOCATION_POOL_APP_SHARES_MAX_CAP, votingThreshold: config.X_ALLOCATION_VOTING_VOTING_THRESHOLD, }, ])).to.be.reverted; }); (0, mocha_1.it)("Should revert if an admin is set to zero address in initilisation", async () => { const config = (0, local_1.createLocalConfig)(); const { owner, x2EarnApps, timeLock, vot3, voterRewards, emissions } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config, }); await (0, chai_1.expect)((0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: await vot3.getAddress(), quorumPercentage: config.X_ALLOCATION_VOTING_QUORUM_PERCENTAGE, // quorum percentage initialVotingPeriod: config.EMISSIONS_CYCLE_DURATION - 1, // X Alloc voting period timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), helpers_1.ZERO_ADDRESS], upgrader: owner.address, contractsAddressManager: owner.address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: config.X_ALLOCATION_POOL_BASE_ALLOCATION_PERCENTAGE, appSharesCap: config.X_ALLOCATION_POOL_APP_SHARES_MAX_CAP, votingThreshold: config.X_ALLOCATION_VOTING_VOTING_THRESHOLD, }, ])).to.be.reverted; }); }); (0, mocha_1.describe)("Contract upgradeablity", () => { (0, mocha_1.it)("Admin should be able to upgrade the contract", async function () { const { xAllocationVoting, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); const xAllocLibs = await (0, xAllocationVotingLibraries_1.xAllocationVotingLibraries)(); // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("XAllocationVoting", { libraries: { AutoVotingLogic: await xAllocLibs.AutoVotingLogic.getAddress(), ExternalContractsUtils: await xAllocLibs.ExternalContractsUtils.getAddress(), VotingSettingsUtils: await xAllocLibs.VotingSettingsUtils.getAddress(), VotesUtils: await xAllocLibs.VotesUtils.getAddress(), VotesQuorumFractionUtils: await xAllocLibs.VotesQuorumFractionUtils.getAddress(), RoundEarningsSettingsUtils: await xAllocLibs.RoundEarningsSettingsUtils.getAddress(), RoundFinalizationUtils: await xAllocLibs.RoundFinalizationUtils.getAddress(), RoundsStorageUtils: await xAllocLibs.RoundsStorageUtils.getAddress(), RoundVotesCountingUtils: await xAllocLibs.RoundVotesCountingUtils.getAddress(), }, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); const UPGRADER_ROLE = await xAllocationVoting.UPGRADER_ROLE(); (0, chai_1.expect)(await xAllocationVoting.hasRole(UPGRADER_ROLE, owner.address)).to.eql(true); await (0, chai_1.expect)(xAllocationVoting.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be .reverted; const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase()); }); (0, mocha_1.it)("Only admin should be able to upgrade the contract", async function () { const { xAllocationVoting, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("TimeLock"); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); const UPGRADER_ROLE = await xAllocationVoting.UPGRADER_ROLE(); (0, chai_1.expect)(await xAllocationVoting.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false); await (0, chai_1.expect)(xAllocationVoting.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to .be.reverted; const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql(currentImplAddress.toUpperCase()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql((await implementation.getAddress()).toUpperCase()); }); (0, mocha_1.it)("Admin can change UPGRADER_ROLE", async function () { const { xAllocationVoting, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("TimeLock"); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); const UPGRADER_ROLE = await xAllocationVoting.UPGRADER_ROLE(); (0, chai_1.expect)(await xAllocationVoting.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false); await (0, chai_1.expect)(xAllocationVoting.connect(owner).grantRole(UPGRADER_ROLE, otherAccount.address)).to.not.be.reverted; await (0, chai_1.expect)(xAllocationVoting.connect(owner).revokeRole(UPGRADER_ROLE, owner.address)).to.not.be.reverted; await (0, chai_1.expect)(xAllocationVoting.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to .not.be.reverted; const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase()); }); (0, mocha_1.it)("should be able to upgrade the xAllocationVoting contract through governance", async function () { const config = (0, local_1.createLocalConfig)(); config.B3TR_GOVERNOR_DEPOSIT_THRESHOLD = 0; const { xAllocationVoting, timeLock, governor, owner, otherAccount, vot3, veBetterPassport } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config, }); const xAllocLibs = await (0, xAllocationVotingLibraries_1.xAllocationVotingLibraries)(); await (0, helpers_1.getVot3Tokens)(otherAccount, "1000"); await vot3.connect(otherAccount).approve(await governor.getAddress(), "1000"); const UPGRADER_ROLE = await xAllocationVoting.UPGRADER_ROLE(); await (0, chai_1.expect)(xAllocationVoting.connect(owner).grantRole(UPGRADER_ROLE, await timeLock.getAddress())).to.not.be .reverted; // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("XAllocationVoting", { libraries: { AutoVotingLogic: await xAllocLibs.AutoVotingLogic.getAddress(), ExternalContractsUtils: await xAllocLibs.ExternalContractsUtils.getAddress(), VotingSettingsUtils: await xAllocLibs.VotingSettingsUtils.getAddress(), VotesUtils: await xAllocLibs.VotesUtils.getAddress(), VotesQuorumFractionUtils: await xAllocLibs.VotesQuorumFractionUtils.getAddress(), RoundEarningsSettingsUtils: await xAllocLibs.RoundEarningsSettingsUtils.getAddress(), RoundFinalizationUtils: await xAllocLibs.RoundFinalizationUtils.getAddress(), RoundsStorageUtils: await xAllocLibs.RoundsStorageUtils.getAddress(), RoundVotesCountingUtils: await xAllocLibs.RoundVotesCountingUtils.getAddress(), }, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); await (0, helpers_1.bootstrapAndStartEmissions)(); // Whitelist user await veBetterPassport.whitelist(otherAccount.address); await veBetterPassport.toggleCheck(1); const LatestContract = await hardhat_1.ethers.getContractAt("XAllocationVoting", await xAllocationVoting.getAddress()); // Now we can create a proposal const encodedFunctionCall = LatestContract.interface.encodeFunctionData("upgradeToAndCall", [ await implementation.getAddress(), "0x", ]); const description = "Upgrading XAllocationVoting contracts"; const descriptionHash = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes(description)); const currentRoundId = await xAllocationVoting.currentRoundId(); const tx = await governor .connect(owner) .propose([await xAllocationVoting.getAddress()], [0], [encodedFunctionCall], description, currentRoundId + 1n, 0); const proposalId = await (0, helpers_1.getProposalIdFromTx)(tx); await (0, helpers_1.waitForProposalToBeActive)(proposalId); await governor.connect(otherAccount).castVote(proposalId, 1); await (0, helpers_1.waitForVotingPeriodToEnd)(proposalId); (0, chai_1.expect)(await governor.state(proposalId)).to.eql(4n); // succeded await governor.queue([await xAllocationVoting.getAddress()], [0], [encodedFunctionCall], descriptionHash); (0, chai_1.expect)(await governor.state(proposalId)).to.eql(5n); await governor.execute([await xAllocationVoting.getAddress()], [0], [encodedFunctionCall], descriptionHash); (0, chai_1.expect)(await governor.state(proposalId)).to.eql(6n); const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await xAllocationVoting.getAddress()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase()); }); (0, mocha_1.it)("Cannot initialize twice", async function () { const { voterRewards, timeLock, emissions, x2EarnApps, vot3, otherAccounts, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false, }); const xAllocationVotingV1 = (await (0, helpers_2.deployProxy)("XAllocationVotingV1", [ { vot3Token: await vot3.getAddress(), quorumPercentage: 1, initialVotingPeriod: 2, timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), otherAccounts[2].address, otherAccounts[2].address], upgrader: owner.address, contractsAddressManager: otherAccounts[2].address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: 2, appSharesCap: 2, votingThreshold: BigInt(1), }, ])); await (0, helpers_1.catchRevert)(xAllocationVotingV1.initialize({ vot3Token: await vot3.getAddress(), quorumPercentage: 1, initialVotingPeriod: 2, timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), otherAccounts[2].address, otherAccounts[2].address], upgrader: owner.address, contractsAddressManager: otherAccounts[2].address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: 2, appSharesCap: 2, votingThreshold: BigInt(1), })); }); (0, mocha_1.it)("Should return correct version of the contract", async () => { const { xAllocationVoting } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await xAllocationVoting.version()).to.equal("10"); }); (0, mocha_1.it)("Should not break storage when upgrading to V2, V3, V4, V5, V6, V7 and V8", async () => { const config = (0, config_1.createTestConfig)(); const configContracts = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); const { otherAccounts, x2EarnApps, xAllocationPool, b3tr, vot3, galaxyMember, timeLock, treasury, owner, veBetterPassport, minterAccount, } = configContracts; // set personhood threshold to 0 await veBetterPassport.connect(owner).setThresholdPoPScore(0); await veBetterPassport.toggleCheck(4); const emissionsV1 = (await (0, helpers_2.deployProxy)("Emissions", [ { minter: minterAccount.address, admin: owner.address, upgrader: owner.address, contractsAddressManager: owner.address, decaySettingsManager: owner.address, b3trAddress: await b3tr.getAddress(), destinations: [ await xAllocationPool.getAddress(), owner.address, await treasury.getAddress(), config.MIGRATION_ADDRESS, ], initialXAppAllocation: config.INITIAL_X_ALLOCATION, cycleDuration: config.EMISSIONS_CYCLE_DURATION, decaySettings: [ config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE, config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE, config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD, config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD, ], treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE, maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE, migrationAmount: config.MIGRATION_AMOUNT, }, ])); const emissions = (await (0, helpers_2.upgradeProxy)("EmissionsV1", "Emissions", await emissionsV1.getAddress(), [config.EMISSIONS_IS_NOT_ALIGNED ?? false], { version: 2, })); const voterRewardsV1 = (await (0, helpers_2.deployProxy)("VoterRewardsV1", [ owner.address, // admin owner.address, // upgrader owner.address, // contractsAddressManager await emissions.getAddress(), await galaxyMember.getAddress(), await b3tr.getAddress(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [0, 10, 20, 50, 100, 150, 200, 400, 900, 2400], ])); const voterRewards = (await (0, helpers_2.upgradeProxy)("VoterRewardsV1", "VoterRewards", await voterRewardsV1.getAddress(), [], { version: 2, })); // Set vote 2 earn (VoterRewards deployed contract) address in emissions await emissions.connect(owner).setVote2EarnAddress(await voterRewards.getAddress()); // Deploy and upgrade through V3 const xAllocationVotingV3 = (await (0, helpers_2.deployAndUpgrade)(["XAllocationVotingV1", "XAllocationVotingV2", "XAllocationVotingV3"], [ [ { vot3Token: await vot3.getAddress(), quorumPercentage: config.X_ALLOCATION_VOTING_QUORUM_PERCENTAGE, initialVotingPeriod: config.EMISSIONS_CYCLE_DURATION - 1, timeLock: await timeLock.getAddress(), voterRewards: await voterRewards.getAddress(), emissions: await emissions.getAddress(), admins: [await timeLock.getAddress(), owner.address], upgrader: owner.address, contractsAddressManager: owner.address, x2EarnAppsAddress: await x2EarnApps.getAddress(), baseAllocationPercentage: config.X_ALLOCATION_POOL_BASE_ALLOCATION_PERCENTAGE, appSharesCap: config.X_ALLOCATION_POOL_APP_SHARES_MAX_CAP, votingThreshold: config.X_ALLOCATION_VOTING_VOTING_THRESHOLD, }, ], [await veBetterPassport.getAddress()], [], ], { versions: [undefined, 2, 3], })); (0, chai_1.expect)(await xAllocationVotingV3.version()).to.equal("3"); await emissions.setXAllocationsGovernorAddress(await xAllocationVotingV3.getAddress()); (0, chai_1.expect)(await emissions.xAllocationsGovernor()).to.eql(await xAllocationVotingV3.getAddress()); await xAllocationPool.setXAllocationVotingAddress(await xAllocationVotingV3.getAddress()); (0, chai_1.expect)(await xAllocationPool.xAllocationVoting()).to.eql(await xAllocationVotingV3.getAddress()); await xAllocationPool.setEmissionsAddress(await emissions.getAddress()); (0, chai_1.expect)(await xAllocationPool.emissions()).to.eql(await emissions.getAddress()); // Grant Vote registrar role to XAllocationVoting await voterRewards .connect(owner) .grantRole(await voterRewards.VOTE_REGISTRAR_ROLE(), await xAllocationVotingV3.getAddress()); // Grant admin role to voter rewards for registering x allocation voting await xAllocationVotingV3 .connect(owner) .grantRole(await xAllocationVotingV3.DEFAULT_ADMIN_ROLE(), emissions.getAddress()); //Set the emissions address and the admin as the ROUND_STARTER_ROLE in XAllocationVoting const roundStarterRole = await xAllocationVotingV3.ROUND_STARTER_ROLE(); await xAllocationVotingV3 .connect(owner) .grantRole(roundStarterRole, await emissions.getAddress()) .then(async (tx) => await tx.wait()); await xAllocationVotingV3 .connect(owner) .grantRole(roundStarterRole, owner.address) .then(async (tx) => await tx.wait()); const user1 = otherAccounts[0]; const user2 = otherAccounts[1]; const user3 = otherAccounts[2]; // fund wallets await (0, helpers_1.getVot3Tokens)(user1, "1000"); await (0, helpers_1.getVot3Tokens)(user2, "1000"); await (0, helpers_1.getVot3Tokens)(user3, "1000"); // add apps const app1Id = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes(otherAccounts[2].address)); const app2Id = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes(otherAccounts[3].address)); const app3Id = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes(otherAccounts[4].address)); await x2EarnApps .connect(creator1) .submitApp(otherAccounts[2].address, otherAccounts[2].address, otherAccounts[2].address, "metadataURI"); await x2EarnApps .connect(creator2) .submitApp(otherAccounts[3].address, otherAccounts[3].address, otherAccounts[3].address, "metadataURI"); await x2EarnApps .connect(creator3) .submitApp(otherAccounts[4].address, otherAccounts[4].address, otherAccounts[4].address, "metadataURI"); await (0, xnodes_1.endorseApp)(app1Id, otherAccounts[2]); await (0, xnodes_1.endorseApp)(app2Id, otherAccounts[3]); await (0, xnodes_1.endorseApp)(app3Id, otherAccounts[4]); // Grant minter role to emissions contract await b3tr.connect(owner).grantRole(await b3tr.MINTER_ROLE(), await emissions.getAddress()); // Bootstrap emissions await emissions.connect(minterAccount).bootstrap(); // start round await emissions.connect(minterAccount).start(); (0, chai_1.expect)(await xAllocationVotingV3.currentRoundId()).to.equal(1n); // make people vote await xAllocationVotingV3.connect(user1).castVote(1, [app1Id], [hardhat_1.ethers.parseEther("100")]); await xAllocationVotingV3 .connect(user2) .castVote(1, [app1Id, app2Id], [hardhat_1.ethers.parseEther("100"), hardhat_1.ethers.parseEther("200")]); // Upgrade to V4 (using the V3 contract address) const xAllocationVotingV4 = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV3", "XAllocationVotingV4", await xAllocationVotingV3.getAddress(), [], { version: 4, })); const xAllocationVotingV5 = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV4", "XAllocationVotingV5", await xAllocationVotingV4.getAddress(), // Use V4's address [], { version: 5, })); (0, chai_1.expect)(await xAllocationVotingV5.version()).to.equal("5"); // check that round is ok (0, chai_1.expect)(await xAllocationVotingV5.currentRoundId()).to.equal(1n); (0, chai_1.expect)(await xAllocationVotingV5.state(1n)).to.equal(0n); // Active (0, chai_1.expect)(await xAllocationVotingV5.hasVoted(1, user1.address)).to.be.true; (0, chai_1.expect)(await xAllocationVotingV5.hasVoted(1, user2.address)).to.be.true; (0, chai_1.expect)(await xAllocationVotingV5.hasVoted(1, user3.address)).to.be.false; (0, chai_1.expect)(await xAllocationVotingV5.getAppVotes(1, app1Id)).to.equal(hardhat_1.ethers.parseEther("200")); (0, chai_1.expect)(await xAllocationVotingV5.getAppVotes(1, app2Id)).to.equal(hardhat_1.ethers.parseEther("200")); (0, chai_1.expect)(await xAllocationVotingV5.getAppVotes(1, app3Id)).to.equal(hardhat_1.ethers.parseEther("0")); // check that can still vote on the new round await xAllocationVotingV5.connect(user3).castVote(1, [app1Id], [hardhat_1.ethers.parseEther("100")]); (0, chai_1.expect)(await xAllocationVotingV5.getAppVotes(1, app1Id)).to.equal(hardhat_1.ethers.parseEther("300")); // check that round is over correctly const blockNextCycle = await emissions.getNextCycleBlock(); await (0, helpers_1.waitForBlock)(Number(blockNextCycle)); (0, chai_1.expect)(await emissions.isCycleEnded(1)).to.be.true; await emissions.distribute(); (0, chai_1.expect)(await xAllocationVotingV4.currentRoundId()).to.equal(2n); // check that rewards are distributed correctly await (0, chai_1.expect)(xAllocationPool.claim(1, app1Id)).to.not.be.reverted; await (0, chai_1.expect)(xAllocationPool.claim(1, app2Id)).to.not.be.reverted; await (0, chai_1.expect)(xAllocationPool.claim(1, app3Id)).to.not.be.reverted; // can cast vote for round 2 await xAllocationVotingV4.connect(user1).castVote(2, [app1Id], [hardhat_1.ethers.parseEther("100")]); // Upgrade to V5 (using the V4 contract address) const xAllocationVotingV6 = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV5", "XAllocationVotingV6", await xAllocationVotingV4.getAddress(), // Use V4's address [], { version: 6, })); (0, chai_1.expect)(await xAllocationVotingV6.version()).to.equal("6"); // check that round is ok (0, chai_1.expect)(await xAllocationVotingV6.currentRoundId()).to.equal(2n); (0, chai_1.expect)(await xAllocationVotingV6.state(2n)).to.equal(0n); // Active (0, chai_1.expect)(await xAllocationVotingV6.hasVoted(1, user1.address)).to.be.true; (0, chai_1.expect)(await xAllocationVotingV6.hasVoted(1, user2.address)).to.be.true; (0, chai_1.expect)(await xAllocationVotingV6.hasVoted(1, user3.address)).to.be.true; // Update these expectations to match the actual state after user3's vote (0, chai_1.expect)(await xAllocationVotingV6.getAppVotes(1, app1Id)).to.equal(hardhat_1.ethers.parseEther("300")); (0, chai_1.expect)(await xAllocationVotingV6.getAppVotes(1, app2Id)).to.equal(hardhat_1.ethers.parseEther("200")); (0, chai_1.expect)(await xAllocationVotingV6.getAppVotes(1, app3Id)).to.equal(hardhat_1.ethers.parseEther("0")); // check that can still vote on the new round await xAllocationVotingV6.connect(user3).castVote(2, [app1Id], [hardhat_1.ethers.parseEther("100")]); (0, chai_1.expect)(await xAllocationVotingV6.getAppVotes(2, app1Id)).to.equal(hardhat_1.ethers.parseEther("200")); // check that round is over correctly await (0, helpers_1.waitForBlock)(Number(await emissions.getNextCycleBlock())); (0, chai_1.expect)(await emissions.isCycleEnded(2)).to.be.true; await emissions.distribute(); (0, chai_1.expect)(await xAllocationVotingV6.currentRoundId()).to.equal(3n); // check that rewards are distributed correctly await (0, chai_1.expect)(xAllocationPool.claim(2, app1Id)).to.not.be.reverted; await (0, chai_1.expect)(xAllocationPool.claim(2, app2Id)).to.not.be.reverted; await (0, chai_1.expect)(xAllocationPool.claim(2, app3Id)).to.not.be.reverted; // can cast vote for round 3 await xAllocationVotingV6.connect(user1).castVote(3, [app1Id], [hardhat_1.ethers.parseEther("100")]); // Upgrade to V7 (using the V6 contract address) const xAllocationVotingV7 = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV6", "XAllocationVotingV7", await xAllocationVotingV6.getAddress(), // Use V6's address [], { version: 7, })); (0, chai_1.expect)(await xAllocationVotingV7.version()).to.equal("7"); // check that round is ok (0, chai_1.expect)(await xAllocationVotingV7.currentRoundId()).to.equal(3n); (0, chai_1.expect)(await xAllocationVotingV7.state(3n)).to.equal(0n); // Active const xAllocLibs = await (0, xAllocationVotingLibraries_1.xAllocationVotingLibraries)(); // Latest version const xAllocationVoting = (await (0, helpers_2.upgradeProxy)("XAllocationVotingV7", "XAllocationVoting", await xAllocationVotingV7.getAddress(), // Use V7's address [], { version: 8, libraries: { AutoVotingLogic: await xAllocLibs.AutoVotingLogic.getAddress(), ExternalContractsUtils: await xAllocLibs.ExternalContractsUtils.getAddress(), VotingSettingsUtils: await xAllocLibs.VotingSettingsUtils.getAddress(), VotesUtils: await xAllocLibs.VotesUtils.getAddress(), VotesQuorumFractionUtils: await xAllocLibs.VotesQuorumFractionUtils.getAddress(), RoundEarningsSettingsUtils: await xAllocLibs.RoundEarningsSettingsUtils.getAddress(), RoundFinalizationUtils: await xAllocLibs.RoundFinalizationUtils.getAddress(), RoundsStorageUtils: await xAllocLibs.RoundsStorageUtils.getAddress(), RoundVotesCountingUtils: await xAllocLibs.RoundVotesCountingUtils.getAddress(), }, })); (0, chai_1.expect)(await xAllocationVoting.version()).to.equal("10"); }); }); (0, mocha_1.describe)("Settings", function () { (0, mocha_1.describe)("General settings", function () { (0, mocha_1.it)("Contract should not be able to receive ether", async function () { const { xAllocationVoting, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false }); await (0, chai_1.expect)(owner.sendTransaction({ to: await xAllocationVoting.getAddress(), value: hardhat_1.ethers.parseEther("1.0"), // Sends exactly 1.0 ether })).to.be.reverted; (0, chai_1.expect)(await hardhat_1.ethers.provider.getBalance(await xAllocationVoting.getAddress())).to.eql(0n); }); (0, mocha_1.describe)("emissions address", function () { (0, mocha_1.it)("Admin with CONTRACTS_ADDRESS_MANAGER_ROLE can set a new emissions contract correctly", async function () { const { xAllocationVoting, owner, emissions } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); await (0, helpers_1.bootstrapAndStartEmissions)(); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.CONTRACTS_ADDRESS_MANAGER_ROLE(), owner.address)).to.be.true; const oldEmissionsAddress = await emissions.getAddress(); const emissionsSetIface = new hardhat_1.ethers.Interface([ "event EmissionsSet(address oldContractAddress, address newContractAddress)", ]); const receipt = await (await xAllocationVoting.connect(owner).setEmissionsAddress(owner.address)).wait(); let emissionsSetEvent; for (const log of receipt?.logs ?? []) { try { const p = emissionsSetIface.parseLog({ topics: [...log.topics], data: log.data }); if (p !== null && p.name === "EmissionsSet") { emissionsSetEvent = p; break; } } catch { /* ignore non-matching logs */ } } (0, chai_1.expect)(emissionsSetEvent).to.not.be.undefined; (0, chai_1.expect)(emissionsSetEvent.args.oldContractAddress).to.equal(oldEmissionsAddress); (0, chai_1.expect)(emissionsSetEvent.args.newContractAddress).to.equal(owner.address); }); (0, mocha_1.it)("Cannot set a new emissions contract to zero address", async function () { const { xAllocationVoting, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); await (0, helpers_1.bootstrapAndStartEmissions)(); await (0, chai_1.expect)(xAllocationVoting.connect(owner).setEmissionsAddress(helpers_1.ZERO_ADDRESS)).to.be.reverted; }); (0, mocha_1.it)("Only admin with CONTRACTS_ADDRESS_MANAGER_ROLE should be able to set a new emissions contract", async function () { const { xAllocationVoting, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.CONTRACTS_ADDRESS_MANAGER_ROLE(), otherAccount.address)).to.be.false; await (0, chai_1.expect)(xAllocationVoting.connect(otherAccount).setEmissionsAddress(otherAccount.address)).to.be.reverted; }); }); (0, mocha_1.describe)("x2EarnApps address", function () { (0, mocha_1.it)("Admin with CONTRACTS_ADDRESS_MANAGER_ROLE can set x2EarnApps address correctly", async function () { const { xAllocationVoting, owner, x2EarnApps } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.CONTRACTS_ADDRESS_MANAGER_ROLE(), owner.address)).to.be.true; const oldX2EarnAppsAddress = await x2EarnApps.getAddress(); const tx = await xAllocationVoting.connect(owner).setX2EarnAppsAddress(owner.address); const x2EarnAppsSetIface = new hardhat_1.ethers.Interface([ "event X2EarnAppsSet(address oldContractAddress, address newContractAddress)", ]); const receipt = await tx.wait(); let x2EarnAppsSetEvent; for (const log of receipt?.logs ?? []) { try { const p = x2EarnAppsSetIface.parseLog({ topics: [...log.topics], data: log.data }); if (p !== null && p.name === "X2EarnAppsSet") { x2EarnAppsSetEvent = p; break; } } catch { /* ignore non-matching logs */ } } (0, chai_1.expect)(x2EarnAppsSetEvent).to.not.be.undefined; (0, chai_1.expect)(x2EarnAppsSetEvent.args.oldContractAddress).to.equal(oldX2EarnAppsAddress); (0, chai_1.expect)(x2EarnAppsSetEvent.args.newContractAddress).to.equal(owner.address); }); (0, mocha_1.it)("Cannot set x2EarnApps address to zero address", async function () { const { xAllocationVoting, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); await (0, chai_1.expect)(xAllocationVoting.connect(owner).setX2EarnAppsAddress(helpers_1.ZERO_ADDRESS)).to.be.reverted; }); (0, mocha_1.it)("Only admin with CONTRACTS_ADDRESS_MANAGER_ROLE can set x2EarnApps address", async function () { const { xAllocationVoting, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.CONTRACTS_ADDRESS_MANAGER_ROLE(), otherAccount.address)).to.be.false; await (0, chai_1.expect)(xAllocationVoting.connect(otherAccount).setX2EarnAppsAddress(otherAccount.address)).to.be .reverted; }); }); (0, mocha_1.describe)("VoterRewards address", function () { (0, mocha_1.it)("Admin with CONTRACTS_ADDRESS_MANAGER_ROLE can set voter rewards address correctly", async function () { const { xAllocationVoting, owner, voterRewards } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await xAllocationVoting.hasRole(await xAllocationVoting.CONTRACTS_ADDRESS_MANAGER_ROLE(), owner.address)).to.be.true; const oldVoterRewardsAddress = await voterRewards.getAddress(); const tx = await xAllocationVoting.connect(owner).setVoterRewardsAddress(owner.address); const voterRewardsSetIface = new hardhat_1.ethers.Interface([ "event VoterRewardsSet(address oldContractAddress, address newContractAddress)", ]); const receipt = await tx.wait(); let voterRewardsSetEvent; for (const log of receipt?.logs ?? []) { try { const p = voterRewardsSetIface.parseLog({ topics: [...log.topics], data: log.data }); if (p !== null && p.name === "VoterRewardsSet") { voterRewardsSetEvent = p; break; } } catch { /* ignore non-matching logs */ } } (0, chai_1.expect)(voterRewardsSetEvent).to.not.be.undefined; (0, chai_1.expect)(voterRewardsSetEvent.args.oldContractAddress).to.equal(oldVoterRewardsAddress); (0, chai_1.expect)(voterRewardsSetEvent.args.newContractAddress).to.equal(owner.address); }); (0, mocha_1.it)("Cannot set voter rewards address to zero address", async function () { const { xAllocationVoting, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, }); await (0, chai_1.expect)(xAllocationVoting.connect(owner).setVoterRewardsAddress(helpers_1.ZERO_ADDRESS)).to.be.reverted; }); (0, mocha_1.it)("Only admin with CONTRACTS_ADDRESS_MANAGER_ROLE can