@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
JavaScript
"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