UNPKG

@vechain/vebetterdao-contracts

Version:

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

777 lines 75.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const { ethers } = require("hardhat"); const chai_1 = require("chai"); const mocha_1 = require("mocha"); const upgrades_1 = require("../../scripts/helpers/upgrades"); const deploy_1 = require("../helpers/deploy"); const local_1 = require("@repo/config/contracts/envs/local"); const helpers_1 = require("../helpers"); const xnodes_1 = require("../helpers/xnodes"); (0, mocha_1.describe)("VoterRewards Upgrade Test - @shard10a", function () { let creator1; before(async function () { const config = await (0, deploy_1.getOrDeployContractInstances)({ forceDeploy: true }); if (!config) throw new Error("Failed to deploy contracts"); creator1 = config.creators[0]; }); (0, mocha_1.it)("Should not have state conflict after upgrading to V3 and V4", async () => { const config = (0, local_1.createLocalConfig)(); const contractConfig = await (0, deploy_1.getOrDeployContractInstances)({ forceDeploy: true, }); if (!contractConfig) throw new Error("Failed to deploy contracts"); const { otherAccounts, otherAccount: voter1, owner, emissions, b3tr, timeLock, vechainNodesMock, nodeManagement, vot3, treasury, x2EarnApps, xAllocationPool, governorClockLogicLibV1, governorConfiguratorLibV1, governorDepositLogicLibV1, governorFunctionRestrictionsLogicLibV1, governorProposalLogicLibV1, governorQuorumLogicLibV1, governorStateLogicLibV1, governorVotesLogicLibV1, governorClockLogicLibV3, governorConfiguratorLibV3, governorDepositLogicLibV3, governorFunctionRestrictionsLogicLibV3, governorProposalLogicLibV3, governorQuorumLogicLibV3, governorStateLogicLibV3, governorVotesLogicLibV3, governorClockLogicLibV4, governorConfiguratorLibV4, governorDepositLogicLibV4, governorFunctionRestrictionsLogicLibV4, governorProposalLogicLibV4, governorQuorumLogicLibV4, governorStateLogicLibV4, governorVotesLogicLibV4, veBetterPassport, } = contractConfig; const galaxyMemberV1 = (await (0, upgrades_1.deployProxy)("GalaxyMemberV1", [ { name: "galaxyMember", symbol: "GM", admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 1, baseTokenURI: config.GM_NFT_BASE_URI, xNodeMaxMintableLevels: [1, 2, 3, 4, 5, 6, 7], b3trToUpgradeToLevel: [1000000n], b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ])); const voterRewardsV1 = (await (0, upgrades_1.deployProxy)("VoterRewardsV1", [ owner.address, // admin owner.address, // upgrader owner.address, // contractsAddressManager await emissions.getAddress(), await galaxyMemberV1.getAddress(), await b3tr.getAddress(), deploy_1.levels, deploy_1.multipliers, ])); // Deploy XAllocationVoting const xAllocationVotingV2 = (await (0, upgrades_1.deployAndUpgrade)(["XAllocationVotingV1", "XAllocationVotingV2"], [ [ { 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 voterRewardsV1.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], })); // Deploy Governor const governorV4 = (await (0, upgrades_1.deployAndUpgrade)(["B3TRGovernorV1", "B3TRGovernorV2", "B3TRGovernorV3", "B3TRGovernorV4"], [ [ { vot3Token: await vot3.getAddress(), timelock: await timeLock.getAddress(), xAllocationVoting: await xAllocationVotingV2.getAddress(), b3tr: await b3tr.getAddress(), quorumPercentage: config.B3TR_GOVERNOR_QUORUM_PERCENTAGE, // quorum percentage initialDepositThreshold: config.B3TR_GOVERNOR_DEPOSIT_THRESHOLD, // deposit threshold initialMinVotingDelay: config.B3TR_GOVERNOR_MIN_VOTING_DELAY, // delay before vote starts initialVotingThreshold: config.B3TR_GOVERNOR_VOTING_THRESHOLD, // voting threshold voterRewards: await voterRewardsV1.getAddress(), isFunctionRestrictionEnabled: true, }, { governorAdmin: owner.address, pauser: owner.address, contractsAddressManager: owner.address, proposalExecutor: owner.address, governorFunctionSettingsRoleAddress: owner.address, }, ], [], [], [await veBetterPassport.getAddress()], ], { versions: [undefined, 2, 3, 4], libraries: [ { GovernorClockLogicV1: await governorClockLogicLibV1.getAddress(), GovernorConfiguratorV1: await governorConfiguratorLibV1.getAddress(), GovernorDepositLogicV1: await governorDepositLogicLibV1.getAddress(), GovernorFunctionRestrictionsLogicV1: await governorFunctionRestrictionsLogicLibV1.getAddress(), GovernorProposalLogicV1: await governorProposalLogicLibV1.getAddress(), GovernorQuorumLogicV1: await governorQuorumLogicLibV1.getAddress(), GovernorStateLogicV1: await governorStateLogicLibV1.getAddress(), GovernorVotesLogicV1: await governorVotesLogicLibV1.getAddress(), }, { GovernorClockLogicV1: await governorClockLogicLibV1.getAddress(), GovernorConfiguratorV1: await governorConfiguratorLibV1.getAddress(), GovernorDepositLogicV1: await governorDepositLogicLibV1.getAddress(), GovernorFunctionRestrictionsLogicV1: await governorFunctionRestrictionsLogicLibV1.getAddress(), GovernorProposalLogicV1: await governorProposalLogicLibV1.getAddress(), GovernorQuorumLogicV1: await governorQuorumLogicLibV1.getAddress(), GovernorStateLogicV1: await governorStateLogicLibV1.getAddress(), GovernorVotesLogicV1: await governorVotesLogicLibV1.getAddress(), }, { GovernorClockLogicV3: await governorClockLogicLibV3.getAddress(), GovernorConfiguratorV3: await governorConfiguratorLibV3.getAddress(), GovernorDepositLogicV3: await governorDepositLogicLibV3.getAddress(), GovernorFunctionRestrictionsLogicV3: await governorFunctionRestrictionsLogicLibV3.getAddress(), GovernorProposalLogicV3: await governorProposalLogicLibV3.getAddress(), GovernorQuorumLogicV3: await governorQuorumLogicLibV3.getAddress(), GovernorStateLogicV3: await governorStateLogicLibV3.getAddress(), GovernorVotesLogicV3: await governorVotesLogicLibV3.getAddress(), }, { GovernorClockLogicV4: await governorClockLogicLibV4.getAddress(), GovernorConfiguratorV4: await governorConfiguratorLibV4.getAddress(), GovernorDepositLogicV4: await governorDepositLogicLibV4.getAddress(), GovernorFunctionRestrictionsLogicV4: await governorFunctionRestrictionsLogicLibV4.getAddress(), GovernorProposalLogicV4: await governorProposalLogicLibV4.getAddress(), GovernorQuorumLogicV4: await governorQuorumLogicLibV4.getAddress(), GovernorStateLogicV4: await governorStateLogicLibV4.getAddress(), GovernorVotesLogicV4: await governorVotesLogicLibV4.getAddress(), }, ], })); await xAllocationPool.connect(owner).setXAllocationVotingAddress(await xAllocationVotingV2.getAddress()); // Grant Vote registrar role to XAllocationVoting await voterRewardsV1 .connect(owner) .grantRole(await voterRewardsV1.VOTE_REGISTRAR_ROLE(), await xAllocationVotingV2.getAddress()); // Grant Vote registrar role to Governor await voterRewardsV1 .connect(owner) .grantRole(await voterRewardsV1.VOTE_REGISTRAR_ROLE(), await governorV4.getAddress()); // Grant admin role to voter rewards for registering x allocation voting await xAllocationVotingV2 .connect(owner) .grantRole(await xAllocationVotingV2.DEFAULT_ADMIN_ROLE(), emissions.getAddress()); // Set xAllocationGovernor in emissions await emissions.connect(owner).setXAllocationsGovernorAddress(await xAllocationVotingV2.getAddress()); await emissions.connect(owner).setVote2EarnAddress(await voterRewardsV1.getAddress()); // Setup XAllocationPool addresses await xAllocationPool.connect(owner).setXAllocationVotingAddress(await xAllocationVotingV2.getAddress()); await xAllocationPool.connect(owner).setEmissionsAddress(await emissions.getAddress()); //Set the emissions address and the admin as the ROUND_STARTER_ROLE in XAllocationVoting const roundStarterRole = await xAllocationVotingV2.ROUND_STARTER_ROLE(); await xAllocationVotingV2 .connect(owner) .grantRole(roundStarterRole, await emissions.getAddress()) .then(async (tx) => await tx.wait()); await xAllocationVotingV2 .connect(owner) .grantRole(roundStarterRole, owner.address) .then(async (tx) => await tx.wait()); await x2EarnApps .connect(owner) .submitApp(otherAccounts[0].address, otherAccounts[0].address, otherAccounts[0].address, "metadataURI"); const app1 = ethers.keccak256(ethers.toUtf8Bytes(otherAccounts[0].address)); await (0, xnodes_1.endorseApp)(app1, otherAccounts[0]); await x2EarnApps .connect(creator1) .submitApp(otherAccounts[1].address, otherAccounts[1].address, otherAccounts[1].address, "metadataURI"); const app2 = ethers.keccak256(ethers.toUtf8Bytes(otherAccounts[1].address)); await (0, xnodes_1.endorseApp)(app2, otherAccounts[1]); const voter2 = otherAccounts[3]; const voter3 = otherAccounts[4]; await (0, helpers_1.getVot3Tokens)(voter1, "1000"); await (0, helpers_1.getVot3Tokens)(voter2, "1000"); await (0, helpers_1.getVot3Tokens)(voter3, "1000"); // Bootstrap emissions await (0, helpers_1.bootstrapAndStartEmissions)(); const roundId = await xAllocationVotingV2.currentRoundId(); (0, chai_1.expect)(roundId).to.equal(1); (0, chai_1.expect)(Number(await xAllocationVotingV2.roundDeadline(roundId))).to.lt(Number(await emissions.getNextCycleBlock())); await veBetterPassport.whitelist(voter1.address); await veBetterPassport.whitelist(voter2.address); await veBetterPassport.whitelist(voter3.address); await veBetterPassport.toggleCheck(1); await xAllocationVotingV2.connect(voter1).castVote(roundId, [app1], [ethers.parseEther("1000")]); await xAllocationVotingV2 .connect(voter2) .castVote(roundId, [app1, app2], [ethers.parseEther("200"), ethers.parseEther("100")]); await xAllocationVotingV2 .connect(voter3) .castVote(roundId, [app1, app2], [ethers.parseEther("500"), ethers.parseEther("500")]); (0, chai_1.expect)(await emissions.isCycleEnded(1)).to.equal(false); await (0, helpers_1.catchRevert)(voterRewardsV1.claimReward(1, voter1.address)); (0, chai_1.expect)(await voterRewardsV1.cycleToVoterToTotal(1, voter1)).to.equal(ethers.parseEther("31.622776601")); (0, chai_1.expect)(await voterRewardsV1.cycleToVoterToTotal(1, voter2)).to.equal(ethers.parseEther("17.320508075")); await (0, helpers_1.catchRevert)(voterRewardsV1.claimReward(1, voter2.address)); (0, chai_1.expect)(await voterRewardsV1.cycleToVoterToTotal(1, voter3)).to.equal(ethers.parseEther("31.622776601")); // Votes should be tracked correctly let appVotes = await xAllocationVotingV2.getAppVotes(roundId, app1); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("1700")); appVotes = await xAllocationVotingV2.getAppVotes(roundId, app2); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("600")); let totalVotes = await xAllocationVotingV2.totalVotes(roundId); (0, chai_1.expect)(totalVotes).to.eql(ethers.parseEther("2300")); // Total voters should be tracked correctly let totalVoters = await xAllocationVotingV2.totalVoters(roundId); (0, chai_1.expect)(totalVoters).to.eql(BigInt(3)); // Voter rewards checks (0, chai_1.expect)(await voterRewardsV1.cycleToTotal(1)).to.equal(ethers.parseEther("80.566061277")); // Total votes -> Math.sqrt(1000) + Math.sqrt(300) + Math.sqrt(1000) (0, chai_1.expect)(await voterRewardsV1.cycleToTotal(1)).to.equal((await voterRewardsV1.cycleToVoterToTotal(1, voter1)) + (await voterRewardsV1.cycleToVoterToTotal(1, voter2)) + (await voterRewardsV1.cycleToVoterToTotal(1, voter3))); // Total votes let storageSlots = []; const initialSlot = BigInt("0x114e7ffaaf205d38cd05b17b56f3357806ef2ce889cb4748445ae91cdfc37c00"); // Slot 0 of VoterRewards for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlots.push(await ethers.provider.getStorage(await voterRewardsV1.getAddress(), i)); } storageSlots = storageSlots.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // removing empty slots const voterRewardsV2 = (await (0, upgrades_1.upgradeProxy)("VoterRewardsV1", "VoterRewardsV2", await voterRewardsV1.getAddress(), [], { version: 2, })); let storageSlotsAfter = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsAfter.push(await ethers.provider.getStorage(await voterRewardsV2.getAddress(), i)); } // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { (0, chai_1.expect)(storageSlots[i]).to.equal(storageSlotsAfter[i]); } await (0, helpers_1.waitForRoundToEnd)(Number(roundId)); // Votes should be the same after round ended appVotes = await xAllocationVotingV2.getAppVotes(roundId, app1); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("1700")); appVotes = await xAllocationVotingV2.getAppVotes(roundId, app2); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("600")); totalVotes = await xAllocationVotingV2.totalVotes(roundId); (0, chai_1.expect)(totalVotes).to.eql(ethers.parseEther("2300")); await (0, helpers_1.waitForNextCycle)(); (0, chai_1.expect)(await emissions.isCycleDistributed(await emissions.nextCycle())).to.equal(false); (0, chai_1.expect)(await emissions.isNextCycleDistributable()).to.equal(true); // Reward claiming (0, chai_1.expect)(await emissions.isCycleDistributed(1)).to.equal(true); (0, chai_1.expect)(await b3tr.balanceOf(await voterRewardsV2.getAddress())).to.equal(await emissions.getVote2EarnAmount(1)); const voter1Rewards = await voterRewardsV2.getReward(1, voter1.address); const voter2Rewards = await voterRewardsV2.getReward(1, voter2.address); const voter3Rewards = await voterRewardsV2.getReward(1, voter3.address); await voterRewardsV2.connect(voter1).claimReward(1, voter1); (0, chai_1.expect)(await b3tr.balanceOf(voter1.address)).to.equal(voter1Rewards); (0, chai_1.expect)(await b3tr.balanceOf(await voterRewardsV2.getAddress())).to.equal((await emissions.getVote2EarnAmount(1)) - voter1Rewards); // Second round await emissions.connect(voter1).distribute(); // Anyone can distribute the cycle const roundId2 = await xAllocationVotingV2.currentRoundId(); (0, chai_1.expect)(roundId2).to.equal(2); (0, chai_1.expect)(Number(await xAllocationVotingV2.roundDeadline(roundId))).to.lt(Number(await emissions.getNextCycleBlock())); await xAllocationVotingV2.connect(voter1).castVote(roundId2, [app2], [ethers.parseEther("1000")]); await xAllocationVotingV2 .connect(voter2) .castVote(roundId2, [app1, app2], [ethers.parseEther("100"), ethers.parseEther("500")]); await xAllocationVotingV2 .connect(voter3) .castVote(roundId2, [app1, app2], [ethers.parseEther("500"), ethers.parseEther("500")]); (0, chai_1.expect)(await emissions.isCycleEnded(2)).to.equal(false); await (0, helpers_1.catchRevert)(voterRewardsV2.claimReward(2, voter1.address)); (0, chai_1.expect)(await voterRewardsV2.cycleToVoterToTotal(2, voter1)).to.equal(ethers.parseEther("31.622776601")); (0, chai_1.expect)(await voterRewardsV2.cycleToVoterToTotal(2, voter2)).to.equal(ethers.parseEther("24.494897427")); await (0, helpers_1.catchRevert)(voterRewardsV2.claimReward(2, voter2.address)); (0, chai_1.expect)(await voterRewardsV2.cycleToVoterToTotal(2, voter3)).to.equal(ethers.parseEther("31.622776601")); // Votes should be tracked correctly appVotes = await xAllocationVotingV2.getAppVotes(roundId2, app1); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("600")); appVotes = await xAllocationVotingV2.getAppVotes(roundId2, app2); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("2000")); totalVotes = await xAllocationVotingV2.totalVotes(roundId2); (0, chai_1.expect)(totalVotes).to.eql(ethers.parseEther("2600")); // Total voters should be tracked correctly totalVoters = await xAllocationVotingV2.totalVoters(roundId2); (0, chai_1.expect)(totalVoters).to.eql(BigInt(3)); // Voter rewards checks (0, chai_1.expect)(await voterRewardsV2.cycleToTotal(2)).to.equal(ethers.parseEther("87.740450629")); // Total votes -> Math.sqrt(1000) + Math.sqrt(300) + Math.sqrt(1000) (0, chai_1.expect)(await voterRewardsV2.cycleToTotal(2)).to.equal((await voterRewardsV2.cycleToVoterToTotal(2, voter1)) + (await voterRewardsV2.cycleToVoterToTotal(2, voter2)) + (await voterRewardsV2.cycleToVoterToTotal(2, voter3))); // Total votes await (0, helpers_1.waitForRoundToEnd)(Number(roundId2)); // Votes should be the same after round ended appVotes = await xAllocationVotingV2.getAppVotes(roundId2, app1); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("600")); appVotes = await xAllocationVotingV2.getAppVotes(roundId2, app2); (0, chai_1.expect)(appVotes).to.eql(ethers.parseEther("2000")); totalVotes = await xAllocationVotingV2.totalVotes(roundId2); (0, chai_1.expect)(totalVotes).to.eql(ethers.parseEther("2600")); await (0, helpers_1.waitForNextCycle)(); (0, chai_1.expect)(await emissions.isCycleEnded(2)).to.equal(true); (0, chai_1.expect)(await emissions.isCycleDistributed(await emissions.nextCycle())).to.equal(false); (0, chai_1.expect)(await emissions.isNextCycleDistributable()).to.equal(true); // Reward claiming (0, chai_1.expect)(await emissions.isCycleDistributed(2)).to.equal(true); (0, chai_1.expect)(Number(await b3tr.balanceOf(await voterRewardsV2.getAddress()))).to.gt(Number(await emissions.getVote2EarnAmount(2))); // Voters of round 1 can still claim rewards of round 1 thus the balance of VoterRewards contract should be greater than the emission amount const voter1Rewards2 = await voterRewardsV2.getReward(2, voter1.address); const voter2Rewards2 = await voterRewardsV2.getReward(2, voter2.address); const voter3Rewards2 = await voterRewardsV2.getReward(2, voter3.address); await voterRewardsV2.connect(voter1).claimReward(2, voter1); await voterRewardsV2.connect(voter2).claimReward(2, voter2); await voterRewardsV2.connect(voter3).claimReward(2, voter3); (0, chai_1.expect)(await b3tr.balanceOf(voter1.address)).to.equal(voter1Rewards + voter1Rewards2); // Voter 1 claimed also rewards of round 1 (0, chai_1.expect)(await b3tr.balanceOf(voter2.address)).to.equal(voter2Rewards2); (0, chai_1.expect)(await b3tr.balanceOf(voter3.address)).to.equal(voter3Rewards2); // Voters of round 1 can still claim rewards of round 1 await voterRewardsV2.connect(voter2).claimReward(1, voter2); await voterRewardsV2.connect(voter3).claimReward(1, voter3); (0, chai_1.expect)(await b3tr.balanceOf(voter2.address)).to.equal(voter2Rewards + voter2Rewards2); (0, chai_1.expect)(await b3tr.balanceOf(voter3.address)).to.equal(voter3Rewards + voter3Rewards2); // Check if storage slots are the same after upgrade storageSlots = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlots.push(await ethers.provider.getStorage(await voterRewardsV2.getAddress(), i)); } ; (await (0, upgrades_1.upgradeProxy)("GalaxyMemberV1", "GalaxyMemberV2", await galaxyMemberV1.getAddress(), [ await vechainNodesMock.getAddress(), await nodeManagement.getAddress(), owner.address, config.GM_NFT_NODE_TO_FREE_LEVEL, ], { version: 2 })); const voterRewardsV3 = (await (0, upgrades_1.upgradeProxy)("VoterRewardsV2", "VoterRewardsV3", await voterRewardsV1.getAddress(), [], { version: 3, })); await (0, helpers_1.waitForNextCycle)(); // start round await emissions.connect(voter1).distribute(); // Anyone can distribute the cycle const roundId3 = await xAllocationVotingV2.currentRoundId(); (0, chai_1.expect)(roundId3).to.equal(3); await xAllocationVotingV2 .connect(voter1) .castVote(roundId3, [app1, app2], [ethers.parseEther("0"), ethers.parseEther("1000")]); await xAllocationVotingV2 .connect(voter2) .castVote(roundId3, [app1, app2], [ethers.parseEther("100"), ethers.parseEther("500")]); await (0, helpers_1.waitForRoundToEnd)(Number(roundId3)); // Check storage slots after upgrade storageSlotsAfter = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsAfter.push(await ethers.provider.getStorage(await voterRewardsV3.getAddress(), i)); } // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { (0, chai_1.expect)(storageSlots[i]).to.equal(storageSlotsAfter[i]); } // Upgrade to V3 await (0, upgrades_1.upgradeProxy)("GalaxyMemberV2", "GalaxyMemberV3", await galaxyMemberV1.getAddress(), [], { version: 3, }); const voterRewardsV4 = (await (0, upgrades_1.upgradeProxy)("VoterRewardsV3", "VoterRewardsV4", await voterRewardsV1.getAddress(), [], { version: 3, })); let storageSlotsV4 = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsV4.push(await ethers.provider.getStorage(await voterRewardsV4.getAddress(), i)); } // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { (0, chai_1.expect)(storageSlotsAfter[i]).to.equal(storageSlotsV4[i]); } await (0, helpers_1.waitForNextCycle)(); // start round await emissions.connect(voter1).distribute(); // Anyone can distribute the cycle const roundId4 = await xAllocationVotingV2.currentRoundId(); (0, chai_1.expect)(roundId4).to.equal(4); await xAllocationVotingV2 .connect(voter1) .castVote(roundId4, [app1, app2], [ethers.parseEther("0"), ethers.parseEther("1000")]); await xAllocationVotingV2 .connect(voter2) .castVote(roundId4, [app1, app2], [ethers.parseEther("100"), ethers.parseEther("500")]); // Wait for round to end const deadline = await xAllocationVotingV2.roundDeadline(roundId4); const currentBlock = await xAllocationVotingV2.clock(); await (0, helpers_1.moveBlocks)(parseInt((deadline - currentBlock + BigInt(1)).toString())); await (0, chai_1.expect)(voterRewardsV4.connect(voter1).claimReward(4, voter1)).to.emit(voterRewardsV4, "RewardClaimed"); await (0, chai_1.expect)(voterRewardsV4.connect(voter2).claimReward(4, voter2)).to.emit(voterRewardsV4, "RewardClaimed"); }); (0, mocha_1.it)("Should not have state conflict after upgrading to V5 and then to V6", async () => { // ======================================== // INITIAL SETUP AND CONTRACT DEPLOYMENT // ======================================== const config = (0, local_1.createLocalConfig)(); const contractConfig = await (0, deploy_1.getOrDeployContractInstances)({ forceDeploy: true, }); if (!contractConfig) throw new Error("Failed to deploy contracts"); const { otherAccounts, otherAccount: voter1, owner, b3tr, timeLock, nodeManagement, vot3, treasury, x2EarnApps, veBetterPassport, xAllocationPool, governorClockLogicLibV1, governorConfiguratorLibV1, governorDepositLogicLibV1, governorFunctionRestrictionsLogicLibV1, governorProposalLogicLibV1, governorQuorumLogicLibV1, governorStateLogicLibV1, governorVotesLogicLibV1, governorClockLogicLibV3, governorConfiguratorLibV3, governorDepositLogicLibV3, governorFunctionRestrictionsLogicLibV3, governorProposalLogicLibV3, governorQuorumLogicLibV3, governorStateLogicLibV3, governorVotesLogicLibV3, governorClockLogicLibV4, governorConfiguratorLibV4, governorDepositLogicLibV4, governorFunctionRestrictionsLogicLibV4, governorProposalLogicLibV4, governorQuorumLogicLibV4, governorStateLogicLibV4, governorVotesLogicLibV4, governorClockLogicLibV5, governorConfiguratorLibV5, governorDepositLogicLibV5, governorFunctionRestrictionsLogicLibV5, governorQuorumLogicLibV5, governorStateLogicLibV5, governorVotesLogicLibV5, governorProposalLogicLibV5, governorClockLogicLibV6, governorConfiguratorLibV6, governorDepositLogicLibV6, governorFunctionRestrictionsLogicLibV6, governorProposalLogicLibV6, governorQuorumLogicLibV6, governorStateLogicLibV6, governorVotesLogicLibV6, governorClockLogicLib, governorConfiguratorLib, governorDepositLogicLib, governorFunctionRestrictionsLogicLib, governorProposalLogicLib, governorQuorumLogicLib, governorStateLogicLib, governorVotesLogicLib, minterAccount, } = contractConfig; const galaxyMember = (await (0, upgrades_1.deployAndUpgrade)(["GalaxyMemberV1", "GalaxyMemberV2"], [ [ { name: "galaxyMember", symbol: "GM", admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 10, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: config.GM_NFT_B3TR_REQUIRED_TO_UPGRADE_TO_LEVEL, b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ], [owner.address, await nodeManagement.getAddress(), owner.address, config.GM_NFT_NODE_TO_FREE_LEVEL], ], { versions: [undefined, 2] })); // Deploy Emissions contract const emissions = (await (0, upgrades_1.deployAndUpgrade)(["EmissionsV1", "EmissionsV2"], [ [ { 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, }, ], [config.EMISSIONS_IS_NOT_ALIGNED], ], { versions: [undefined, 2], logOutput: false, })); // Deploy VoterRewards V4 contract const voterRewardsV4 = (await (0, upgrades_1.deployAndUpgrade)(["VoterRewardsV1", "VoterRewardsV2", "VoterRewardsV3", "VoterRewardsV4"], [ [ owner.address, // admin owner.address, // upgrader owner.address, // contractsAddressManager await emissions.getAddress(), await galaxyMember.getAddress(), await b3tr.getAddress(), deploy_1.levels, deploy_1.multipliers, ], [], [], [], ], { versions: [undefined, 2, 3, 4], })); await voterRewardsV4.connect(owner).setEmissions(await emissions.getAddress()); // Deploy XAllocationVoting contract const xAllocationVoting = (await (0, upgrades_1.deployAndUpgrade)([ "XAllocationVotingV1", "XAllocationVotingV2", "XAllocationVotingV3", "XAllocationVotingV4", "XAllocationVotingV5", ], [ [ { 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 voterRewardsV4.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, 4, 5], logOutput: false, })); // Deploy B3TR Governor V5 contract const governor = (await (0, upgrades_1.deployAndUpgrade)(["B3TRGovernorV1", "B3TRGovernorV2", "B3TRGovernorV3", "B3TRGovernorV4", "B3TRGovernorV5"], [ [ { vot3Token: await vot3.getAddress(), timelock: await timeLock.getAddress(), xAllocationVoting: await xAllocationVoting.getAddress(), b3tr: await b3tr.getAddress(), quorumPercentage: config.B3TR_GOVERNOR_QUORUM_PERCENTAGE, initialDepositThreshold: config.B3TR_GOVERNOR_DEPOSIT_THRESHOLD, initialMinVotingDelay: config.B3TR_GOVERNOR_MIN_VOTING_DELAY, initialVotingThreshold: config.B3TR_GOVERNOR_VOTING_THRESHOLD, voterRewards: await voterRewardsV4.getAddress(), isFunctionRestrictionEnabled: true, }, { governorAdmin: owner.address, // admin pauser: owner.address, // botSignaler contractsAddressManager: owner.address, // upgrader proposalExecutor: owner.address, // settingsManager governorFunctionSettingsRoleAddress: owner.address, // roleGranter }, ], [], [], [await veBetterPassport.getAddress()], [], ], { versions: [undefined, 2, 3, 4, 5], libraries: [ { GovernorClockLogicV1: await governorClockLogicLibV1.getAddress(), GovernorConfiguratorV1: await governorConfiguratorLibV1.getAddress(), GovernorDepositLogicV1: await governorDepositLogicLibV1.getAddress(), GovernorFunctionRestrictionsLogicV1: await governorFunctionRestrictionsLogicLibV1.getAddress(), GovernorProposalLogicV1: await governorProposalLogicLibV1.getAddress(), GovernorQuorumLogicV1: await governorQuorumLogicLibV1.getAddress(), GovernorStateLogicV1: await governorStateLogicLibV1.getAddress(), GovernorVotesLogicV1: await governorVotesLogicLibV1.getAddress(), }, { GovernorClockLogicV1: await governorClockLogicLibV1.getAddress(), GovernorConfiguratorV1: await governorConfiguratorLibV1.getAddress(), GovernorDepositLogicV1: await governorDepositLogicLibV1.getAddress(), GovernorFunctionRestrictionsLogicV1: await governorFunctionRestrictionsLogicLibV1.getAddress(), GovernorProposalLogicV1: await governorProposalLogicLibV1.getAddress(), GovernorQuorumLogicV1: await governorQuorumLogicLibV1.getAddress(), GovernorStateLogicV1: await governorStateLogicLibV1.getAddress(), GovernorVotesLogicV1: await governorVotesLogicLibV1.getAddress(), }, { GovernorClockLogicV3: await governorClockLogicLibV3.getAddress(), GovernorConfiguratorV3: await governorConfiguratorLibV3.getAddress(), GovernorDepositLogicV3: await governorDepositLogicLibV3.getAddress(), GovernorFunctionRestrictionsLogicV3: await governorFunctionRestrictionsLogicLibV3.getAddress(), GovernorProposalLogicV3: await governorProposalLogicLibV3.getAddress(), GovernorQuorumLogicV3: await governorQuorumLogicLibV3.getAddress(), GovernorStateLogicV3: await governorStateLogicLibV3.getAddress(), GovernorVotesLogicV3: await governorVotesLogicLibV3.getAddress(), }, { GovernorClockLogicV4: await governorClockLogicLibV4.getAddress(), GovernorConfiguratorV4: await governorConfiguratorLibV4.getAddress(), GovernorDepositLogicV4: await governorDepositLogicLibV4.getAddress(), GovernorFunctionRestrictionsLogicV4: await governorFunctionRestrictionsLogicLibV4.getAddress(), GovernorProposalLogicV4: await governorProposalLogicLibV4.getAddress(), GovernorQuorumLogicV4: await governorQuorumLogicLibV4.getAddress(), GovernorStateLogicV4: await governorStateLogicLibV4.getAddress(), GovernorVotesLogicV4: await governorVotesLogicLibV4.getAddress(), }, { GovernorClockLogicV5: await governorClockLogicLibV5.getAddress(), GovernorConfiguratorV5: await governorConfiguratorLibV5.getAddress(), GovernorDepositLogicV5: await governorDepositLogicLibV5.getAddress(), GovernorFunctionRestrictionsLogicV5: await governorFunctionRestrictionsLogicLibV5.getAddress(), GovernorProposalLogicV5: await governorProposalLogicLibV5.getAddress(), GovernorQuorumLogicV5: await governorQuorumLogicLibV5.getAddress(), GovernorStateLogicV5: await governorStateLogicLibV5.getAddress(), GovernorVotesLogicV5: await governorVotesLogicLibV5.getAddress(), }, ], })); // ======================================== // SETUP ROLES AND PERMISSIONS // ======================================== await xAllocationPool.connect(owner).setXAllocationVotingAddress(await xAllocationVoting.getAddress()); // Grant Vote registrar role to XAllocationVoting await voterRewardsV4 .connect(owner) .grantRole(await voterRewardsV4.VOTE_REGISTRAR_ROLE(), await xAllocationVoting.getAddress()); // Grant Vote registrar role to Governor await voterRewardsV4 .connect(owner) .grantRole(await voterRewardsV4.VOTE_REGISTRAR_ROLE(), await governor.getAddress()); // Grant admin role to voter rewards for registering x allocation voting await xAllocationVoting .connect(owner) .grantRole(await xAllocationVoting.DEFAULT_ADMIN_ROLE(), emissions.getAddress()); // Set xAllocationGovernor in emissions await emissions.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); await emissions.connect(owner).setVote2EarnAddress(await voterRewardsV4.getAddress()); // Setup XAllocationPool addresses await xAllocationPool.connect(owner).setXAllocationVotingAddress(await xAllocationVoting.getAddress()); await xAllocationPool.connect(owner).setEmissionsAddress(await emissions.getAddress()); await b3tr.connect(owner).grantRole(await b3tr.MINTER_ROLE(), await emissions.getAddress()); //Set the emissions address and the admin as the ROUND_STARTER_ROLE in XAllocationVoting const roundStarterRole = await xAllocationVoting.ROUND_STARTER_ROLE(); await xAllocationVoting .connect(owner) .grantRole(roundStarterRole, await emissions.getAddress()) .then(async (tx) => await tx.wait()); await xAllocationVoting .connect(owner) .grantRole(roundStarterRole, owner.address) .then(async (tx) => await tx.wait()); await galaxyMember.connect(owner).setB3trGovernorAddress(await governor.getAddress()); await galaxyMember.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); // ======================================== // SETUP APPS AND VOTERS // ======================================== await x2EarnApps .connect(owner) .submitApp(otherAccounts[0].address, otherAccounts[0].address, otherAccounts[0].address, "metadataURI"); const app1 = ethers.keccak256(ethers.toUtf8Bytes(otherAccounts[0].address)); await (0, xnodes_1.endorseApp)(app1, otherAccounts[0]); await x2EarnApps .connect(creator1) .submitApp(otherAccounts[1].address, otherAccounts[1].address, otherAccounts[1].address, "metadataURI"); const app2 = ethers.keccak256(ethers.toUtf8Bytes(otherAccounts[1].address)); await (0, xnodes_1.endorseApp)(app2, otherAccounts[1]); const voter2 = otherAccounts[3]; const voter3 = otherAccounts[4]; await (0, helpers_1.getVot3Tokens)(voter1, "1000"); await (0, helpers_1.getVot3Tokens)(voter2, "1000"); await (0, helpers_1.getVot3Tokens)(voter3, "1000"); await veBetterPassport.whitelist(voter1.address); await veBetterPassport.whitelist(voter2.address); await veBetterPassport.whitelist(voter3.address); await veBetterPassport.toggleCheck(1); // ======================================== // START EMISSIONS AND FIRST ROUND // ======================================== // Bootstrap emissions await emissions.connect(minterAccount).bootstrap(); await emissions.connect(minterAccount).start(); const roundId = await xAllocationVoting.currentRoundId(); (0, chai_1.expect)(roundId).to.equal(1); (0, chai_1.expect)(await xAllocationVoting.roundDeadline(roundId)).to.lt(await emissions.getNextCycleBlock()); // ======================================== // ROUND 1: VOTING (PRE-UPGRADE) // ======================================== // Vote on apps for the first round await (0, helpers_1.voteOnApps)([app1, app2], [voter1, voter2, voter3], [ [ethers.parseEther("1000"), ethers.parseEther("0")], // Voter 1 votes 1000 for app1 [ethers.parseEther("500"), ethers.parseEther("500")], // Voter 2 votes 500 for app1 and 500 for app2 [ethers.parseEther("500"), ethers.parseEther("500")], // Voter 3 votes 500 for app1 and 500 for app2 ], roundId, // First round xAllocationVoting, veBetterPassport); // Track Round 1 rewards before claiming const round1Voter1Reward = await voterRewardsV4.getReward(1, voter1.address); const round1Voter2Reward = await voterRewardsV4.getReward(1, voter2.address); const round1Voter3Reward = await voterRewardsV4.getReward(1, voter3.address); // Rewards to be claimed are the same for all voters: (0, chai_1.expect)(await voterRewardsV4.getReward(1, voter1.address)).to.equal(666666666666666666666666n); (0, chai_1.expect)(await voterRewardsV4.getReward(1, voter2.address)).to.equal(666666666666666666666666n); (0, chai_1.expect)(await voterRewardsV4.getReward(1, voter3.address)).to.equal(666666666666666666666666n); await (0, helpers_1.waitForRoundToEnd)(Number(roundId), xAllocationVoting); await (0, helpers_1.waitForNextCycle)(emissions); // Claim Round 1 rewards - verify basic functionality works await (0, chai_1.expect)(voterRewardsV4.connect(voter1).claimReward(1, voter1.address)) .to.emit(voterRewardsV4, "RewardClaimed") .withArgs(1, voter1.address, 666666666666666666666666n); await (0, chai_1.expect)(voterRewardsV4.connect(voter2).claimReward(1, voter2.address)) .to.emit(voterRewardsV4, "RewardClaimed") .withArgs(1, voter2.address, 666666666666666666666666n); await (0, chai_1.expect)(voterRewardsV4.connect(voter3).claimReward(1, voter3.address)) .to.emit(voterRewardsV4, "RewardClaimed") .withArgs(1, voter3.address, 666666666666666666666666n); // ======================================== // SETUP GM NFTS FOR MULTIPLIERS // ======================================== // GM NFT token mint and upgrade await galaxyMember.connect(voter1).freeMint(); await (0, helpers_1.upgradeNFTtoLevel)(1, 5, galaxyMember, b3tr, voter1, minterAccount); // Upgrading to level 5 (0, chai_1.expect)(await galaxyMember.levelOf(await galaxyMember.getSelectedTokenId(voter1.address))).to.equal(5); await galaxyMember.connect(voter2).freeMint(); await (0, helpers_1.upgradeNFTtoLevel)(2, 10, galaxyMember, b3tr, voter2, minterAccount); // Upgrading to level 10 (0, chai_1.expect)(await galaxyMember.levelOf(await galaxyMember.getSelectedTokenId(voter2.address))).to.equal(10); await galaxyMember.connect(voter3).freeMint(); await (0, helpers_1.upgradeNFTtoLevel)(3, 2, galaxyMember, b3tr, voter3, minterAccount); // Upgrading to level 2 (0, chai_1.expect)(await galaxyMember.levelOf(await galaxyMember.getSelectedTokenId(voter3.address))).to.equal(2); // ======================================== // ROUND 2: VOTING (BEFORE V5 UPGRADE) // ======================================== await emissions.connect(voter1).distribute(); // Anyone can distribute the cycle const roundId2 = await xAllocationVoting.currentRoundId(); (0, chai_1.expect)(roundId2).to.equal(2); (0, chai_1.expect)(await xAllocationVoting.roundDeadline(roundId2)).to.lt(await emissions.getNextCycleBlock()); await (0, helpers_1.waitForNextBlock)(); // ======================================== // STORAGE VERIFICATION BEFORE V5 UPGRADE // ======================================== let storageSlots = []; const initialSlot = BigInt("0x114e7ffaaf205d38cd05b17b56f3357806ef2ce889cb4748445ae91cdfc37c00"); // Slot 0 of VoterRewards for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlots.push(await ethers.provider.getStorage(await voterRewardsV4.getAddress(), i)); } storageSlots = storageSlots.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // removing empty slots // ======================================== // UPGRADE TO V5 (GM POOL SUPPORT) // ======================================== // Upgrade Emissions and Voter Rewards to support GM Pool const emissionsLatest = (await (0, upgrades_1.upgradeProxy)("EmissionsV2", "Emissions", await emissions.getAddress(), [config.GM_PERCENTAGE_OF_TREASURY], { version: 3, })); const voterRewardsV5 = (await (0, upgrades_1.upgradeProxy)("VoterRewardsV4", "VoterRewardsV5", await voterRewardsV4.getAddress(), [config.VOTER_REWARDS_LEVELS_V2, config.GM_MULTIPLIERS_V2], { version: 5, })); // Upgrade contracts that use interfaces await (0, upgrades_1.upgradeProxy)("XAllocationVotingV5", "XAllocationVotingV6", await xAllocationVoting.getAddress(), [], { version: 6, }); await (0, upgrades_1.upgradeProxy)("XAllocationPoolV5", "XAllocationPool", await xAllocationPool.getAddress(), [], { version: 6, }); await (0, upgrades_1.upgradeProxy)("B3TRGovernorV5", "B3TRGovernorV6", await governor.getAddress(), [], { version: 6, libraries: { GovernorClockLogicV6: await governorClockLogicLibV6.getAddress(), GovernorConfiguratorV6: await governorConfiguratorLibV6.getAddress(), GovernorDepositLogicV6: await governorDepositLogicLibV6.getAddress(), GovernorFunctionRestrictionsLogicV6: await governorFunctionRest