UNPKG

@vechain/vebetterdao-contracts

Version:

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

488 lines (487 loc) 32.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const hardhat_1 = require("hardhat"); const chai_1 = require("chai"); const mocha_1 = require("mocha"); const local_1 = require("@repo/config/contracts/envs/local"); const deploy_1 = require("../helpers/deploy"); const common_1 = require("../helpers/common"); (0, mocha_1.describe)("NavigatorRegistry - @shard19a", function () { let navigatorRegistry; let xAllocationVoting; let b3tr; let vot3; let treasury; let owner; let otherAccount; let minterAccount; let otherAccounts; const config = (0, local_1.createLocalConfig)(); const STAKE_AMOUNT = hardhat_1.ethers.parseEther("50000"); const METADATA_URI = "ipfs://navigator-metadata"; // Ensure VOT3 supply exists so max stake calculation is non-zero const ensureVot3Supply = async () => { const supply = await vot3.totalSupply(); if (supply === 0n) { // Mint and convert enough B3TR -> VOT3 to create supply await (0, common_1.getVot3Tokens)(owner, "10000000"); } }; // Helper: mint B3TR for account and approve NavigatorRegistry const fundAndApprove = async (account, amount) => { await b3tr.connect(minterAccount).mint(account.address, amount); const registryAddress = await navigatorRegistry.getAddress(); await b3tr.connect(account).approve(registryAddress, amount); }; // Helper: advance blocks past the exit effective deadline const advancePastExitDeadline = async (navigatorAddr) => { const filter = navigatorRegistry.filters.ExitAnnounced(navigatorAddr); const events = await navigatorRegistry.queryFilter(filter); const effectiveDeadline = events[events.length - 1].args.effectiveDeadline; const currentBlock = await xAllocationVoting.clock(); if (currentBlock < effectiveDeadline) { await (0, common_1.moveBlocks)(Number(effectiveDeadline - currentBlock) + 1); } }; // Helper: register a navigator with default stake (ensures VOT3 supply first) const registerNavigator = async (account, amount = STAKE_AMOUNT, uri = METADATA_URI) => { await ensureVot3Supply(); await fundAndApprove(account, amount); await navigatorRegistry.connect(account).register(amount, uri); }; (0, mocha_1.beforeEach)(async function () { const deployment = await (0, deploy_1.getOrDeployContractInstances)({ forceDeploy: true }); if (!deployment) throw new Error("Failed to deploy contracts"); navigatorRegistry = deployment.navigatorRegistry; xAllocationVoting = deployment.xAllocationVoting; b3tr = deployment.b3tr; vot3 = deployment.vot3; treasury = deployment.treasury; owner = deployment.owner; otherAccount = deployment.otherAccount; minterAccount = deployment.minterAccount; otherAccounts = deployment.otherAccounts; }); // ======================== 1. Initialization ======================== // (0, mocha_1.describe)("Initialization", function () { (0, mocha_1.it)("version() returns '1'", async function () { (0, chai_1.expect)(await navigatorRegistry.version()).to.equal("1"); }); (0, mocha_1.it)("BASIS_POINTS returns 10000", async function () { (0, chai_1.expect)(await navigatorRegistry.BASIS_POINTS()).to.equal(10000n); }); (0, mocha_1.it)("getMinStake returns config value", async function () { (0, chai_1.expect)(await navigatorRegistry.getMinStake()).to.equal(config.NAVIGATOR_MIN_STAKE); }); (0, mocha_1.it)("getFeeLockPeriod returns config value", async function () { (0, chai_1.expect)(await navigatorRegistry.getFeeLockPeriod()).to.equal(config.NAVIGATOR_FEE_LOCK_PERIOD); }); (0, mocha_1.it)("getFeePercentage returns config value", async function () { (0, chai_1.expect)(await navigatorRegistry.getFeePercentage()).to.equal(config.NAVIGATOR_FEE_PERCENTAGE); }); (0, mocha_1.it)("getReportInterval returns config value", async function () { (0, chai_1.expect)(await navigatorRegistry.getReportInterval()).to.equal(config.NAVIGATOR_REPORT_INTERVAL); }); (0, mocha_1.it)("getMinorSlashPercentage returns config value", async function () { (0, chai_1.expect)(await navigatorRegistry.getMinorSlashPercentage()).to.equal(config.NAVIGATOR_MINOR_SLASH_PERCENTAGE); }); (0, mocha_1.it)("getPreferenceCutoffPeriod returns config value", async function () { (0, chai_1.expect)(await navigatorRegistry.getPreferenceCutoffPeriod()).to.equal(config.NAVIGATOR_PREFERENCE_CUTOFF_PERIOD); }); (0, mocha_1.it)("should not allow reinitialization", async function () { await (0, chai_1.expect)(navigatorRegistry.initialize({ admin: owner.address, upgrader: owner.address, governance: owner.address, b3trToken: await b3tr.getAddress(), vot3Token: await vot3.getAddress(), treasury: await treasury.getAddress(), minStake: STAKE_AMOUNT, maxStakePercentage: 100, feeLockPeriod: 4, feePercentage: 2000, exitNoticePeriod: 1, reportInterval: 2, minorSlashPercentage: 1000, preferenceCutoffPeriod: 8640, voterRewards: owner.address, xAllocationVoting: owner.address, relayerRewardsPool: owner.address, })).to.be.reverted; }); }); // ======================== 2. Registration & Staking ======================== // (0, mocha_1.describe)("Registration & Staking", function () { (0, mocha_1.describe)("register()", function () { (0, mocha_1.it)("should register a navigator with correct stake, metadata, and emit event", async function () { const navigator = otherAccounts[10]; await ensureVot3Supply(); await fundAndApprove(navigator, STAKE_AMOUNT); const tx = await navigatorRegistry.connect(navigator).register(STAKE_AMOUNT, METADATA_URI); (0, chai_1.expect)(await navigatorRegistry.isNavigator(navigator.address)).to.be.true; (0, chai_1.expect)(await navigatorRegistry.getStake(navigator.address)).to.equal(STAKE_AMOUNT); (0, chai_1.expect)(await navigatorRegistry.getMetadataURI(navigator.address)).to.equal(METADATA_URI); await (0, chai_1.expect)(tx) .to.emit(navigatorRegistry, "NavigatorRegistered") .withArgs(navigator.address, STAKE_AMOUNT, METADATA_URI); }); (0, mocha_1.it)("should revert with StakeBelowMinimum when stake is below minimum", async function () { const navigator = otherAccounts[10]; const lowStake = hardhat_1.ethers.parseEther("100"); await fundAndApprove(navigator, lowStake); await (0, chai_1.expect)(navigatorRegistry.connect(navigator).register(lowStake, METADATA_URI)).to.be.revertedWithCustomError(navigatorRegistry, "StakeBelowMinimum"); }); (0, mocha_1.it)("should revert with AlreadyRegistered when registering twice", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); // Fund again and try to register a second time await fundAndApprove(navigator, STAKE_AMOUNT); await (0, chai_1.expect)(navigatorRegistry.connect(navigator).register(STAKE_AMOUNT, METADATA_URI)).to.be.revertedWithCustomError(navigatorRegistry, "AlreadyRegistered"); }); (0, mocha_1.it)("should revert with DelegatorCannotRegister when caller is currently delegating", async function () { const existingNavigator = otherAccounts[10]; const delegator = otherAccounts[11]; await registerNavigator(existingNavigator); // delegator gets VOT3 and delegates to the existing navigator await (0, common_1.getVot3Tokens)(delegator, "1000"); await navigatorRegistry.connect(delegator).delegate(existingNavigator.address, hardhat_1.ethers.parseEther("500")); // delegator tries to register as a navigator while still delegating await fundAndApprove(delegator, STAKE_AMOUNT); await (0, chai_1.expect)(navigatorRegistry.connect(delegator).register(STAKE_AMOUNT, METADATA_URI)).to.be.revertedWithCustomError(navigatorRegistry, "DelegatorCannotRegister"); }); (0, mocha_1.it)("should allow registration after delegator undelegates", async function () { const existingNavigator = otherAccounts[10]; const delegator = otherAccounts[11]; await registerNavigator(existingNavigator); // delegator delegates then undelegates await (0, common_1.getVot3Tokens)(delegator, "1000"); await navigatorRegistry.connect(delegator).delegate(existingNavigator.address, hardhat_1.ethers.parseEther("500")); await navigatorRegistry.connect(delegator).undelegate(); // Now registration should succeed await fundAndApprove(delegator, STAKE_AMOUNT); await navigatorRegistry.connect(delegator).register(STAKE_AMOUNT, METADATA_URI); (0, chai_1.expect)(await navigatorRegistry.isNavigator(delegator.address)).to.be.true; }); (0, mocha_1.it)("should allow registration when previous navigator was deactivated (stale delegation)", async function () { const existingNavigator = otherAccounts[10]; const delegator = otherAccounts[11]; await registerNavigator(existingNavigator); // delegator delegates await (0, common_1.getVot3Tokens)(delegator, "1000"); await navigatorRegistry.connect(delegator).delegate(existingNavigator.address, hardhat_1.ethers.parseEther("500")); // Governance deactivates the navigator — delegation becomes stale await navigatorRegistry.connect(owner).deactivateNavigator(existingNavigator.address, 0, false); // Delegator should be able to register since their navigator is dead await fundAndApprove(delegator, STAKE_AMOUNT); await navigatorRegistry.connect(delegator).register(STAKE_AMOUNT, METADATA_URI); (0, chai_1.expect)(await navigatorRegistry.isNavigator(delegator.address)).to.be.true; }); }); (0, mocha_1.describe)("addStake()", function () { (0, mocha_1.it)("should increase stake and emit event", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); const additionalStake = hardhat_1.ethers.parseEther("10000"); await fundAndApprove(navigator, additionalStake); const tx = await navigatorRegistry.connect(navigator).addStake(additionalStake); const expectedTotal = STAKE_AMOUNT + additionalStake; (0, chai_1.expect)(await navigatorRegistry.getStake(navigator.address)).to.equal(expectedTotal); await (0, chai_1.expect)(tx) .to.emit(navigatorRegistry, "StakeAdded") .withArgs(navigator.address, additionalStake, expectedTotal); }); (0, mocha_1.it)("should revert when caller is not a navigator", async function () { const nonNavigator = otherAccounts[11]; const amount = hardhat_1.ethers.parseEther("10000"); await fundAndApprove(nonNavigator, amount); await (0, chai_1.expect)(navigatorRegistry.connect(nonNavigator).addStake(amount)).to.be.revertedWithCustomError(navigatorRegistry, "NotRegistered"); }); }); (0, mocha_1.describe)("withdrawStake()", function () { (0, mocha_1.it)("should allow withdrawal after exit notice period elapses", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await (0, common_1.bootstrapAndStartEmissions)(); await navigatorRegistry.connect(navigator).announceExit(); await advancePastExitDeadline(navigator.address); const tx = await navigatorRegistry.connect(navigator).withdrawStake(STAKE_AMOUNT); (0, chai_1.expect)(await navigatorRegistry.getStake(navigator.address)).to.equal(0n); await (0, chai_1.expect)(tx).to.emit(navigatorRegistry, "StakeWithdrawn").withArgs(navigator.address, STAKE_AMOUNT, 0n); }); (0, mocha_1.it)("should revert with NavigatorStillActive when navigator has not announced exit", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await (0, chai_1.expect)(navigatorRegistry.connect(navigator).withdrawStake(STAKE_AMOUNT)).to.be.revertedWithCustomError(navigatorRegistry, "NavigatorStillActive"); }); (0, mocha_1.it)("should revert with InsufficientStake when withdrawing more than staked", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await (0, common_1.bootstrapAndStartEmissions)(); await navigatorRegistry.connect(navigator).announceExit(); await advancePastExitDeadline(navigator.address); const tooMuch = STAKE_AMOUNT + hardhat_1.ethers.parseEther("1"); await (0, chai_1.expect)(navigatorRegistry.connect(navigator).withdrawStake(tooMuch)).to.be.revertedWithCustomError(navigatorRegistry, "InsufficientStake"); }); }); (0, mocha_1.describe)("reduceStake()", function () { (0, mocha_1.it)("should allow active navigator to reduce stake and emit StakeWithdrawn", async function () { const navigator = otherAccounts[10]; const extraStake = hardhat_1.ethers.parseEther("100000"); await registerNavigator(navigator, extraStake); const reduceAmount = hardhat_1.ethers.parseEther("10000"); const remaining = extraStake - reduceAmount; const tx = await navigatorRegistry.connect(navigator).reduceStake(reduceAmount); (0, chai_1.expect)(await navigatorRegistry.getStake(navigator.address)).to.equal(remaining); await (0, chai_1.expect)(tx) .to.emit(navigatorRegistry, "StakeWithdrawn") .withArgs(navigator.address, reduceAmount, remaining); }); (0, mocha_1.it)("should revert with StakeBelowMinimum when reducing below min stake", async function () { const navigator = otherAccounts[10]; const minStake = await navigatorRegistry.getMinStake(); // Register with exactly min stake await registerNavigator(navigator, minStake); // Reducing by any amount should revert since we're at the minimum await (0, chai_1.expect)(navigatorRegistry.connect(navigator).reduceStake(1n)).to.be.revertedWithCustomError(navigatorRegistry, "StakeBelowMinimum"); }); (0, mocha_1.it)("should revert when reducing would break delegation capacity", async function () { const navigator = otherAccounts[10]; // Register with extra stake so we can reduce const extraStake = hardhat_1.ethers.parseEther("100000"); await registerNavigator(navigator, extraStake); // Delegate close to capacity: 100K stake * 10 = 1M capacity, delegate 600K const citizen = otherAccounts[11]; await (0, common_1.getVot3Tokens)(citizen, "600000"); await navigatorRegistry.connect(citizen).delegate(navigator.address, hardhat_1.ethers.parseEther("600000")); // Reducing 50K would leave 50K stake, capacity = 500K < 600K delegated await (0, chai_1.expect)(navigatorRegistry.connect(navigator).reduceStake(hardhat_1.ethers.parseEther("50000"))).to.be.revertedWithCustomError(navigatorRegistry, "StakeBelowMinimum"); }); (0, mocha_1.it)("should revert with InsufficientStake when reducing more than staked", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); const tooMuch = STAKE_AMOUNT + hardhat_1.ethers.parseEther("1"); await (0, chai_1.expect)(navigatorRegistry.connect(navigator).reduceStake(tooMuch)).to.be.revertedWithCustomError(navigatorRegistry, "InsufficientStake"); }); (0, mocha_1.it)("should revert when caller is not a navigator", async function () { const nonNavigator = otherAccounts[11]; await (0, chai_1.expect)(navigatorRegistry.connect(nonNavigator).reduceStake(hardhat_1.ethers.parseEther("1000"))).to.be.revertedWithCustomError(navigatorRegistry, "NotRegistered"); }); }); (0, mocha_1.describe)("Delegation Capacity", function () { (0, mocha_1.it)("getDelegationCapacity should be stake * 10", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); const capacity = await navigatorRegistry.getDelegationCapacity(navigator.address); (0, chai_1.expect)(capacity).to.equal(STAKE_AMOUNT * 10n); }); (0, mocha_1.it)("getRemainingCapacity should decrease after delegation", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); const citizen = otherAccounts[12]; const delegateAmount = hardhat_1.ethers.parseEther("100000"); // 100k VOT3 // Give citizen VOT3 tokens await (0, common_1.getVot3Tokens)(citizen, "100000"); // Delegate await navigatorRegistry.connect(citizen).delegate(navigator.address, delegateAmount); const remaining = await navigatorRegistry.getRemainingCapacity(navigator.address); const expectedRemaining = STAKE_AMOUNT * 10n - delegateAmount; (0, chai_1.expect)(remaining).to.equal(expectedRemaining); }); }); }); // ======================== 3. Governance Setters ======================== // (0, mocha_1.describe)("Governance Setters", function () { // ---- Numeric setters with GOVERNANCE_ROLE ---- // (0, mocha_1.describe)("setMinStake", function () { (0, mocha_1.it)("should update minStake", async function () { const newValue = hardhat_1.ethers.parseEther("100000"); await navigatorRegistry.connect(owner).setMinStake(newValue); (0, chai_1.expect)(await navigatorRegistry.getMinStake()).to.equal(newValue); }); (0, mocha_1.it)("should revert with InvalidParameter when value is 0", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setMinStake(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setMinStake(1)).to.be.reverted; }); }); (0, mocha_1.describe)("setMaxStakePercentage", function () { (0, mocha_1.it)("should update maxStakePercentage", async function () { // Create some VOT3 supply so getMaxStake returns non-zero await (0, common_1.getVot3Tokens)(owner, "1000000"); await navigatorRegistry.connect(owner).setMaxStakePercentage(500); const maxStake = await navigatorRegistry.getMaxStake(); (0, chai_1.expect)(maxStake).to.be.gt(0n); }); (0, mocha_1.it)("should revert with InvalidParameter when value is 0", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setMaxStakePercentage(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert with InvalidParameter when value exceeds BASIS_POINTS", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setMaxStakePercentage(10001)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setMaxStakePercentage(500)).to.be.reverted; }); }); (0, mocha_1.describe)("setFeeLockPeriod", function () { (0, mocha_1.it)("should update feeLockPeriod", async function () { await navigatorRegistry.connect(owner).setFeeLockPeriod(8); (0, chai_1.expect)(await navigatorRegistry.getFeeLockPeriod()).to.equal(8); }); (0, mocha_1.it)("should revert with InvalidParameter when value is 0", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setFeeLockPeriod(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setFeeLockPeriod(8)).to.be.reverted; }); }); (0, mocha_1.describe)("setFeePercentage", function () { (0, mocha_1.it)("should update feePercentage", async function () { await navigatorRegistry.connect(owner).setFeePercentage(3000); (0, chai_1.expect)(await navigatorRegistry.getFeePercentage()).to.equal(3000); }); (0, mocha_1.it)("should revert with InvalidParameter when value exceeds BASIS_POINTS", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setFeePercentage(10001)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setFeePercentage(3000)).to.be.reverted; }); }); (0, mocha_1.describe)("setReportInterval", function () { (0, mocha_1.it)("should update reportInterval", async function () { await navigatorRegistry.connect(owner).setReportInterval(5); (0, chai_1.expect)(await navigatorRegistry.getReportInterval()).to.equal(5); }); (0, mocha_1.it)("should revert with InvalidParameter when value is 0", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setReportInterval(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setReportInterval(5)).to.be.reverted; }); }); (0, mocha_1.describe)("setMinorSlashPercentage", function () { (0, mocha_1.it)("should update minorSlashPercentage", async function () { await navigatorRegistry.connect(owner).setMinorSlashPercentage(2000); (0, chai_1.expect)(await navigatorRegistry.getMinorSlashPercentage()).to.equal(2000); }); (0, mocha_1.it)("should revert with InvalidParameter when value is 0", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setMinorSlashPercentage(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert with InvalidParameter when value exceeds BASIS_POINTS", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setMinorSlashPercentage(10001)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setMinorSlashPercentage(2000)).to.be.reverted; }); }); (0, mocha_1.describe)("setPreferenceCutoffPeriod", function () { (0, mocha_1.it)("should update preferenceCutoffPeriod", async function () { await navigatorRegistry.connect(owner).setPreferenceCutoffPeriod(4320); (0, chai_1.expect)(await navigatorRegistry.getPreferenceCutoffPeriod()).to.equal(4320); }); (0, mocha_1.it)("should revert with InvalidParameter when value is 0", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setPreferenceCutoffPeriod(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter"); }); (0, mocha_1.it)("should revert when caller lacks GOVERNANCE_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setPreferenceCutoffPeriod(4320)).to.be.reverted; }); }); // ---- Address setters with DEFAULT_ADMIN_ROLE ---- // (0, mocha_1.describe)("setXAllocationVoting", function () { (0, mocha_1.it)("should update xAllocationVoting address", async function () { const newAddress = otherAccounts[13].address; await navigatorRegistry.connect(owner).setXAllocationVoting(newAddress); }); (0, mocha_1.it)("should revert with ZeroAddress when address is zero", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setXAllocationVoting(hardhat_1.ethers.ZeroAddress)).to.be.revertedWithCustomError(navigatorRegistry, "ZeroAddress"); }); (0, mocha_1.it)("should revert when caller lacks DEFAULT_ADMIN_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setXAllocationVoting(otherAccounts[13].address)).to.be .reverted; }); }); (0, mocha_1.describe)("setRelayerRewardsPool", function () { (0, mocha_1.it)("should update relayerRewardsPool address", async function () { const newAddress = otherAccounts[14].address; await navigatorRegistry.connect(owner).setRelayerRewardsPool(newAddress); }); (0, mocha_1.it)("should revert with ZeroAddress when address is zero", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setRelayerRewardsPool(hardhat_1.ethers.ZeroAddress)).to.be.revertedWithCustomError(navigatorRegistry, "ZeroAddress"); }); (0, mocha_1.it)("should revert when caller lacks DEFAULT_ADMIN_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setRelayerRewardsPool(otherAccounts[14].address)).to.be .reverted; }); }); (0, mocha_1.describe)("setVoterRewards", function () { (0, mocha_1.it)("should update voterRewards address", async function () { const newAddress = otherAccounts[15].address; await navigatorRegistry.connect(owner).setVoterRewards(newAddress); }); (0, mocha_1.it)("should revert with ZeroAddress when address is zero", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(owner).setVoterRewards(hardhat_1.ethers.ZeroAddress)).to.be.revertedWithCustomError(navigatorRegistry, "ZeroAddress"); }); (0, mocha_1.it)("should revert when caller lacks DEFAULT_ADMIN_ROLE", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(otherAccount).setVoterRewards(otherAccounts[15].address)).to.be.reverted; }); }); }); (0, mocha_1.describe)("getStatus()", function () { (0, mocha_1.it)("should return NONE (0) for unregistered address", async function () { const status = await navigatorRegistry.getStatus(otherAccounts[10].address); (0, chai_1.expect)(status).to.equal(0n); }); (0, mocha_1.it)("should return ACTIVE (1) after registration", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); const status = await navigatorRegistry.getStatus(navigator.address); (0, chai_1.expect)(status).to.equal(1n); }); (0, mocha_1.it)("should return EXITING (2) after announceExit while notice period is active", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await (0, common_1.bootstrapAndStartEmissions)(); await navigatorRegistry.connect(navigator).announceExit(); const status = await navigatorRegistry.getStatus(navigator.address); (0, chai_1.expect)(status).to.equal(2n); }); (0, mocha_1.it)("should return DEACTIVATED (3) after notice period elapses", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await (0, common_1.bootstrapAndStartEmissions)(); await navigatorRegistry.connect(navigator).announceExit(); (0, chai_1.expect)(await navigatorRegistry.getStatus(navigator.address)).to.equal(2n); await advancePastExitDeadline(navigator.address); const status = await navigatorRegistry.getStatus(navigator.address); (0, chai_1.expect)(status).to.equal(3n); }); (0, mocha_1.it)("should return DEACTIVATED (3) after governance deactivation", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await navigatorRegistry.connect(owner).deactivateNavigator(navigator.address, 0, false); const status = await navigatorRegistry.getStatus(navigator.address); (0, chai_1.expect)(status).to.equal(3n); }); (0, mocha_1.it)("should return DEACTIVATED (3) for governance-deactivated even if exit was also announced", async function () { const navigator = otherAccounts[10]; await registerNavigator(navigator); await (0, common_1.bootstrapAndStartEmissions)(); await navigatorRegistry.connect(navigator).announceExit(); (0, chai_1.expect)(await navigatorRegistry.getStatus(navigator.address)).to.equal(2n); // Governance deactivates while exiting await navigatorRegistry.connect(owner).deactivateNavigator(navigator.address, 0, false); const status = await navigatorRegistry.getStatus(navigator.address); (0, chai_1.expect)(status).to.equal(3n); }); (0, mocha_1.it)("should still return ACTIVE (1) for an active navigator after another is deactivated", async function () { const nav1 = otherAccounts[10]; const nav2 = otherAccounts[11]; await registerNavigator(nav1); await registerNavigator(nav2); await navigatorRegistry.connect(owner).deactivateNavigator(nav1.address, 0, false); (0, chai_1.expect)(await navigatorRegistry.getStatus(nav1.address)).to.equal(3n); (0, chai_1.expect)(await navigatorRegistry.getStatus(nav2.address)).to.equal(1n); }); }); });