@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
177 lines (176 loc) • 9.86 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const hardhat_1 = require("hardhat");
const chai_1 = require("chai");
const mocha_1 = require("mocha");
const helpers_1 = require("../helpers");
const helpers_2 = require("../helpers");
const xnodes_1 = require("../helpers/xnodes");
(0, mocha_1.describe)("XAllocationVoting - V10 Double Process Prevention - @shard14c", function () {
let navigatorRegistry;
let xAllocationVoting;
let b3tr;
let vot3;
let emissions;
let veBetterPassport;
let x2EarnApps;
let relayerRewardsPool;
let owner;
let minterAccount;
let otherAccounts;
let creators;
let nav1;
let citizenA;
let autoUser;
let relayer1;
let relayer2;
let app1Id;
let app2Id;
const STAKE = hardhat_1.ethers.parseEther("50000");
const DELEGATE_AMT = hardhat_1.ethers.parseEther("500");
const fundAndApprove = async (acct, amount) => {
await b3tr.connect(owner).transfer(acct.address, amount);
await b3tr.connect(acct).approve(await navigatorRegistry.getAddress(), amount);
};
const advanceRound = async () => {
const cur = await xAllocationVoting.currentRoundId();
await (0, helpers_2.waitForRoundToEnd)(Number(cur));
await emissions.distribute();
return xAllocationVoting.currentRoundId();
};
async function advancePastSkipWindow(roundId) {
const skipWindow = await xAllocationVoting.citizenSkipWindowBlocks();
const deadline = await xAllocationVoting.roundDeadline(roundId);
const currentBlock = BigInt(await hardhat_1.ethers.provider.getBlockNumber());
const blocksToMine = deadline - currentBlock - skipWindow;
if (blocksToMine > 0n)
await (0, helpers_1.moveBlocks)(Number(blocksToMine));
}
async function setup() {
const d = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
if (!d)
throw new Error("deploy failed");
navigatorRegistry = d.navigatorRegistry;
xAllocationVoting = d.xAllocationVoting;
b3tr = d.b3tr;
vot3 = d.vot3;
emissions = d.emissions;
veBetterPassport = d.veBetterPassport;
x2EarnApps = d.x2EarnApps;
relayerRewardsPool = d.relayerRewardsPool;
owner = d.owner;
minterAccount = d.minterAccount;
otherAccounts = d.otherAccounts;
creators = d.creators;
nav1 = otherAccounts[8];
citizenA = otherAccounts[10];
autoUser = otherAccounts[13];
relayer1 = otherAccounts[15];
relayer2 = otherAccounts[3];
await b3tr.connect(minterAccount).mint(owner.address, hardhat_1.ethers.parseEther("10000000"));
await (0, helpers_1.getVot3Tokens)(owner, "10000000");
// Apps
await x2EarnApps.connect(creators[0]).submitApp(creators[0].address, creators[0].address, "App1", "uri");
app1Id = await x2EarnApps.hashAppName("App1");
await (0, xnodes_1.endorseApp)(app1Id, otherAccounts[4]);
await x2EarnApps.connect(creators[1]).submitApp(creators[1].address, creators[1].address, "App2", "uri");
app2Id = await x2EarnApps.hashAppName("App2");
await (0, xnodes_1.endorseApp)(app2Id, otherAccounts[5]);
// Passport
if (!(await veBetterPassport.isCheckEnabled(1)))
await veBetterPassport.toggleCheck(1);
await veBetterPassport.whitelist(citizenA.address);
await veBetterPassport.whitelist(autoUser.address);
// VOT3
await (0, helpers_1.getVot3Tokens)(citizenA, "1000");
await (0, helpers_1.getVot3Tokens)(autoUser, "100");
// Register navigator
await fundAndApprove(nav1, STAKE);
await navigatorRegistry.connect(nav1).register(STAKE, "ipfs://nav1");
// Register relayers
await relayerRewardsPool.registerRelayer(relayer1.address);
await relayerRewardsPool.registerRelayer(relayer2.address);
await relayerRewardsPool.connect(owner).setRelayerFeePercentage(10);
await (0, helpers_2.bootstrapAndStartEmissions)();
await (0, helpers_1.waitForNextBlock)();
}
(0, mocha_1.describe)("Version check", function () {
(0, mocha_1.beforeEach)(setup);
(0, mocha_1.it)("should report version 10", async function () {
(0, chai_1.expect)(await xAllocationVoting.version()).to.equal("10");
});
});
(0, mocha_1.describe)("castVoteOnBehalfOf double-process prevention", function () {
(0, mocha_1.beforeEach)(setup);
(0, mocha_1.it)("should revert on retry after successful auto-vote", async function () {
// Enable auto-voting for user
await xAllocationVoting.connect(autoUser).setUserVotingPreferences([app1Id]);
await xAllocationVoting.connect(autoUser).toggleAutoVoting(autoUser.address);
// Start a new round
const roundId = await advanceRound();
// Relayer1 votes on behalf of autoUser
await xAllocationVoting.connect(relayer1).castVoteOnBehalfOf(autoUser.address, roundId);
// Relayer2 retries — should revert
await (0, chai_1.expect)(xAllocationVoting.connect(relayer2).castVoteOnBehalfOf(autoUser.address, roundId)).to.be.revertedWithCustomError(xAllocationVoting, "VoteAlreadyProcessed");
});
(0, mocha_1.it)("should revert on retry after auto-vote skip", async function () {
// Enable auto-voting for user with an app, then remove personhood
await xAllocationVoting.connect(autoUser).setUserVotingPreferences([app1Id]);
await xAllocationVoting.connect(autoUser).toggleAutoVoting(autoUser.address);
// Remove personhood so the vote will be skipped
await veBetterPassport.removeFromWhitelist(autoUser.address);
// Start a new round
const roundId = await advanceRound();
// Relayer1 tries — skipped (not a person)
await (0, chai_1.expect)(xAllocationVoting.connect(relayer1).castVoteOnBehalfOf(autoUser.address, roundId)).to.emit(xAllocationVoting, "AutoVoteSkipped");
// Relayer2 retries — should revert
await (0, chai_1.expect)(xAllocationVoting.connect(relayer2).castVoteOnBehalfOf(autoUser.address, roundId)).to.be.revertedWithCustomError(xAllocationVoting, "VoteAlreadyProcessed");
});
});
(0, mocha_1.describe)("castNavigatorVote double-process prevention", function () {
(0, mocha_1.beforeEach)(setup);
(0, mocha_1.it)("should revert on retry after successful navigator vote", async function () {
// Delegate citizen to navigator
await vot3.connect(citizenA).approve(await navigatorRegistry.getAddress(), DELEGATE_AMT);
await navigatorRegistry.connect(citizenA).delegate(nav1.address, DELEGATE_AMT);
// Start a new round
const roundId = await advanceRound();
// Navigator sets preferences
await navigatorRegistry.connect(nav1).setAllocationPreferences(roundId, [app1Id, app2Id], [5000, 5000]);
// Relayer1 votes on behalf of citizen
await xAllocationVoting.connect(relayer1).castNavigatorVote(citizenA.address, roundId);
// Relayer2 retries — should revert
await (0, chai_1.expect)(xAllocationVoting.connect(relayer2).castNavigatorVote(citizenA.address, roundId)).to.be.revertedWithCustomError(xAllocationVoting, "VoteAlreadyProcessed");
});
(0, mocha_1.it)("should revert on retry after navigator vote skip (dead navigator)", async function () {
// Delegate citizen to navigator
await vot3.connect(citizenA).approve(await navigatorRegistry.getAddress(), DELEGATE_AMT);
await navigatorRegistry.connect(citizenA).delegate(nav1.address, DELEGATE_AMT);
// Start a new round
const roundId = await advanceRound();
// Deactivate navigator (governance action)
await navigatorRegistry.connect(owner).deactivateNavigator(nav1.address, 0, false);
// Relayer1 tries — skip (dead navigator)
await (0, chai_1.expect)(xAllocationVoting.connect(relayer1).castNavigatorVote(citizenA.address, roundId))
.to.emit(xAllocationVoting, "NavigatorVoteSkipped")
.withArgs(citizenA.address, nav1.address, roundId);
// Relayer2 retries — should revert
await (0, chai_1.expect)(xAllocationVoting.connect(relayer2).castNavigatorVote(citizenA.address, roundId)).to.be.revertedWithCustomError(xAllocationVoting, "VoteAlreadyProcessed");
});
(0, mocha_1.it)("should revert on retry after navigator vote skip (no preferences past skip window)", async function () {
// Delegate citizen to navigator
await vot3.connect(citizenA).approve(await navigatorRegistry.getAddress(), DELEGATE_AMT);
await navigatorRegistry.connect(citizenA).delegate(nav1.address, DELEGATE_AMT);
// Start a new round (navigator does NOT set prefs)
const roundId = await advanceRound();
// Advance past skip window
await advancePastSkipWindow(roundId);
// Relayer1 skips
await (0, chai_1.expect)(xAllocationVoting.connect(relayer1).castNavigatorVote(citizenA.address, roundId))
.to.emit(xAllocationVoting, "NavigatorVoteSkipped")
.withArgs(citizenA.address, nav1.address, roundId);
// Relayer2 retries — should revert
await (0, chai_1.expect)(xAllocationVoting.connect(relayer2).castNavigatorVote(citizenA.address, roundId)).to.be.revertedWithCustomError(xAllocationVoting, "VoteAlreadyProcessed");
});
});
});