UNPKG

@vechain/vebetterdao-contracts

Version:

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

200 lines (199 loc) 13.6 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 deploy_1 = require("../helpers/deploy"); const common_1 = require("../helpers/common"); (0, mocha_1.describe)("NavigatorRegistry Voting - @shard19c", function () { let navigatorRegistry; let b3tr; let xAllocationVoting; let emissions; let navigatorVotingUtils; let owner; let minterAccount; let otherAccounts; let navigator1; let nonNavigator; let roundId; const STAKE_AMOUNT = hardhat_1.ethers.parseEther("50000"); const METADATA_URI = "ipfs://navigator-metadata"; const app1 = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes("App1")); const app2 = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes("App2")); const app3 = hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes("App3")); // Helper: fund account with B3TR (via owner) and approve NavigatorRegistry const fundAndApprove = async (account, amount) => { await b3tr.connect(owner).transfer(account.address, amount); const registryAddress = await navigatorRegistry.getAddress(); await b3tr.connect(account).approve(registryAddress, amount); }; // Helper: register a navigator with default stake const registerNavigator = async (account) => { await fundAndApprove(account, STAKE_AMOUNT); await navigatorRegistry.connect(account).register(STAKE_AMOUNT, METADATA_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; b3tr = deployment.b3tr; xAllocationVoting = deployment.xAllocationVoting; emissions = deployment.emissions; owner = deployment.owner; minterAccount = deployment.minterAccount; otherAccounts = deployment.otherAccounts; navigator1 = otherAccounts[10]; nonNavigator = otherAccounts[11]; // Mint B3TR to owner for navigator registration transfers await b3tr.connect(minterAccount).mint(owner.address, hardhat_1.ethers.parseEther("10000000")); // Create VOT3 supply (max stake = 1% of VOT3 supply, so need >= 5M VOT3 for 50k stake) await (0, common_1.getVot3Tokens)(otherAccounts[15], "10000000"); // Deploy NavigatorVotingUtils library for decisionToSupport tests const Factory = await hardhat_1.ethers.getContractFactory("NavigatorVotingUtils"); navigatorVotingUtils = (await Factory.deploy()); await navigatorVotingUtils.waitForDeployment(); // Register navigator await registerNavigator(navigator1); // Start emissions so we have a round await (0, common_1.bootstrapAndStartEmissions)(); roundId = await xAllocationVoting.currentRoundId(); }); // ======================== 1. setAllocationPreferences ======================== // (0, mocha_1.describe)("setAllocationPreferences()", function () { (0, mocha_1.it)("happy path: 2 apps [6000, 4000]", async function () { await navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1, app2], [6000, 4000]); const [appIds, percentages] = await navigatorRegistry.getAllocationPreferences(navigator1.address, roundId); (0, chai_1.expect)(appIds).to.deep.equal([app1, app2]); (0, chai_1.expect)(percentages).to.deep.equal([6000n, 4000n]); }); (0, mocha_1.it)("1 app with [10000] is valid", async function () { await navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1], [10000]); const [appIds, percentages] = await navigatorRegistry.getAllocationPreferences(navigator1.address, roundId); (0, chai_1.expect)(appIds).to.deep.equal([app1]); (0, chai_1.expect)(percentages).to.deep.equal([10000n]); }); (0, mocha_1.it)("15 apps is valid", async function () { const apps = []; const weights = []; for (let i = 0; i < 15; i++) { apps.push(hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes(`App${i}`))); weights.push(i < 14 ? 666 : 676); // 14 * 666 + 676 = 10000 } await navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, apps, weights); const [appIds] = await navigatorRegistry.getAllocationPreferences(navigator1.address, roundId); (0, chai_1.expect)(appIds.length).to.equal(15); }); (0, mocha_1.it)("16 apps reverts TooManyApps", async function () { const apps = []; const weights = []; for (let i = 0; i < 16; i++) { apps.push(hardhat_1.ethers.keccak256(hardhat_1.ethers.toUtf8Bytes(`App${i}`))); weights.push(i < 15 ? 625 : 625); // doesn't matter, should revert first } await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, apps, weights)).to.be.revertedWithCustomError(navigatorRegistry, "TooManyApps"); }); (0, mocha_1.it)("empty apps reverts EmptyPreferences", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [], [])).to.be.revertedWithCustomError(navigatorRegistry, "EmptyPreferences"); }); (0, mocha_1.it)("duplicate apps reverts DuplicateApp", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1, app1], [5000, 5000])).to.be.revertedWithCustomError(navigatorRegistry, "DuplicateApp"); }); (0, mocha_1.it)("percentages sum != 10000 reverts PercentageMismatch", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1, app2], [5000, 4000])).to.be.revertedWithCustomError(navigatorRegistry, "PercentageMismatch"); }); (0, mocha_1.it)("zero percentage reverts ZeroPercentage", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1, app2], [0, 10000])).to.be.revertedWithCustomError(navigatorRegistry, "ZeroPercentage"); }); (0, mocha_1.it)("already set for this round reverts PreferencesAlreadySet", async function () { await navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1], [10000]); await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app2], [10000])).to.be.revertedWithCustomError(navigatorRegistry, "PreferencesAlreadySet"); }); (0, mocha_1.it)("array length mismatch reverts LengthMismatch", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1, app2, app3], [5000, 5000])).to.be.revertedWithCustomError(navigatorVotingUtils, "LengthMismatch"); }); (0, mocha_1.it)("non-navigator reverts", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(nonNavigator).setAllocationPreferences(roundId, [app1], [10000])).to.be.revertedWithCustomError(navigatorRegistry, "NotRegistered"); }); (0, mocha_1.it)("hasSetPreferences returns true after setting", async function () { (0, chai_1.expect)(await navigatorRegistry.hasSetPreferences(navigator1.address, roundId)).to.equal(false); await navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1], [10000]); (0, chai_1.expect)(await navigatorRegistry.hasSetPreferences(navigator1.address, roundId)).to.equal(true); }); (0, mocha_1.it)("getPreferencesSetBlock returns non-zero after setting", async function () { (0, chai_1.expect)(await navigatorRegistry.getPreferencesSetBlock(navigator1.address, roundId)).to.equal(0n); await navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1], [10000]); (0, chai_1.expect)(await navigatorRegistry.getPreferencesSetBlock(navigator1.address, roundId)).to.be.gt(0n); }); (0, mocha_1.it)("emits AllocationPreferencesSet event", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setAllocationPreferences(roundId, [app1, app2], [6000, 4000])) .to.emit(navigatorRegistry, "AllocationPreferencesSet") .withArgs(navigator1.address, roundId, [app1, app2]); }); }); // ======================== 2. setProposalDecision ======================== // (0, mocha_1.describe)("setProposalDecision()", function () { const proposalId = 1n; (0, mocha_1.it)("decision 1 (Against): getProposalDecision returns 1", async function () { await navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 1); (0, chai_1.expect)(await navigatorRegistry.getProposalDecision(navigator1.address, proposalId)).to.equal(1); }); (0, mocha_1.it)("decision 2 (For): getProposalDecision returns 2", async function () { await navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 2); (0, chai_1.expect)(await navigatorRegistry.getProposalDecision(navigator1.address, proposalId)).to.equal(2); }); (0, mocha_1.it)("decision 3 (Abstain): getProposalDecision returns 3", async function () { await navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 3); (0, chai_1.expect)(await navigatorRegistry.getProposalDecision(navigator1.address, proposalId)).to.equal(3); }); (0, mocha_1.it)("decision 0 reverts InvalidDecision", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidDecision"); }); (0, mocha_1.it)("decision 4 reverts InvalidDecision", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 4)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidDecision"); }); (0, mocha_1.it)("already set reverts DecisionAlreadySet", async function () { await navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 2); await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 1)).to.be.revertedWithCustomError(navigatorRegistry, "DecisionAlreadySet"); }); (0, mocha_1.it)("non-navigator reverts", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(nonNavigator).setProposalDecision(proposalId, 2)).to.be.revertedWithCustomError(navigatorRegistry, "NotRegistered"); }); (0, mocha_1.it)("hasSetDecision: true after, false before", async function () { (0, chai_1.expect)(await navigatorRegistry.hasSetDecision(navigator1.address, proposalId)).to.equal(false); await navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 2); (0, chai_1.expect)(await navigatorRegistry.hasSetDecision(navigator1.address, proposalId)).to.equal(true); }); (0, mocha_1.it)("emits ProposalDecisionSet event", async function () { await (0, chai_1.expect)(navigatorRegistry.connect(navigator1).setProposalDecision(proposalId, 2)) .to.emit(navigatorRegistry, "ProposalDecisionSet") .withArgs(navigator1.address, proposalId, 2); }); }); // ======================== 3. decisionToSupport ======================== // (0, mocha_1.describe)("decisionToSupport()", function () { (0, mocha_1.it)("1 -> 0 (Against)", async function () { (0, chai_1.expect)(await navigatorVotingUtils.decisionToSupport(1)).to.equal(0); }); (0, mocha_1.it)("2 -> 1 (For)", async function () { (0, chai_1.expect)(await navigatorVotingUtils.decisionToSupport(2)).to.equal(1); }); (0, mocha_1.it)("3 -> 2 (Abstain)", async function () { (0, chai_1.expect)(await navigatorVotingUtils.decisionToSupport(3)).to.equal(2); }); (0, mocha_1.it)("0 reverts InvalidDecision", async function () { await (0, chai_1.expect)(navigatorVotingUtils.decisionToSupport(0)).to.be.revertedWithCustomError(navigatorVotingUtils, "InvalidDecision"); }); (0, mocha_1.it)("4 reverts InvalidDecision", async function () { await (0, chai_1.expect)(navigatorVotingUtils.decisionToSupport(4)).to.be.revertedWithCustomError(navigatorVotingUtils, "InvalidDecision"); }); }); // ======================== 4. getProposalDecision ======================== // (0, mocha_1.describe)("getProposalDecision()", function () { (0, mocha_1.it)("reverts DecisionNotSet if not set", async function () { const unsetProposalId = 999n; await (0, chai_1.expect)(navigatorRegistry.getProposalDecision(navigator1.address, unsetProposalId)).to.be.revertedWithCustomError(navigatorRegistry, "DecisionNotSet"); }); }); });