@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
509 lines (508 loc) • 39.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const hardhat_1 = require("hardhat");
const chai_1 = require("chai");
const helpers_1 = require("./helpers");
const mocha_1 = require("mocha");
const upgrades_core_1 = require("@openzeppelin/upgrades-core");
const helpers_2 = require("../scripts/helpers");
(0, mocha_1.describe)("VOT3 - @shard9", function () {
(0, mocha_1.describe)("Deployment", function () {
(0, mocha_1.it)("should deploy the contract", async function () {
const { vot3 } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false });
await vot3.waitForDeployment();
const address = await vot3.getAddress();
(0, chai_1.expect)(address).not.to.eql(undefined);
});
(0, mocha_1.it)("should have the correct name", async function () {
const { vot3 } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false });
const res = await vot3.name();
(0, chai_1.expect)(res).to.eql("VOT3");
const res2 = await vot3.symbol();
(0, chai_1.expect)(res2).to.eql("VOT3");
});
(0, mocha_1.it)("admin role is set correctly upon deploy", async function () {
const { vot3, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false });
const defaultAdminRole = await vot3.DEFAULT_ADMIN_ROLE();
const res = await vot3.hasRole(defaultAdminRole, owner);
(0, chai_1.expect)(res).to.eql(true);
});
(0, mocha_1.it)("Only admin should be able to pause and unpause VOT3 contract", async function () {
const { vot3, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
(0, chai_1.expect)(await vot3.hasRole(await vot3.PAUSER_ROLE(), owner.address)).to.eql(true);
await vot3.connect(owner).pause();
await vot3.connect(owner).unpause();
(0, chai_1.expect)(await vot3.hasRole(await vot3.PAUSER_ROLE(), otherAccount.address)).to.eql(false);
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).pause());
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).unpause());
});
(0, mocha_1.it)("Should be able to pause and unpause VOT3 contract", async function () {
const { vot3, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
(0, chai_1.expect)(await vot3.hasRole(await vot3.PAUSER_ROLE(), owner.address)).to.eql(true);
(0, chai_1.expect)(await vot3.hasRole(await vot3.PAUSER_ROLE(), otherAccount.address)).to.eql(false);
await vot3.connect(owner).pause();
await (0, chai_1.expect)(vot3.connect(otherAccount).unpause()).to.be.reverted; // only admin can unpause
await vot3.connect(owner).unpause();
await (0, chai_1.expect)(vot3.connect(otherAccount).pause()).to.be.reverted; // only admin can pause
});
(0, mocha_1.it)("Should be able to get nonces", async function () {
const { vot3, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await vot3.nonces(owner);
});
(0, mocha_1.it)("Should be able to get b3tr address", async function () {
const { vot3, b3tr } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
(0, chai_1.expect)(await vot3.b3tr()).to.eql(await b3tr.getAddress());
});
(0, mocha_1.it)("Should revert if B3TR is set to zero address in initilisation", async () => {
const { owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
await (0, chai_1.expect)((0, helpers_2.deployProxy)("VOT3", [owner.address, owner.address, owner.address, helpers_1.ZERO_ADDRESS])).to.be.reverted;
});
(0, mocha_1.it)("Should revert if admin is set to zero address in initilisation", async () => {
const { owner, b3tr } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
await (0, chai_1.expect)((0, helpers_2.deployProxy)("VOT3", [helpers_1.ZERO_ADDRESS, owner.address, owner.address, await b3tr.getAddress()])).to.be
.reverted;
});
});
(0, mocha_1.describe)("Contract upgradeablity", () => {
(0, mocha_1.it)("Admin should be able to upgrade the contract", async function () {
const { vot3, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await hardhat_1.ethers.getContractFactory("VOT3");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await vot3.getAddress());
const UPGRADER_ROLE = await vot3.UPGRADER_ROLE();
(0, chai_1.expect)(await vot3.hasRole(UPGRADER_ROLE, owner.address)).to.eql(true);
await (0, chai_1.expect)(vot3.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be.reverted;
const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await vot3.getAddress());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase());
});
(0, mocha_1.it)("Only admin should be able to upgrade the contract", async function () {
const { vot3, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await hardhat_1.ethers.getContractFactory("VOT3");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await vot3.getAddress());
const UPGRADER_ROLE = await vot3.UPGRADER_ROLE();
(0, chai_1.expect)(await vot3.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false);
await (0, chai_1.expect)(vot3.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.be.reverted;
const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await vot3.getAddress());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql(currentImplAddress.toUpperCase());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql((await implementation.getAddress()).toUpperCase());
});
(0, mocha_1.it)("Admin can change UPGRADER_ROLE", async function () {
const { vot3, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await hardhat_1.ethers.getContractFactory("VOT3");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await vot3.getAddress());
const UPGRADER_ROLE = await vot3.UPGRADER_ROLE();
(0, chai_1.expect)(await vot3.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false);
await (0, chai_1.expect)(vot3.connect(owner).grantRole(UPGRADER_ROLE, otherAccount.address)).to.not.be.reverted;
await (0, chai_1.expect)(vot3.connect(owner).revokeRole(UPGRADER_ROLE, owner.address)).to.not.be.reverted;
await (0, chai_1.expect)(vot3.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be
.reverted;
const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await vot3.getAddress());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase());
});
(0, mocha_1.it)("Should not be able to initialize the contract after it has already been initialized", async function () {
const { vot3, owner, b3tr } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
await (0, chai_1.expect)(vot3.initialize(owner.address, owner.address, owner.address, await b3tr.getAddress())).to.be.reverted; // already initialized
});
(0, mocha_1.it)("Should return correct version of the contract", async () => {
const { vot3 } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
(0, chai_1.expect)(await vot3.version()).to.equal("2");
});
});
(0, mocha_1.describe)("Lock B3TR", function () {
(0, mocha_1.it)("should lock B3TR and mint VOT3", async function () {
const { b3tr, vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("9"))).not.to.be
.reverted;
// Lock B3TR to get VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("9"))).not.to.be.reverted;
// Check balances
(0, chai_1.expect)(await b3tr.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("991"));
(0, chai_1.expect)(await b3tr.balanceOf(await vot3.getAddress())).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
});
(0, mocha_1.it)("should not lock B3TR if not enough B3TR approved", async function () {
const { b3tr, vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("9"))).not.to.be
.reverted;
// Lock B3TR to get VOT3
await (0, helpers_1.catchRevert)(vot3.convertToVOT3(hardhat_1.ethers.parseEther("10")));
});
(0, mocha_1.it)("Should not be able to lock B3TR if VOT3 contract is paused", async function () {
const { b3tr, vot3, minterAccount, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Mint some B3TR
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("9"))).not.to.be
.reverted;
// Pause VOT3
await vot3.connect(owner).pause();
// Lock B3TR to get VOT3
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("9")));
// Unpause VOT3
await vot3.connect(owner).unpause();
// Lock B3TR to get VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("9"))).not.to.be.reverted;
});
});
(0, mocha_1.describe)("Unlock B3TR", function () {
(0, mocha_1.it)("should burn VOT3 and unlock B3TR", async function () {
const { b3tr, vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
const vot3Address = await vot3.getAddress();
// Mint some B3TR
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(vot3Address, hardhat_1.ethers.parseEther("9"))).not.to.be.reverted;
// Lock B3TR to get VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("9"))).not.to.be.reverted;
// Check balances
(0, chai_1.expect)(await b3tr.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("991"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await b3tr.balanceOf(vot3Address)).to.eql(hardhat_1.ethers.parseEther("9"));
// Unlock B3TR to burn VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("9"), { gasLimit: 10000000 })).not.to.be
.reverted;
// Check balances
(0, chai_1.expect)(await b3tr.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await b3tr.balanceOf(vot3Address)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
});
(0, mocha_1.it)("should not unlock B3TR if not enough VOT3", async function () {
const { b3tr, vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("9"))).not.to.be
.reverted;
// Lock B3TR to get VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("9"), { gasLimit: 10000000 })).not.to.be
.reverted;
// Check balances
(0, chai_1.expect)(await b3tr.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("991"));
(0, chai_1.expect)(await b3tr.balanceOf(await vot3.getAddress())).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
// Unlock B3TR to burn VOT3
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("10"), { gasLimit: 10000000 }));
});
(0, mocha_1.it)("should not unlock B3TR if not enough converted balance, even if there is enough VOT3 balance", async function () {
const { b3tr, vot3, minterAccount, otherAccount, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Mint some B3TR to two accounts
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccounts[0], hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("7"))).not.to.be
.reverted;
await (0, chai_1.expect)(b3tr.connect(otherAccounts[0]).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("8"))).not.to.be
.reverted;
// Lock B3TR to get VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("7"), { gasLimit: 10000000 })).not.to.be
.reverted;
await (0, chai_1.expect)(vot3.connect(otherAccounts[0]).convertToVOT3(hardhat_1.ethers.parseEther("8"), { gasLimit: 10000000 })).not
.to.be.reverted;
// Check balances
(0, chai_1.expect)(await b3tr.balanceOf(await vot3.getAddress())).to.eql(hardhat_1.ethers.parseEther("15"));
(0, chai_1.expect)(await b3tr.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("993"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("7"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("7"));
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[0])).to.eql(hardhat_1.ethers.parseEther("992"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccounts[0])).to.eql(hardhat_1.ethers.parseEther("8"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccounts[0])).to.eql(hardhat_1.ethers.parseEther("8"));
// Transfer VOT3 from otherAccounts[0] to otherAccount
await (0, chai_1.expect)(vot3.connect(otherAccounts[0]).transfer(otherAccount, hardhat_1.ethers.parseEther("2"), { gasLimit: 10000000 })).not.to.be.reverted;
// Check balances
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("7"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccounts[0])).to.eql(hardhat_1.ethers.parseEther("6"));
(0, chai_1.expect)(await vot3.convertedB3trOf(otherAccounts[0])).to.eql(hardhat_1.ethers.parseEther("8"));
// Attempt to unlock 8 VOT3 from otherAccount
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("8"), { gasLimit: 10000000 }));
// Finally unlock 7 VOT3 from otherAccount
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("7"), { gasLimit: 10000000 })).not.to.be
.reverted;
});
(0, mocha_1.it)("Should not be able to unlock B3TR if VOT3 transfers are paused", async function () {
const { b3tr, vot3, minterAccount, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Mint some B3TR
await (0, chai_1.expect)(b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"))).not.to.be.reverted;
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await (0, chai_1.expect)(b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("9"))).not.to.be
.reverted;
// Lock B3TR to get VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("9"), { gasLimit: 10000000 })).not.to.be
.reverted;
// Pause VOT3
await vot3.connect(owner).pause();
// Unlock B3TR to burn VOT3
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("9"), { gasLimit: 10000000 }));
// Unpause VOT3
await vot3.connect(owner).unpause();
// Unlock B3TR to burn VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("9"), { gasLimit: 10000000 })).not.to.be
.reverted;
});
});
(0, mocha_1.describe)("Delegation", function () {
(0, mocha_1.it)("User that never owned VOT3 should have 0 voting rights and no delegation set", async function () {
const { vot3, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql("0x0000000000000000000000000000000000000000");
});
(0, mocha_1.it)("Self-delegation should be automatic upon swapping B3TR for VOT3", async function () {
const { vot3, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(otherAccount.address);
});
(0, mocha_1.it)("Self-delegation should be automatic upon receiving VOT3 from another user", async function () {
const { vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(minterAccount, "1000");
// user has no VOT3 and no delegation
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql("0x0000000000000000000000000000000000000000");
// transfer
await vot3.connect(minterAccount).transfer(otherAccount, hardhat_1.ethers.parseEther("1"));
// check that delegation was automatic
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(otherAccount.address);
});
(0, mocha_1.it)("Vote power is being tracked correctly", async function () {
const { vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// Initial state: 1000 VOT3, 1000 voting power, self-delegated
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(otherAccount.address);
// transfer
let tx = await vot3
.connect(otherAccount)
.transfer(minterAccount, hardhat_1.ethers.parseEther("1"), { gasLimit: 10000000 });
const receipt = await tx.wait();
if (!receipt)
chai_1.assert.fail("No receipt");
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("999"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("999"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(otherAccount.address);
(0, chai_1.expect)(await vot3.getPastVotes(otherAccount, receipt.blockNumber - 1)).to.eql(hardhat_1.ethers.parseEther("1000"));
// transfer back
await vot3.connect(minterAccount).transfer(otherAccount, hardhat_1.ethers.parseEther("1"), { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(otherAccount.address);
// convertToB3TR
await vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("1000"), { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(otherAccount.address);
// we should not count voting power for the VOT3 contract itself
(0, chai_1.expect)(await vot3.delegates(await vot3.getAddress())).to.eql("0x0000000000000000000000000000000000000000");
});
(0, mocha_1.it)("Automatic self-delegation should be triggered only once", async function () {
const { vot3, b3tr, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Mint some B3TR
await b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"));
// Approve VOT3 to spend B3TR on behalf of otherAccount. N.B. this is an important step and could be included in a multi clause transaction
await b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("1000"));
// Lock B3TR to get VOT3
const tx = await vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("1000"));
let receipt = await tx.wait();
let events = receipt?.logs;
if (!events)
chai_1.assert.fail("No events");
// DelegateChanged event should be emitted
let delegateChangedEvents = events.filter((event) => event.fragment && event.fragment.name === "DelegateChanged");
(0, chai_1.expect)(delegateChangedEvents).not.to.eql([]);
// Now if I do it again, it should not emit the event because it's already delegated to itself
await b3tr.connect(minterAccount).mint(otherAccount, hardhat_1.ethers.parseEther("1000"));
await b3tr.connect(otherAccount).approve(await vot3.getAddress(), hardhat_1.ethers.parseEther("1000"));
const secondTx = await vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("1000"));
receipt = await secondTx.wait();
events = receipt?.logs;
if (!events)
chai_1.assert.fail("No events");
// DelegateChanged event should not be emitted
delegateChangedEvents = events.filter((event) => event.fragment && event.fragment.name === "DelegateChanged");
(0, chai_1.expect)(delegateChangedEvents).to.eql([]);
});
(0, mocha_1.it)("Delegation to another user should still be possible", async function () {
const { vot3, minterAccount, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// delegate to another user
await vot3.connect(otherAccount).delegate(minterAccount.address, { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(minterAccount.address);
(0, chai_1.expect)(await vot3.balanceOf(minterAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.getVotes(minterAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
});
(0, mocha_1.it)("Self delegation is not happening for the VOT3 contract itself", async function () {
const { vot3, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// delegate to another user
await vot3.connect(otherAccount).convertToB3TR(hardhat_1.ethers.parseEther("1000"), { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.getVotes(await vot3.getAddress())).to.eql(hardhat_1.ethers.parseEther("0"));
});
(0, mocha_1.it)("Self delegation is not happening if interacting with another contract", async function () {
const { vot3, otherAccount, b3tr } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
await vot3
.connect(otherAccount)
.transfer(await b3tr.getAddress(), hardhat_1.ethers.parseEther("1"), { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.getVotes(await b3tr.getAddress())).to.eql(hardhat_1.ethers.parseEther("0"));
});
(0, mocha_1.it)("Should not trigger self-delegation if VOT3 transfers are paused", async function () {
const { vot3, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Pause VOT3
await vot3.connect(owner).pause();
// Mint some B3TR and Convert B3TR for VOT3
await (0, chai_1.expect)(vot3.connect(otherAccount).convertToVOT3(hardhat_1.ethers.parseEther("1000"))).to.be.reverted;
// Unpause VOT3
await vot3.connect(owner).unpause();
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// delegate to another user
await vot3.connect(otherAccount).delegate(owner.address, { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(owner.address);
(0, chai_1.expect)(await vot3.balanceOf(owner)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.getVotes(owner)).to.eql(hardhat_1.ethers.parseEther("1000"));
});
(0, mocha_1.it)("Should not be able to delegate voting power if VOT3 transfers are paused", async function () {
const { vot3, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// Pause VOT3
await vot3.connect(owner).pause();
// delegate to another user
await (0, helpers_1.catchRevert)(vot3.connect(otherAccount).delegate(owner.address, { gasLimit: 10000000 }));
// Unpause VOT3
await vot3.connect(owner).unpause();
// delegate to another user
await vot3.connect(otherAccount).delegate(owner.address, { gasLimit: 10000000 });
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("1000"));
(0, chai_1.expect)(await vot3.getVotes(otherAccount)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.delegates(otherAccount)).to.eql(owner.address);
(0, chai_1.expect)(await vot3.balanceOf(owner)).to.eql(hardhat_1.ethers.parseEther("0"));
(0, chai_1.expect)(await vot3.getVotes(owner)).to.eql(hardhat_1.ethers.parseEther("1000"));
});
});
(0, mocha_1.describe)("Voting power", function () {
let vot3Contract;
let other;
let accounts;
this.beforeAll(async function () {
const { vot3, otherAccount, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
vot3Contract = vot3;
other = otherAccount;
accounts = otherAccounts;
});
(0, mocha_1.it)("Voting power should be the square root of the amount of vote", async function () {
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(other, "1000");
// Initial state: 1000 VOT3, 1000 voting power, self-delegated
(0, chai_1.expect)(await vot3Contract.balanceOf(other)).to.eql(hardhat_1.ethers.parseEther("1000"));
//scale down by 1e9 as sqrt of 10^18 is 10^9
(0, chai_1.expect)(await vot3Contract.getQuadraticVotingPower(other)).to.eql(hardhat_1.ethers.parseEther("31.622776601"));
});
(0, mocha_1.it)("Voting power should be the square root of the amount of delegated votes", async function () {
await (0, helpers_1.getVot3Tokens)(accounts[1], "9");
await (0, helpers_1.getVot3Tokens)(accounts[2], "7");
(0, chai_1.expect)(await vot3Contract.balanceOf(accounts[1])).to.eql(hardhat_1.ethers.parseEther("9"));
(0, chai_1.expect)(await vot3Contract.balanceOf(accounts[2])).to.eql(hardhat_1.ethers.parseEther("7"));
// delegate to another user
await vot3Contract.connect(accounts[1]).delegate(accounts[2].address, { gasLimit: 10000000 });
// Initial state: 7 VOT3, recived 9 delegated VOT3s, 16 VOT3s in total and 4 voting power
(0, chai_1.expect)(await vot3Contract.getVotes(accounts[2])).to.eql(hardhat_1.ethers.parseEther("16"));
(0, chai_1.expect)(await vot3Contract.getQuadraticVotingPower(accounts[2])).to.eql(hardhat_1.ethers.parseEther("4"));
});
(0, mocha_1.it)("Should be able to get past voting power", async function () {
await (0, helpers_1.getVot3Tokens)(accounts[3], "50000");
// Initial state: 50000 VOT3, sqrt(50000) voting power, self-delegated
(0, chai_1.expect)(await vot3Contract.balanceOf(accounts[3])).to.eql(hardhat_1.ethers.parseEther("50000"));
(0, chai_1.expect)(await vot3Contract.getQuadraticVotingPower(accounts[3])).to.eql(hardhat_1.ethers.parseEther("223.606797749"));
// Transfer to another account
const tx = await vot3Contract.connect(accounts[3]).transfer(accounts[4].address, hardhat_1.ethers.parseEther("10000"));
const receipt = await tx.wait();
// 40000 VOT3, sqrt(40000) voting power, self-delegated
(0, chai_1.expect)(await vot3Contract.balanceOf(accounts[3])).to.eql(hardhat_1.ethers.parseEther("40000"));
(0, chai_1.expect)(await vot3Contract.getQuadraticVotingPower(accounts[3])).to.eql(hardhat_1.ethers.parseEther("200"));
// Voting power should be the same as before the transfer
(0, chai_1.expect)(await vot3Contract.getPastQuadraticVotingPower(accounts[3], receipt.blockNumber - 1)).to.eql(hardhat_1.ethers.parseEther("223.606797749"));
});
});
(0, mocha_1.describe)("Transferring", function () {
(0, mocha_1.it)("Should be able to transfer VOT3", async function () {
const { vot3, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// transfer
await vot3.connect(otherAccount).transfer(owner.address, hardhat_1.ethers.parseEther("1"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("999"));
(0, chai_1.expect)(await vot3.balanceOf(owner)).to.eql(hardhat_1.ethers.parseEther("1"));
});
(0, mocha_1.it)("Should be able to approve and transferFrom VOT3", async function () {
const { vot3, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Mint some B3TR and Convert B3TR for VOT3
await (0, helpers_1.getVot3Tokens)(otherAccount, "1000");
// approve
await vot3.connect(otherAccount).approve(owner.address, hardhat_1.ethers.parseEther("1"));
// transferFrom
await vot3.connect(owner).transferFrom(otherAccount.address, owner.address, hardhat_1.ethers.parseEther("1"));
(0, chai_1.expect)(await vot3.balanceOf(otherAccount)).to.eql(hardhat_1.ethers.parseEther("999"));
(0, chai_1.expect)(await vot3.balanceOf(owner)).to.eql(hardhat_1.ethers.parseEther("1"));
});
});
});