@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
160 lines (159 loc) • 11.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const local_1 = require("@repo/config/contracts/envs/local");
const chai_1 = require("chai");
const hardhat_1 = require("hardhat");
const mocha_1 = require("mocha");
const helpers_1 = require("../helpers");
const common_1 = require("../helpers/common");
(0, mocha_1.describe)("VOT3 - V2 Upgrade - @shard19a", function () {
(0, mocha_1.it)("Should deploy through full upgrade chain and report version 2", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3 } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
(0, chai_1.expect)(await vot3.version()).to.equal("2");
(0, chai_1.expect)(await vot3.getAddress()).to.not.equal(hardhat_1.ethers.ZeroAddress);
});
(0, mocha_1.it)("Should have NavigatorRegistry set after initializeV2", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, navigatorRegistry } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
const lockedAmount = await vot3.getNavigatorLockedAmount(hardhat_1.ethers.ZeroAddress);
(0, chai_1.expect)(lockedAmount).to.equal(0n);
// NavigatorRegistry should be wired
(0, chai_1.expect)(await navigatorRegistry.getAddress()).to.not.equal(hardhat_1.ethers.ZeroAddress);
});
(0, mocha_1.it)("Should preserve B3TR reference after upgrade", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, b3tr } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
(0, chai_1.expect)(await vot3.b3tr()).to.equal(await b3tr.getAddress());
});
(0, mocha_1.it)("Should preserve roles after upgrade", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
(0, chai_1.expect)(await vot3.hasRole(await vot3.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.true;
(0, chai_1.expect)(await vot3.hasRole(await vot3.UPGRADER_ROLE(), owner.address)).to.be.true;
(0, chai_1.expect)(await vot3.hasRole(await vot3.PAUSER_ROLE(), owner.address)).to.be.true;
});
(0, mocha_1.it)("Should still allow convertToVOT3 and convertToB3TR after upgrade", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, b3tr, owner, otherAccount, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
config,
});
// Mint B3TR to owner so we can transfer
const amount = hardhat_1.ethers.parseEther("100");
await b3tr.connect(minterAccount).mint(owner.address, amount);
await b3tr.connect(owner).transfer(otherAccount.address, amount);
// Approve and convert to VOT3
await b3tr.connect(otherAccount).approve(await vot3.getAddress(), amount);
await vot3.connect(otherAccount).convertToVOT3(amount);
(0, chai_1.expect)(await vot3.balanceOf(otherAccount.address)).to.equal(amount);
// Convert back to B3TR
await vot3.connect(otherAccount).convertToB3TR(amount);
(0, chai_1.expect)(await vot3.balanceOf(otherAccount.address)).to.equal(0n);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccount.address)).to.equal(amount);
});
(0, mocha_1.it)("Should enforce delegation lock after upgrade — transfer blocked for locked portion", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, b3tr, navigatorRegistry, owner, otherAccounts, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
config,
});
const navigator = otherAccounts[10];
const citizen = otherAccounts[11];
const recipient = otherAccounts[12];
// Mint B3TR to owner for distribution
await b3tr.connect(minterAccount).mint(owner.address, hardhat_1.ethers.parseEther("10000000"));
// Ensure VOT3 supply exists for max stake check
await (0, common_1.getVot3Tokens)(owner, "10000000");
// Register navigator
const stakeAmount = hardhat_1.ethers.parseEther("50000");
await b3tr.connect(owner).transfer(navigator.address, stakeAmount);
await b3tr.connect(navigator).approve(await navigatorRegistry.getAddress(), stakeAmount);
await navigatorRegistry.connect(navigator).register(stakeAmount, "ipfs://nav");
// Give citizen 1000 VOT3
await (0, common_1.getVot3Tokens)(citizen, "1000");
// Delegate 500 VOT3
const delegateAmount = hardhat_1.ethers.parseEther("500");
await navigatorRegistry.connect(citizen).delegate(navigator.address, delegateAmount);
// Locked = 500, free = 500
(0, chai_1.expect)(await vot3.getNavigatorLockedAmount(citizen.address)).to.equal(delegateAmount);
// Transfer 500 (free portion) should succeed
await vot3.connect(citizen).transfer(recipient.address, hardhat_1.ethers.parseEther("500"));
// Transfer 1 more wei should fail (would go below locked amount)
await (0, chai_1.expect)(vot3.connect(citizen).transfer(recipient.address, 1n)).to.be.revertedWith("VOT3: transfer exceeds unlocked balance");
// Undelegate: lock released
await navigatorRegistry.connect(citizen).undelegate();
(0, chai_1.expect)(await vot3.getNavigatorLockedAmount(citizen.address)).to.equal(0n);
});
(0, mocha_1.it)("Should not allow re-initialization of V2", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3 } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
await (0, chai_1.expect)(vot3.initializeV2(hardhat_1.ethers.ZeroAddress)).to.be.reverted;
});
(0, mocha_1.describe)("unlockedBalance", function () {
(0, mocha_1.it)("Should return full balance when no delegation exists", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
const citizen = otherAccounts[11];
const amount = hardhat_1.ethers.parseEther("1000");
await (0, common_1.getVot3Tokens)(citizen, "1000");
(0, chai_1.expect)(await vot3.unlockedBalance(citizen.address)).to.equal(amount);
(0, chai_1.expect)(await vot3.unlockedBalance(citizen.address)).to.equal(await vot3.balanceOf(citizen.address));
});
(0, mocha_1.it)("Should return balance minus delegated amount", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, b3tr, navigatorRegistry, owner, otherAccounts, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
const navigator = otherAccounts[10];
const citizen = otherAccounts[11];
await b3tr.connect(minterAccount).mint(owner.address, hardhat_1.ethers.parseEther("10000000"));
await (0, common_1.getVot3Tokens)(owner, "10000000");
const stakeAmount = hardhat_1.ethers.parseEther("50000");
await b3tr.connect(owner).transfer(navigator.address, stakeAmount);
await b3tr.connect(navigator).approve(await navigatorRegistry.getAddress(), stakeAmount);
await navigatorRegistry.connect(navigator).register(stakeAmount, "ipfs://nav");
await (0, common_1.getVot3Tokens)(citizen, "1000");
const delegateAmount = hardhat_1.ethers.parseEther("400");
await navigatorRegistry.connect(citizen).delegate(navigator.address, delegateAmount);
const expected = hardhat_1.ethers.parseEther("1000") - delegateAmount;
(0, chai_1.expect)(await vot3.unlockedBalance(citizen.address)).to.equal(expected);
});
(0, mocha_1.it)("Should return zero when entire balance is delegated", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, b3tr, navigatorRegistry, owner, otherAccounts, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
const navigator = otherAccounts[10];
const citizen = otherAccounts[11];
await b3tr.connect(minterAccount).mint(owner.address, hardhat_1.ethers.parseEther("10000000"));
await (0, common_1.getVot3Tokens)(owner, "10000000");
const stakeAmount = hardhat_1.ethers.parseEther("50000");
await b3tr.connect(owner).transfer(navigator.address, stakeAmount);
await b3tr.connect(navigator).approve(await navigatorRegistry.getAddress(), stakeAmount);
await navigatorRegistry.connect(navigator).register(stakeAmount, "ipfs://nav");
await (0, common_1.getVot3Tokens)(citizen, "1000");
await navigatorRegistry.connect(citizen).delegate(navigator.address, hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.unlockedBalance(citizen.address)).to.equal(0n);
});
(0, mocha_1.it)("Should restore full balance after undelegation", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, b3tr, navigatorRegistry, owner, otherAccounts, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
const navigator = otherAccounts[10];
const citizen = otherAccounts[11];
await b3tr.connect(minterAccount).mint(owner.address, hardhat_1.ethers.parseEther("10000000"));
await (0, common_1.getVot3Tokens)(owner, "10000000");
const stakeAmount = hardhat_1.ethers.parseEther("50000");
await b3tr.connect(owner).transfer(navigator.address, stakeAmount);
await b3tr.connect(navigator).approve(await navigatorRegistry.getAddress(), stakeAmount);
await navigatorRegistry.connect(navigator).register(stakeAmount, "ipfs://nav");
const citizenBalance = hardhat_1.ethers.parseEther("1000");
await (0, common_1.getVot3Tokens)(citizen, "1000");
await navigatorRegistry.connect(citizen).delegate(navigator.address, hardhat_1.ethers.parseEther("500"));
(0, chai_1.expect)(await vot3.unlockedBalance(citizen.address)).to.equal(hardhat_1.ethers.parseEther("500"));
await navigatorRegistry.connect(citizen).undelegate();
(0, chai_1.expect)(await vot3.unlockedBalance(citizen.address)).to.equal(citizenBalance);
});
(0, mocha_1.it)("Should return zero for account with no tokens", async () => {
const config = (0, local_1.createLocalConfig)();
const { vot3, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true, config });
(0, chai_1.expect)(await vot3.unlockedBalance(otherAccounts[15].address)).to.equal(0n);
});
});
});