UNPKG

@vechain/vebetterdao-contracts

Version:

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

733 lines 318 kB
import { ethers } from "hardhat"; import { expect } from "chai"; import { bootstrapAndStartEmissions, bootstrapEmissions, createProposal, linkEntityToPassportWithSignature, getOrDeployContractInstances, getProposalIdFromTx, getVot3Tokens, payDeposit, startNewAllocationRound, waitForNextCycle, waitForProposalToBeActive, delegateWithSignature, moveToCycle, waitForCurrentRoundToEnd, moveBlocks, waitForBlock, waitForNextBlock, participateInAllocationVoting, upgradeNFTtoLevel, } from "./helpers"; import { describe, it } from "mocha"; import { getImplementationAddress } from "@openzeppelin/upgrades-core"; import { ZeroAddress } from "ethers"; import { createTestConfig } from "./helpers/config"; import { deployAndUpgrade, deployProxyOnly, initializeProxy, upgradeProxy } from "../scripts/helpers"; import { endorseApp } from "./helpers/xnodes"; import { createLocalConfig } from "@repo/config/contracts/envs/local"; describe("VeBetterPassport - @shard8-core", function () { describe("Contract parameters", function () { it("Should have contract addresses set correctly", async function () { const { veBetterPassport, xAllocationVoting, x2EarnApps, galaxyMember } = await getOrDeployContractInstances({ forceDeploy: true, }); // Verify contract addresses expect(await veBetterPassport.getXAllocationVoting()).to.equal(await xAllocationVoting.getAddress()); expect(await veBetterPassport.getX2EarnApps()).to.equal(await x2EarnApps.getAddress()); expect(await veBetterPassport.getGalaxyMember()).to.equal(await galaxyMember.getAddress()); }); it("Should have correct roles set", async function () { const { veBetterPassport, owner } = await getOrDeployContractInstances({ forceDeploy: true, }); expect(await veBetterPassport.hasRole(await veBetterPassport.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.true; expect(await veBetterPassport.hasRole(await veBetterPassport.SETTINGS_MANAGER_ROLE(), owner.address)).to.be.true; expect(await veBetterPassport.hasRole(await veBetterPassport.ROLE_GRANTER(), owner.address)).to.be.true; expect(await veBetterPassport.hasRole(await veBetterPassport.SIGNALER_ROLE(), owner.address)).to.be.true; expect(await veBetterPassport.hasRole(await veBetterPassport.WHITELISTER_ROLE(), owner.address)).to.be.true; expect(await veBetterPassport.hasRole(await veBetterPassport.ACTION_REGISTRAR_ROLE(), owner.address)).to.be.true; expect(await veBetterPassport.hasRole(await veBetterPassport.ACTION_SCORE_MANAGER_ROLE(), owner.address)).to.be .true; }); it("Should have action score thresholds set correctly", async function () { const config = createTestConfig(); const { veBetterPassport } = await getOrDeployContractInstances({ forceDeploy: true, config: { ...config, VEPASSPORT_BOT_SIGNALING_THRESHOLD: 5, VEPASSPORT_WHITELIST_THRESHOLD_PERCENTAGE: 20, VEPASSPORT_BLACKLIST_THRESHOLD_PERCENTAGE: 10, }, }); expect(await veBetterPassport.thresholdPoPScore()).to.equal(0); expect(await veBetterPassport.signalingThreshold()).to.equal(5); expect(await veBetterPassport.whitelistThreshold()).to.equal(20); expect(await veBetterPassport.blacklistThreshold()).to.equal(10); }); it("Should have rounds for cumulative score set correctly", async function () { const config = createTestConfig(); const { veBetterPassport } = await getOrDeployContractInstances({ forceDeploy: true, config: { ...config, VEPASSPORT_ROUNDS_FOR_CUMULATIVE_PARTICIPATION_SCORE: 5, }, }); expect(await veBetterPassport.roundsForCumulativeScore()).to.equal(5); }); it("Should have minimum galaxy member level set correctly", async function () { const config = createTestConfig(); const { veBetterPassport } = await getOrDeployContractInstances({ forceDeploy: true, config: { ...config, VEPASSPORT_GALAXY_MEMBER_MINIMUM_LEVEL: 5, }, }); expect(await veBetterPassport.getMinimumGalaxyMemberLevel()).to.equal(5); }); it("Should return correct eip712 domain separator", async function () { const { veBetterPassport } = await getOrDeployContractInstances({ forceDeploy: true, }); const domainSeparator = await veBetterPassport.eip712Domain(); expect(domainSeparator).to.deep.equal([ "0x0f", "VeBetterPassport", "1", 1337n, await veBetterPassport.getAddress(), "0x0000000000000000000000000000000000000000000000000000000000000000", [], ]); }); }); }); // Upgrades section split to separate shard describe("VeBetterPassport Upgrades - @shard8d", function () { describe("Upgrades", function () { it("should return the correct version", async function () { const { veBetterPassport } = await getOrDeployContractInstances({ forceDeploy: true, }); expect(await veBetterPassport.version()).to.equal("5"); }); it("Should not be able to initialize twice", async function () { const config = createTestConfig(); const { veBetterPassport, owner, x2EarnApps, xAllocationVoting, galaxyMember } = await getOrDeployContractInstances({ forceDeploy: true, }); await expect(veBetterPassport.initialize({ x2EarnApps: await x2EarnApps.getAddress(), xAllocationVoting: await xAllocationVoting.getAddress(), galaxyMember: await galaxyMember.getAddress(), signalingThreshold: config.VEPASSPORT_BOT_SIGNALING_THRESHOLD, //signalingThreshold roundsForCumulativeScore: config.VEPASSPORT_ROUNDS_FOR_CUMULATIVE_PARTICIPATION_SCORE, //roundsForCumulativeScore minimumGalaxyMemberLevel: config.VEPASSPORT_GALAXY_MEMBER_MINIMUM_LEVEL, //galaxyMemberMinimumLevel blacklistThreshold: config.VEPASSPORT_BLACKLIST_THRESHOLD_PERCENTAGE, //blacklistThreshold whitelistThreshold: config.VEPASSPORT_WHITELIST_THRESHOLD_PERCENTAGE, //whitelistThreshold maxEntitiesPerPassport: config.VEPASSPORT_PASSPORT_MAX_ENTITIES, //maxEntitiesPerPassport decayRate: config.VEPASSPORT_DECAY_RATE, //decayRate }, { admin: owner.address, // admin botSignaler: owner.address, // botSignaler upgrader: owner.address, // upgrader settingsManager: owner.address, // settingsManager roleGranter: owner.address, // roleGranter blacklister: owner.address, // blacklister whitelister: owner.address, // whitelistManager actionRegistrar: owner.address, // actionRegistrar actionScoreManager: owner.address, // actionScoreManager resetSignaler: owner.address, // resetSignaler })).to.be.reverted; }); it("Should not be able to upgrade if without UPGRADER_ROLE", async function () { const { veBetterPassport, otherAccount, passportChecksLogic, passportConfigurator, passportDelegationLogic, passportPersonhoodLogic, passportPoPScoreLogic, passportSignalingLogic, passportEntityLogic, passportWhitelistAndBlacklistLogic, } = await getOrDeployContractInstances({ forceDeploy: true, }); // Deploy the implementation contract const Contract = await ethers.getContractFactory("VeBetterPassport", { libraries: { PassportChecksLogic: await passportChecksLogic.getAddress(), PassportConfigurator: await passportConfigurator.getAddress(), PassportEntityLogic: await passportEntityLogic.getAddress(), PassportPersonhoodLogic: await passportPersonhoodLogic.getAddress(), PassportPoPScoreLogic: await passportPoPScoreLogic.getAddress(), PassportDelegationLogic: await passportDelegationLogic.getAddress(), PassportSignalingLogic: await passportSignalingLogic.getAddress(), PassportWhitelistAndBlacklistLogic: await passportWhitelistAndBlacklistLogic.getAddress(), }, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const UPGRADER_ROLE = await veBetterPassport.UPGRADER_ROLE(); expect(await veBetterPassport.hasRole(UPGRADER_ROLE, otherAccount)).to.eql(false); await expect(veBetterPassport.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to .be.reverted; }); it("User with UPGRADER_ROLE should be able to upgrade the contract", async function () { const { owner, veBetterPassport, passportChecksLogic, passportConfigurator, passportDelegationLogic, passportPersonhoodLogic, passportPoPScoreLogic, passportEntityLogic, passportSignalingLogic, passportWhitelistAndBlacklistLogic, } = await getOrDeployContractInstances({ forceDeploy: true, }); // Deploy the implementation contract // Deploy the implementation contract const Contract = await ethers.getContractFactory("VeBetterPassport", { libraries: { PassportChecksLogic: await passportChecksLogic.getAddress(), PassportConfigurator: await passportConfigurator.getAddress(), PassportEntityLogic: await passportEntityLogic.getAddress(), PassportPersonhoodLogic: await passportPersonhoodLogic.getAddress(), PassportPoPScoreLogic: await passportPoPScoreLogic.getAddress(), PassportDelegationLogic: await passportDelegationLogic.getAddress(), PassportSignalingLogic: await passportSignalingLogic.getAddress(), PassportWhitelistAndBlacklistLogic: await passportWhitelistAndBlacklistLogic.getAddress(), }, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await getImplementationAddress(ethers.provider, await veBetterPassport.getAddress()); const UPGRADER_ROLE = await veBetterPassport.UPGRADER_ROLE(); expect(await veBetterPassport.hasRole(UPGRADER_ROLE, owner.address)).to.eql(true); await expect(veBetterPassport.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be .reverted; const newImplAddress = await getImplementationAddress(ethers.provider, await veBetterPassport.getAddress()); expect(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase()); expect(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase()); }); it("Only user with UPGRADER_ROLE should be able to upgrade the contract", async function () { const { owner, veBetterPassport, otherAccount, passportChecksLogic, passportConfigurator, passportDelegationLogic, passportPersonhoodLogic, passportPoPScoreLogic, passportSignalingLogic, passportWhitelistAndBlacklistLogic, } = await getOrDeployContractInstances({ forceDeploy: true, }); // Deploy the implementation contract // Deploy the implementation contract const Contract = await ethers.getContractFactory("VeBetterPassport", { libraries: { PassportChecksLogic: await passportChecksLogic.getAddress(), PassportConfigurator: await passportConfigurator.getAddress(), PassportEntityLogic: await passportDelegationLogic.getAddress(), PassportDelegationLogic: await passportDelegationLogic.getAddress(), PassportPersonhoodLogic: await passportPersonhoodLogic.getAddress(), PassportPoPScoreLogic: await passportPoPScoreLogic.getAddress(), PassportSignalingLogic: await passportSignalingLogic.getAddress(), PassportWhitelistAndBlacklistLogic: await passportWhitelistAndBlacklistLogic.getAddress(), }, }); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await getImplementationAddress(ethers.provider, await veBetterPassport.getAddress()); await veBetterPassport.revokeRole(await veBetterPassport.UPGRADER_ROLE(), owner.address); // Revoke the UPGRADER_ROLE from the owner expect(await veBetterPassport.hasRole(await veBetterPassport.UPGRADER_ROLE(), owner.address)).to.eql(false); await veBetterPassport.grantRole(await veBetterPassport.UPGRADER_ROLE(), otherAccount.address); // Grant the UPGRADER_ROLE to the otherAccount // Upgrade the VeBetterPassport implementation with NON-UPGRADER_ROLE user await expect(veBetterPassport.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.be .reverted; // Upgrade the VeBetterPassport implementation with UPGRADER_ROLE user await expect(veBetterPassport.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to .not.be.reverted; const newImplAddress = await getImplementationAddress(ethers.provider, await veBetterPassport.getAddress()); expect(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase()); expect(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase()); }); it("Should not have any state conflicts after from V1 to V2, and then to V3", async function () { const config = createTestConfig(); config.VEPASSPORT_DECAY_RATE = 20; config.EMISSIONS_CYCLE_DURATION = 20; const { owner, otherAccount, treasury, galaxyMember, b3tr, timeLock: timelock, minterAccount, vot3, x2EarnApps, otherAccounts, passportChecksLogicV1, passportConfiguratorV1, passportDelegationLogicV1, passportPersonhoodLogicV1, passportPoPScoreLogicV1, passportSignalingLogicV1, passportEntityLogicV1, passportWhitelistAndBlacklistLogicV1, passportChecksLogicV2, passportConfiguratorV2, passportDelegationLogicV2, passportPersonhoodLogicV2, passportPoPScoreLogicV2, passportSignalingLogicV2, passportEntityLogicV2, passportWhitelistAndBlacklistLogicV2, passportChecksLogicV3, passportConfiguratorV3, passportDelegationLogicV3, passportPersonhoodLogicV3, passportPoPScoreLogicV3, passportSignalingLogicV3, passportWhitelistAndBlacklistLogicV3, passportEntityLogicV3, governorClockLogicLibV1, governorConfiguratorLibV1, governorDepositLogicLibV1, governorFunctionRestrictionsLogicLibV1, governorProposalLogicLibV1, governorQuorumLogicLibV1, governorStateLogicLibV1, governorVotesLogicLibV1, governorClockLogicLibV3, governorConfiguratorLibV3, governorDepositLogicLibV3, governorFunctionRestrictionsLogicLibV3, governorProposalLogicLibV3, governorQuorumLogicLibV3, governorStateLogicLibV3, governorVotesLogicLibV3, governorClockLogicLibV4, governorConfiguratorLibV4, governorDepositLogicLibV4, governorFunctionRestrictionsLogicLibV4, governorProposalLogicLibV4, governorQuorumLogicLibV4, governorStateLogicLibV4, governorVotesLogicLibV4, B3trContract, creators, } = await getOrDeployContractInstances({ forceDeploy: true, config, }); const veBetterPassportContractAddress = await deployProxyOnly("VeBetterPassportV1", { PassportChecksLogicV1: await passportChecksLogicV1.getAddress(), PassportConfiguratorV1: await passportConfiguratorV1.getAddress(), PassportEntityLogicV1: await passportEntityLogicV1.getAddress(), PassportDelegationLogicV1: await passportDelegationLogicV1.getAddress(), PassportPersonhoodLogicV1: await passportPersonhoodLogicV1.getAddress(), PassportPoPScoreLogicV1: await passportPoPScoreLogicV1.getAddress(), PassportSignalingLogicV1: await passportSignalingLogicV1.getAddress(), PassportWhitelistAndBlacklistLogicV1: await passportWhitelistAndBlacklistLogicV1.getAddress(), }); const x2EarnRewardsPool = (await deployAndUpgrade(["X2EarnRewardsPoolV1", "X2EarnRewardsPoolV2", "X2EarnRewardsPool"], [ [ owner.address, // admin owner.address, // contracts address manager owner.address, // upgrader //TODO: transferRole await b3tr.getAddress(), await x2EarnApps.getAddress(), ], [ owner.address, // impact admin address config.X_2_EARN_INITIAL_IMPACT_KEYS, // impact keys ], [veBetterPassportContractAddress], ], { versions: [undefined, 2, 3], })); const xAllocationPool = (await deployAndUpgrade(["XAllocationPoolV1", "XAllocationPool"], [ [ owner.address, // admin owner.address, // upgrader owner.address, // contractsAddressManager await b3tr.getAddress(), await treasury.getAddress(), await x2EarnApps.getAddress(), await x2EarnRewardsPool.getAddress(), ], [], ], { versions: [undefined, 2], })); const emissions = (await deployAndUpgrade(["EmissionsV1", "Emissions"], [ [ { minter: minterAccount.address, admin: owner.address, upgrader: owner.address, contractsAddressManager: owner.address, decaySettingsManager: owner.address, b3trAddress: await b3tr.getAddress(), destinations: [ await xAllocationPool.getAddress(), config.VOTE_2_EARN_POOL_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], })); const voterRewards = (await deployAndUpgrade(["VoterRewardsV1", "VoterRewards"], [ [ owner.address, // admin owner.address, // upgrader // TODO: transferRole owner.address, // contractsAddressManager await emissions.getAddress(), await galaxyMember.getAddress(), await b3tr.getAddress(), config.VOTER_REWARDS_LEVELS, config.VOTER_REWARDS_MULTIPLIER, ], [], ], { versions: [undefined, 2], })); const xAllocationVoting = (await deployAndUpgrade(["XAllocationVotingV1", "XAllocationVotingV2"], [ [ { 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, }, ], [veBetterPassportContractAddress], ], { versions: [undefined, 2], })); const veBetterPassportV1 = (await initializeProxy(veBetterPassportContractAddress, "VeBetterPassportV1", [ { x2EarnApps: await x2EarnApps.getAddress(), xAllocationVoting: await xAllocationVoting.getAddress(), galaxyMember: await galaxyMember.getAddress(), signalingThreshold: config.VEPASSPORT_BOT_SIGNALING_THRESHOLD, //signalingThreshold roundsForCumulativeScore: config.VEPASSPORT_ROUNDS_FOR_CUMULATIVE_PARTICIPATION_SCORE, //roundsForCumulativeScore minimumGalaxyMemberLevel: config.VEPASSPORT_GALAXY_MEMBER_MINIMUM_LEVEL, //galaxyMemberMinimumLevel blacklistThreshold: config.VEPASSPORT_BLACKLIST_THRESHOLD_PERCENTAGE, //blacklistThreshold whitelistThreshold: config.VEPASSPORT_WHITELIST_THRESHOLD_PERCENTAGE, //whitelistThreshold maxEntitiesPerPassport: config.VEPASSPORT_PASSPORT_MAX_ENTITIES, //maxEntitiesPerPassport decayRate: config.VEPASSPORT_DECAY_RATE, //decayRate }, { admin: owner.address, // admins botSignaler: owner.address, // botSignaler upgrader: owner.address, // upgrader settingsManager: owner.address, // settingsManager roleGranter: owner.address, // roleGranter blacklister: owner.address, // blacklister whitelister: owner.address, // whitelistManager actionRegistrar: owner.address, // actionRegistrar actionScoreManager: owner.address, // actionScoreManager }, ], { PassportChecksLogicV1: await passportChecksLogicV1.getAddress(), PassportConfiguratorV1: await passportConfiguratorV1.getAddress(), PassportEntityLogicV1: await passportEntityLogicV1.getAddress(), PassportDelegationLogicV1: await passportDelegationLogicV1.getAddress(), PassportPersonhoodLogicV1: await passportPersonhoodLogicV1.getAddress(), PassportPoPScoreLogicV1: await passportPoPScoreLogicV1.getAddress(), PassportSignalingLogicV1: await passportSignalingLogicV1.getAddress(), PassportWhitelistAndBlacklistLogicV1: await passportWhitelistAndBlacklistLogicV1.getAddress(), })); const governor = (await deployAndUpgrade(["B3TRGovernorV1", "B3TRGovernorV2", "B3TRGovernorV3", "B3TRGovernorV4"], [ [ { 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 voterRewards.getAddress(), isFunctionRestrictionEnabled: true, }, { governorAdmin: owner.address, pauser: owner.address, contractsAddressManager: owner.address, proposalExecutor: owner.address, governorFunctionSettingsRoleAddress: owner.address, }, ], [], [], [veBetterPassportContractAddress], ], { 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 veBetterPassportV1 .connect(owner) .grantRole(await veBetterPassportV1.ACTION_REGISTRAR_ROLE(), await x2EarnRewardsPool.getAddress()); // Grant admin role to voter rewards for registering x allocation voting await xAllocationVoting .connect(owner) .grantRole(await xAllocationVoting.DEFAULT_ADMIN_ROLE(), emissions.getAddress()); await voterRewards .connect(owner) .grantRole(await voterRewards.VOTE_REGISTRAR_ROLE(), await xAllocationVoting.getAddress()); await voterRewards.connect(owner).grantRole(await voterRewards.VOTE_REGISTRAR_ROLE(), await governor.getAddress()); await xAllocationPool.connect(owner).setXAllocationVotingAddress(await xAllocationVoting.getAddress()); await xAllocationPool.connect(owner).setEmissionsAddress(await emissions.getAddress()); // Set xAllocationGovernor in emissions await emissions.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); 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()); // Set XAllocation address in GalaxyMember await galaxyMember.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); await getVot3Tokens(otherAccount, "10000"); await getVot3Tokens(owner, "10000"); await getVot3Tokens(otherAccounts[4], "10000"); const creator1 = creators[0]; const creator2 = creators[1]; //Add apps const app1Id = ethers.keccak256(ethers.toUtf8Bytes(otherAccounts[2].address)); const app2Id = ethers.keccak256(ethers.toUtf8Bytes(otherAccounts[3].address)); const app3Id = ethers.keccak256(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(owner) .submitApp(otherAccounts[4].address, otherAccounts[4].address, otherAccounts[4].address, "metadataURI"); await endorseApp(app1Id, otherAccounts[2]); await endorseApp(app2Id, otherAccounts[3]); await endorseApp(app3Id, otherAccounts[4]); await emissions.connect(owner).setVote2EarnAddress(await voterRewards.getAddress()); // Set app security levels await veBetterPassportV1.connect(owner).setAppSecurity(app1Id, 1); await veBetterPassportV1.connect(owner).setAppSecurity(app2Id, 2); await veBetterPassportV1.connect(owner).setAppSecurity(app3Id, 3); // Grant action registrar role await veBetterPassportV1.grantRole(await veBetterPassportV1.ACTION_REGISTRAR_ROLE(), owner); expect(await veBetterPassportV1.hasRole(await veBetterPassportV1.ACTION_REGISTRAR_ROLE(), owner.address)).to.be .true; // Bootstrap emissions // 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(); await emissions.connect(minterAccount).start(); // Whitelist function const funcSig = B3trContract.interface.getFunction("tokenDetails")?.selector; await governor.connect(owner).setWhitelistFunction(await b3tr.getAddress(), funcSig, true); // Create a proposal for next round // create a new proposal active from round 2 const address = await b3tr.getAddress(); const encodedFunctionCall = B3trContract.interface.encodeFunctionData("tokenDetails", []); const tx = await governor.connect(owner).propose([address], [0], [encodedFunctionCall], "test", "2", 0, { gasLimit: 10000000, }); const proposalId = await getProposalIdFromTx(tx); // pay deposit const proposalThreshold = await governor.proposalDepositThreshold(proposalId); await getVot3Tokens(owner, ethers.formatEther(proposalThreshold)); // We also need to wait a block to update the proposer's votes snapshot await waitForNextBlock(); await vot3.connect(owner).approve(await governor.getAddress(), proposalThreshold); await governor.connect(owner).deposit(proposalThreshold, proposalId); // First round, participation score check is disabled // Register actions for round 1 await veBetterPassportV1.connect(owner).registerAction(otherAccount.address, app1Id); await veBetterPassportV1.connect(owner).registerAction(otherAccount.address, app2Id); // User's cumulative score = 100 (app1) + 200 (app2) = 300 expect(await veBetterPassportV1.userRoundScore(otherAccount.address, 1)).to.equal(300); expect(await veBetterPassportV1.getCumulativeScoreWithDecay(otherAccount, 1)).to.equal(300); await veBetterPassportV1.toggleCheck(4); // Vote // Note that `otherAccount` can vote because the participation score threshold is set to 0 await xAllocationVoting .connect(otherAccount) .castVote(1, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")]); await xAllocationVoting.connect(otherAccounts[4]).castVote(1, [app1Id], [ethers.parseEther("1")]); // Set minimum participation score to 500 await veBetterPassportV1.setThresholdPoPScore(500); let blockNextCycle = await emissions.getNextCycleBlock(); await waitForBlock(Number(blockNextCycle)); await emissions.connect(minterAccount).distribute(); expect(await xAllocationVoting.currentRoundId()).to.equal(2); expect(await governor.state(proposalId)).to.equal(1); // User tries to vote both governance and x allocation voting but reverts due to not meeting the participation score threshold await expect(xAllocationVoting .connect(otherAccount) .castVote(2, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")])).to.be.revertedWithCustomError(xAllocationVoting, "GovernorPersonhoodVerificationFailed"); await expect(governor.connect(otherAccount).castVote(proposalId, 2)).to.be.revertedWithCustomError(xAllocationVoting, "GovernorPersonhoodVerificationFailed"); // Register actions for round 2 await veBetterPassportV1.connect(owner).registerAction(otherAccount, app2Id); await veBetterPassportV1.connect(owner).registerAction(otherAccount, app3Id); /* User's cumulative score: round 1 = 300 round 2 = 600 + (300 * 0.8) = 840 */ expect(await veBetterPassportV1.getCumulativeScoreWithDecay(otherAccount, 2)).to.equal(840); // User now meets the participation score threshold and can vote await xAllocationVoting .connect(otherAccount) .castVote(2, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")]); await governor.connect(otherAccount).castVote(proposalId, 2); // Increase participation score threshold to 1000 await veBetterPassportV1.setThresholdPoPScore(1000); // Toggle GM level check await veBetterPassportV1.toggleCheck(5); // Upgrade to V2 const veBetterPassportV2 = (await upgradeProxy("VeBetterPassportV1", "VeBetterPassportV2", await veBetterPassportV1.getAddress(), [], { version: 2, libraries: { PassportChecksLogicV2: await passportChecksLogicV2.getAddress(), PassportConfiguratorV2: await passportConfiguratorV2.getAddress(), PassportEntityLogicV2: await passportEntityLogicV2.getAddress(), PassportDelegationLogicV2: await passportDelegationLogicV2.getAddress(), PassportPersonhoodLogicV2: await passportPersonhoodLogicV2.getAddress(), PassportPoPScoreLogicV2: await passportPoPScoreLogicV2.getAddress(), PassportSignalingLogicV2: await passportSignalingLogicV2.getAddress(), PassportWhitelistAndBlacklistLogicV2: await passportWhitelistAndBlacklistLogicV2.getAddress(), }, })); blockNextCycle = await emissions.getNextCycleBlock(); await waitForBlock(Number(blockNextCycle)); // Increase participation score threshold to 1000 await veBetterPassportV2.setThresholdPoPScore(1000); await emissions.distribute(); expect(await xAllocationVoting.currentRoundId()).to.equal(3); // User tries to vote x allocation voting but reverts due to not meeting the participation score threshold await expect(xAllocationVoting .connect(otherAccount) .castVote(3, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")])).to.be.revertedWithCustomError(xAllocationVoting, "GovernorPersonhoodVerificationFailed"); // Register action for round 3 await veBetterPassportV2.connect(owner).registerAction(otherAccount, app1Id); /* User's cumulative score: round 1 = 300 round 2 = 600 + (300 * 0.8) = 840 round 3 = 100 + (840 * 0.8) = 772 */ expect(await veBetterPassportV2.getCumulativeScoreWithDecay(otherAccount, 3)).to.equal(772); // User still doesn't meet the participation score threshold and can't vote await expect(xAllocationVoting .connect(otherAccount) .castVote(3, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")])).to.be.revertedWithCustomError(xAllocationVoting, "GovernorPersonhoodVerificationFailed"); // register more actions for round 3 await veBetterPassportV2.connect(owner).registerAction(otherAccount, app2Id); await veBetterPassportV2.connect(owner).registerAction(otherAccount, app3Id); /* User's cumulative score: round 1 = 300 round 2 = 600 + (300 * 0.8) = 840 round 3 = 700 + (840 * 0.8) = 1072 */ expect(await veBetterPassportV2.getCumulativeScoreWithDecay(otherAccount, 3)).to.equal(1372); // User now meets the participation score threshold and can vote await xAllocationVoting .connect(otherAccount) .castVote(3, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")]); // "Before linking passport should have 0" expect(await veBetterPassportV2.getCumulativeScoreWithDecay(owner, 3)).to.equal(0); // Before linking passport should not be considered person expect((await veBetterPassportV2.isPersonAtTimepoint(owner.address, await xAllocationVoting.roundSnapshot(3)))[0]).to.be.equal(false); // Delegate passport to owner and try to vote await linkEntityToPassportWithSignature(veBetterPassportV2, owner, otherAccount, 3600); // After linking "other account" should be entity expect(await veBetterPassportV2.isEntity(otherAccount.address)).to.be.true; // After linking owner should be passport expect(await veBetterPassportV2.isPassport(owner.address)).to.be.true; // After linking passport should not be considered person at the beginning of the round expect((await veBetterPassportV2.isPersonAtTimepoint(owner.address, await xAllocationVoting.roundSnapshot(3)))[0]).to.be.equal(false); expect(await veBetterPassportV2.isPassport(owner.address)).to.be.true; // Owner can't vote yet because the delegation is checkpointed and is active from the next round await expect(xAllocationVoting .connect(owner) .castVote(3, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")])).to.be.revertedWithCustomError(xAllocationVoting, "GovernorPersonhoodVerificationFailed"); blockNextCycle = await emissions.getNextCycleBlock(); await waitForBlock(Number(blockNextCycle)); // Mint user GM token await galaxyMember.connect(otherAccounts[4]).freeMint(); await galaxyMember.setMaxLevel(2); // Set user GM level to 2 await upgradeNFTtoLevel(1, 2, galaxyMember, b3tr, otherAccounts[4], minterAccount); // Checking if user is person based on GM level will not work in V2 because the check is not implemented expect(await veBetterPassportV2.isPerson(otherAccounts[4].address)).to.deep.equal([ false, "User does not meet the criteria to be considered a person", ]); // Record contract storage state at this point let storageSlots = []; const initialSlot = BigInt("0x273c9387b78d9b22e6f3371bb3aa3a918f53507e8cacc54e4831933cbb844100"); // Slot 0 of VoterRewards for (let i = initialSlot; i < initialSlot + BigInt(50); i++) { storageSlots.push(await ethers.provider.getStorage(await veBetterPassportV1.getAddress(), i)); } storageSlots = storageSlots.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // Upgrade to V3 const veBetterPassportV3 = (await upgradeProxy("VeBetterPassportV2", "VeBetterPassportV3", await veBetterPassportV2.getAddress(), [], { version: 3, libraries: { PassportChecksLogicV3: await passportChecksLogicV3.getAddress(), PassportConfiguratorV3: await passportConfiguratorV3.getAddress(), PassportEntityLogicV3: await passportEntityLogicV3.getAddress(), PassportDelegationLogicV3: await passportDelegationLogicV3.getAddress(), PassportPersonhoodLogicV3: await passportPersonhoodLogicV3.getAddress(), PassportPoPScoreLogicV3: await passportPoPScoreLogicV3.getAddress(), PassportSignalingLogicV3: await passportSignalingLogicV3.getAddress(), PassportWhitelistAndBlacklistLogicV3: await passportWhitelistAndBlacklistLogicV3.getAddress(), }, })); // Check that the storage slots are the same let storageSlotsAfter = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsAfter.push(await ethers.provider.getStorage(await veBetterPassportV3.getAddress(), i)); } storageSlotsAfter = storageSlotsAfter.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { expect(storageSlots[i]).to.equal(storageSlotsAfter[i]); } await emissions.connect(minterAccount).distribute(); expect(await xAllocationVoting.currentRoundId()).to.equal(4); // Checking if user is person based on GM level will work in V3 because the check is implemented expect(await veBetterPassportV3.isPerson(otherAccounts[4].address)).to.deep.equal([ true, "User's selected Galaxy Member is above the minimum level", ]); // During linking points are not brought over, so we need to register some actions // on both the entity and the passport to see that they are grouped together and can vote expect(await veBetterPassportV1.getCumulativeScoreWithDecay(otherAccount, 4)).to.equal(1097); // register more actions for round 4 (mixing entity and passport) await veBetterPassportV3.connect(owner).registerAction(otherAccount, app2Id); await veBetterPassportV3.connect(owner).registerAction(owner, app3Id); await veBetterPassportV3.connect(owner).registerAction(owner, app3Id); // new points should be added to the passport, entity should not have any new points added expect(await veBetterPassportV3.getCumulativeScoreWithDecay(otherAccount, 4)).to.equal(1097); /* Passport's cumulative score: round 4 = 200 + 400 + 400 */ expect(await veBetterPassportV3.getCumulativeScoreWithDecay(owner, 4)).to.equal(1000); // Now that we reached threshold passport should be considered person expect((await veBetterPassportV3.isPersonAtTimepoint(owner.address, await xAllocationVoting.roundSnapshot(4)))[0]).to.be.equal(true); // Owner can vote now await xAllocationVoting .connect(owner) .castVote(4, [app1Id, app2Id, app3Id], [ethers.parseEther("0"), ethers.parseEther("900"), ethers.parseEther("100")]); }); }); }); // Continue shard8 with smaller sections describe("VeBetterPassport Checks & Config - @shard8-core", function () { describe("Passport Checks", function () { it("Should initialize correctly", async function () { const { owner: settingsManager, veBetterPassport, otherAccount, } = await getOrDeployContractInstances({ forceDeploy: true, }); // Verify non admin account cannot toggle checks by default await expect(veBetterPassport.connect(otherAccount).toggleCheck(1)).to.be.reverted; const settingsManagerRole = await veBetterPassport.SETTINGS_MANAGER_ROLE(); // Verify settingsManager has the role expect(await veBetterPassport.hasRole(settingsManagerRole, settingsManager.address)).to.be.true; }); it("Should allow only settings manager to toggle checks", async function () { const { owner: settingsManager, veBetterPassport, otherAccount, } = await getOrDeployContractInstances({ forceDeploy: true, }); await expect(veBetterPassport.connect(otherAccount).toggleCheck(1)).to.be.reverted; // Settings manager should be able to toggle the checks await expect(veBetterPassport.connect(settingsManager).toggleCheck(1)) // 1 is the .to.emit(veBetterPassport, "CheckToggled") .withArgs("Whitelist Check", true); // Whitelist check should be enabled expect(await veBetterPassport.isCheckEnabled(1)).to.be.true; // Cast SETTING_MANAGER_ROLE to otherAccount const settingsManagerRole = await veBetterPassport.SETTINGS_MANAGER_ROLE(); await veBetterPassport.connect(settingsManager).grantRole(settingsManagerRole, otherAccount.address); // Other account should be able to toggle the checks await expect(veBetterPassport.connect(otherAccount).toggleCheck(1)) .to.emit(veBetterPassport, "CheckToggled") .withArgs("Whitelist Check", false); // Whitelist check should be disabled expect(await veBetterPas