@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
541 lines (540 loc) • 37.4 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 ethers_1 = require("ethers");
(0, mocha_1.describe)("B3TR Multi Sig - @shard0", function () {
(0, mocha_1.describe)("Constructor", function () {
(0, mocha_1.it)("should deploy the contract", async function () {
const { b3trMultiSig } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: false });
await b3trMultiSig.waitForDeployment();
const address = await b3trMultiSig.getAddress();
(0, chai_1.expect)(address).not.to.eql(undefined);
});
(0, mocha_1.it)("Initializes owners correctly", async function () {
const { b3trMultiSig, owner, otherAccount, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
});
(0, mocha_1.it)("Sets required confirmations", async function () {
const { b3trMultiSig } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const threshold = await b3trMultiSig.required();
(0, chai_1.expect)(threshold).to.eql(2n);
});
(0, mocha_1.it)("Rejects duplicate owners", async function () {
const { owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
await (0, chai_1.expect)(B3TRMultiSig.deploy([owner.address, owner.address, owner.address], 2)).to.be.reverted;
});
(0, mocha_1.it)("Rejects zero address", async function () {
const { owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
await (0, chai_1.expect)(B3TRMultiSig.deploy([ethers_1.ZeroAddress, owner.address, otherAccount.address], 2)).to.be.reverted;
});
(0, mocha_1.it)("Rejects if required is greater than owner count", async function () {
const { owner, otherAccount, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
await (0, chai_1.expect)(B3TRMultiSig.deploy([minterAccount.address, owner.address, otherAccount.address], 10)).to.be.reverted;
});
});
(0, mocha_1.describe)("Submit Transaction", function () {
(0, mocha_1.it)("Owner can submit transaction", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await (0, chai_1.expect)(b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall)).to
.not.be.reverted;
});
(0, mocha_1.it)("Emits Submission and Confirmation", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
const tx = b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall);
await (0, chai_1.expect)(tx).to.emit(b3trMultiSig, "Submission").withArgs(1);
await (0, chai_1.expect)(tx).to.emit(b3trMultiSig, "Confirmation").withArgs(owner.address, 1);
});
(0, mocha_1.it)("Non Owner cannot submit transaction", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: false,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccounts[1]).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall)).to.be.reverted;
});
});
(0, mocha_1.describe)("Confirm Transaction", function () {
(0, mocha_1.it)("Owner can confirm transaction", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0)).to.not.be.reverted;
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
});
(0, mocha_1.it)("TX creator is automatically confirmed", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
});
(0, mocha_1.it)("Emits confiration event", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
});
(0, mocha_1.it)("Double-confirmation fails", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(owner).confirmTransaction(0)).to.be.reverted;
});
(0, mocha_1.it)("Non Owner cannot confirm", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccounts[1]).confirmTransaction(0)).to.be.reverted;
});
});
(0, mocha_1.describe)("Revoke Transaction", function () {
(0, mocha_1.it)("Owner can revoke transaction", async function () {
const { b3trMultiSig, B3trContract, b3tr, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(owner).revokeConfirmation(0)).to.not.be.reverted;
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([]);
});
(0, mocha_1.it)("Should emit event revoked", async function () {
const { b3trMultiSig, B3trContract, b3tr, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(owner).revokeConfirmation(0))
.to.emit(b3trMultiSig, "Revocation")
.withArgs(owner.address, 0);
});
(0, mocha_1.it)("Should revert if revoking before confirming", async function () {
const { b3trMultiSig, B3trContract, b3tr, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("10"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).revokeConfirmation(0)).to.be.reverted;
});
});
(0, mocha_1.describe)("Execute Transaction", function () {
(0, mocha_1.it)("Transaction gets executed succesfully", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccounts[10].address,
hardhat_1.ethers.parseEther("10"),
]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(0n);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(hardhat_1.ethers.parseEther("10"));
(0, chai_1.expect)(await b3trMultiSig.isConfirmed(0)).to.eql(true);
});
(0, mocha_1.it)("Should revert if trying to execute an already executed transaction", async function () {
const { b3trMultiSig, B3trContract, b3tr, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
const encoded = B3trContract.interface.encodeFunctionData("mint", [otherAccount.address, hardhat_1.ethers.parseEther("1")]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encoded);
await b3trMultiSig.connect(otherAccount).confirmTransaction(0);
// Already executed
await (0, chai_1.expect)(b3trMultiSig.connect(owner).executeTransaction(0)).to.be.revertedWith("Transaction already executed");
});
(0, mocha_1.it)("Should emit event if executed succesfully", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccounts[10].address,
hardhat_1.ethers.parseEther("10"),
]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(0n);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Execution")
.withArgs(0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(hardhat_1.ethers.parseEther("10"));
});
(0, mocha_1.it)("Should emit event if executed unsuccesfully", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccounts[10].address,
hardhat_1.ethers.parseEther("10"),
]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(0n);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
// Should fail as the multi sig does not have the minter role
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "ExecutionFailure")
.withArgs(0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(hardhat_1.ethers.parseEther("0"));
});
(0, mocha_1.it)("Should be able to retry if unsuccesfully", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccounts[10].address,
hardhat_1.ethers.parseEther("10"),
]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(0n);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
// Should fail as the multi sig does not have the minter role
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "ExecutionFailure")
.withArgs(0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(hardhat_1.ethers.parseEther("0"));
// Granting the minter role to the multisig
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
// Retry
await b3trMultiSig.connect(owner).executeTransaction(0);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(hardhat_1.ethers.parseEther("10"));
});
(0, mocha_1.it)("Should not be able to retry if succesful", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccounts[10].address,
hardhat_1.ethers.parseEther("10"),
]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(0n);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
// Confirming the transaction
await b3trMultiSig.connect(otherAccount).confirmTransaction(0);
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0))
.to.eql([owner.address, otherAccount.address])
.to.emit(b3trMultiSig, "S")
.withArgs(otherAccount.address, 0);
// Should fail as the multi sig does not have the minter role
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).executeTransaction(0)).to.be.reverted;
});
});
(0, mocha_1.describe)("Owner Management", function () {
(0, mocha_1.it)("Proposal must be made to add new owner", async function () {
const { b3trMultiSig, minterAccount, b3tr, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
const encodedFunctionCall = B3TRMultiSig.interface.encodeFunctionData("addOwner", [otherAccounts[10].address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([
owner.address,
otherAccount.address,
minterAccount.address,
otherAccounts[10].address,
]);
});
(0, mocha_1.it)("Should revert if proposal is not made to add new owner", async function () {
const { b3trMultiSig, minterAccount, otherAccount, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(owner).addOwner(otherAccounts[10].address)).to.be.reverted;
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address, otherAccount.address, minterAccount.address]);
});
(0, mocha_1.it)("Proposal can be made to remove owner", async function () {
const { b3trMultiSig, minterAccount, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
const encodedFunctionCall = B3TRMultiSig.interface.encodeFunctionData("removeOwner", [minterAccount.address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address, otherAccount.address]);
});
(0, mocha_1.it)("Proposal can be made to remove owner", async function () {
const { b3trMultiSig, minterAccount, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
const encodedFunctionCall = B3TRMultiSig.interface.encodeFunctionData("removeOwner", [minterAccount.address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address, otherAccount.address]);
const encodedFunctionCall2 = B3TRMultiSig.interface.encodeFunctionData("removeOwner", [otherAccount.address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall2); /// TxId will be 0
await b3trMultiSig.connect(otherAccount).confirmTransaction(1);
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address]);
const encodedFunctionCall3 = B3TRMultiSig.interface.encodeFunctionData("removeOwner", [owner.address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall3); /// TxId will be 0
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(1)).to.be.reverted;
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address]);
});
(0, mocha_1.it)("If owners becomes less than required confirmations confiratirmations becomes equal to number of owners", async function () {
const { minterAccount, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
const b3trMultiSig = await B3TRMultiSig.deploy([owner.address, otherAccount.address, minterAccount.address], 3);
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.required()).to.eql(3n);
const encodedFunctionCall = B3TRMultiSig.interface.encodeFunctionData("removeOwner", [minterAccount.address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
await (0, chai_1.expect)(b3trMultiSig.connect(minterAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(minterAccount.address, 0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.required()).to.eql(2n);
});
(0, mocha_1.it)("Should be able to SWAP owners", async function () {
const { minterAccount, otherAccount, owner, b3trMultiSig, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const B3TRMultiSig = await hardhat_1.ethers.getContractFactory("B3TRMultiSig");
const owners = await b3trMultiSig.getOwners();
(0, chai_1.expect)(owners).to.eql([owner.address, otherAccount.address, minterAccount.address]);
const encodedFunctionCall = B3TRMultiSig.interface.encodeFunctionData("replaceOwner", [
minterAccount.address,
otherAccounts[10].address,
]);
//Current owners are [owner, otherAccount, minterAccount]
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address, otherAccount.address, minterAccount.address]);
await b3trMultiSig.connect(owner).submitTransaction(await b3trMultiSig.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address]);
await (0, chai_1.expect)(b3trMultiSig.connect(otherAccount).confirmTransaction(0))
.to.emit(b3trMultiSig, "Confirmation")
.withArgs(otherAccount.address, 0);
// 2/3 confirmations done -> Executution should have happened
(0, chai_1.expect)(await b3trMultiSig.getConfirmations(0)).to.eql([owner.address, otherAccount.address]);
(0, chai_1.expect)(await b3trMultiSig.getOwners()).to.eql([owner.address, otherAccount.address, otherAccounts[10].address]);
(0, chai_1.expect)(await b3trMultiSig.isOwner(minterAccount.address)).to.eql(false);
(0, chai_1.expect)(await b3trMultiSig.isOwner(otherAccounts[10].address)).to.eql(true);
});
});
(0, mocha_1.describe)("View Functions - getTransactionCount & getTransactionIds", function () {
(0, mocha_1.it)("Correctly returns count for pending and executed transactions", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
// Submit TX 0 - Not executed yet
const encodedCall1 = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("1"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedCall1);
// Submit TX 1 - Will be executed
const encodedCall2 = B3trContract.interface.encodeFunctionData("mint", [
minterAccount.address,
hardhat_1.ethers.parseEther("2"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedCall2);
await b3trMultiSig.connect(otherAccount).confirmTransaction(1);
const pendingCount = await b3trMultiSig.getTransactionCount(true, false);
const executedCount = await b3trMultiSig.getTransactionCount(false, true);
const allCount = await b3trMultiSig.getTransactionCount(true, true);
(0, chai_1.expect)(pendingCount).to.eql(1n);
(0, chai_1.expect)(executedCount).to.eql(1n);
(0, chai_1.expect)(allCount).to.eql(2n);
});
(0, mocha_1.it)("Correctly returns transaction IDs by status and range", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner, minterAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const MINTER_ROLE = await b3tr.MINTER_ROLE();
await b3tr.connect(owner).grantRole(MINTER_ROLE, await b3trMultiSig.getAddress());
// TX 0 - Not executed
const encodedCall1 = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("3"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedCall1);
// TX 1 - Executed
const encodedCall2 = B3trContract.interface.encodeFunctionData("mint", [
minterAccount.address,
hardhat_1.ethers.parseEther("4"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedCall2);
await b3trMultiSig.connect(otherAccount).confirmTransaction(1);
// Test valid range for executed tx
const executedTxIds = await b3trMultiSig.getTransactionIds(0, 1, false, true);
(0, chai_1.expect)(executedTxIds).to.eql([1n]);
// Test valid range for pending tx
const pendingTxIds = await b3trMultiSig.getTransactionIds(0, 1, true, false);
(0, chai_1.expect)(pendingTxIds).to.eql([0n]);
// Test full list
const allTxIds = await b3trMultiSig.getTransactionIds(0, 2, true, true);
(0, chai_1.expect)(allTxIds).to.eql([0n, 1n]);
});
(0, mocha_1.it)("Reverts if range is invalid", async function () {
const { b3trMultiSig, B3trContract, b3tr, otherAccount, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const encodedCall = B3trContract.interface.encodeFunctionData("mint", [
otherAccount.address,
hardhat_1.ethers.parseEther("5"),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedCall);
// Should revert when `to < from`
await (0, chai_1.expect)(b3trMultiSig.getTransactionIds(2, 1, true, true)).to.be.revertedWith("Invalid range");
// Should revert when `to > matching tx count`
await (0, chai_1.expect)(b3trMultiSig.getTransactionIds(0, 2, true, false)).to.be.revertedWith("Range exceeds results");
});
});
(0, mocha_1.describe)("B3TR Management", function () {
(0, mocha_1.it)("B3TR should be able to grant DEFUALT_ADMIN_ROLE to multisig", async function () {
const { b3trMultiSig, minterAccount, b3tr, otherAccount, owner, otherAccounts, B3trContract } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const DEFAULT_ADMIN_ROLE = await b3tr.DEFAULT_ADMIN_ROLE();
const MINTER_ROLE = await b3tr.MINTER_ROLE();
// Granting the default role to the multisig
await b3tr.connect(owner).grantRole(DEFAULT_ADMIN_ROLE, await b3trMultiSig.getAddress());
(0, chai_1.expect)(await b3tr.hasRole(DEFAULT_ADMIN_ROLE, await b3trMultiSig.getAddress())).to.eql(true);
(0, chai_1.expect)(await b3tr.hasRole(MINTER_ROLE, await b3trMultiSig.getAddress())).to.eql(false);
// MultiSig should be able to grant MINTER_ROLE to itself
const encodedFunctionCall = B3trContract.interface.encodeFunctionData("grantRole", [
MINTER_ROLE,
await b3trMultiSig.getAddress(),
]);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall); /// TxId will be 0
// Confirming the transaction
await b3trMultiSig.connect(otherAccount).confirmTransaction(0);
// Transaction has 2 votes and now should be executed
(0, chai_1.expect)(await b3tr.hasRole(MINTER_ROLE, await b3trMultiSig.getAddress())).to.eql(true);
// Should now be able to mint
const encodedFunctionCall2 = B3trContract.interface.encodeFunctionData("mint", [
otherAccounts[10].address,
hardhat_1.ethers.parseEther("10"),
]);
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(0n);
await b3trMultiSig.connect(owner).submitTransaction(await b3tr.getAddress(), 0, encodedFunctionCall2); /// TxId will be 1
await b3trMultiSig.connect(minterAccount).confirmTransaction(1);
// Got 2 confirmations -> Executed
(0, chai_1.expect)(await b3tr.balanceOf(otherAccounts[10].address)).to.eql(hardhat_1.ethers.parseEther("10"));
});
});
});