@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
890 lines • 106 kB
JavaScript
import { describe, it } from "mocha";
import { ZERO_ADDRESS, bootstrapEmissions, catchRevert, getEventName, getOrDeployContractInstances, moveToCycle, waitForBlock, waitForNextCycle, } from "./helpers";
import { assert, expect } from "chai";
import { ethers, network } from "hardhat";
import { calculateTreasuryAllocation } from "./helpers/allocations";
import { createLocalConfig } from "@repo/config/contracts/envs/local";
import { createTestConfig } from "./helpers/config";
import { generateB3trAllocations } from "./helpers/generateB3trAllocations";
import { getImplementationAddress } from "@openzeppelin/upgrades-core";
import { deployAndUpgrade, deployProxy, upgradeProxy } from "../scripts/helpers";
import b3trAllocationsEmissionsDisaligned from "./fixture/full-allocations-round-14-decay.json";
import b3trAllocationsGMPool from "./fixture/updated-full-allocations-with-gm.json";
describe("Emissions - @shard2", () => {
describe("Contract parameters", () => {
it("Should have correct parameters set on deployment", async () => {
const config = createLocalConfig();
const { emissions, owner, b3tr, minterAccount, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
config,
});
// Destination addresses should be set correctly
expect(await emissions.xAllocations()).to.equal(await xAllocationPool.getAddress());
expect(await emissions.vote2Earn()).to.equal(await voterRewards.getAddress());
expect(await emissions.treasury()).to.equal(await treasury.getAddress());
// Admin should be set correctly
expect(await emissions.hasRole(await emissions.DEFAULT_ADMIN_ROLE(), await owner.getAddress())).to.equal(true);
// Minter should be set correctly
expect(await emissions.hasRole(await emissions.MINTER_ROLE(), await minterAccount.getAddress())).to.equal(true);
// Initial allocation amounts should be set correctly
const initialEmissions = await emissions.initialXAppAllocation();
expect(initialEmissions).to.equal(config.INITIAL_X_ALLOCATION);
// B3TR address should be set correctly
expect(await emissions.b3tr()).to.equal(await b3tr.getAddress());
// Decay settings should be set correctly
expect(await emissions.xAllocationsDecay()).to.equal(config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE);
expect(await emissions.vote2EarnDecay()).to.equal(config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE);
expect(await emissions.xAllocationsDecayPeriod()).to.equal(config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD);
expect(await emissions.vote2EarnDecayPeriod()).to.equal(config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD);
// Treasury percentage should be set correctly
expect(await emissions.treasuryPercentage()).to.equal(config.EMISSIONS_TREASURY_PERCENTAGE);
// GM Percentage of Treasury Pool should be set correctly
expect(await emissions.gmPercentage()).to.equal(config.GM_PERCENTAGE_OF_TREASURY);
});
it("Should revert if Treasury is set to zero address in initilisation", async () => {
const config = createLocalConfig();
const { owner, b3tr, minterAccount, xAllocationPool, voterRewards } = await getOrDeployContractInstances({
forceDeploy: true,
config,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
ZERO_ADDRESS,
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should revert if XAllocations is set to zero address in initilisation", async () => {
const config = createLocalConfig();
const { owner, b3tr, minterAccount, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: false,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
ZERO_ADDRESS,
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should revert if vote2Earn is set to zero address in initilisation", async () => {
const config = createLocalConfig();
const { owner, b3tr, minterAccount, xAllocationPool, treasury } = await getOrDeployContractInstances({
forceDeploy: false,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
ZERO_ADDRESS,
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should revert if admin is set to zero address in initilisation", async () => {
const config = createLocalConfig();
const { owner, b3tr, minterAccount, xAllocationPool, treasury, voterRewards } = await getOrDeployContractInstances({
forceDeploy: false,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: ZERO_ADDRESS,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should revert if Treasury is set to zero address in initilisation", async () => {
const config = createLocalConfig();
const { owner, b3tr, minterAccount, xAllocationPool, voterRewards } = await getOrDeployContractInstances({
forceDeploy: true,
config,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
ZERO_ADDRESS,
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should be able to change the X allocations address", async () => {
const { emissions, otherAccounts, owner } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(emissions.connect(owner).setXallocationsAddress(ZERO_ADDRESS)).to.be.reverted;
const tx = await emissions.connect(owner).setXallocationsAddress(otherAccounts[3].address);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("XAllocationsAddressUpdated");
expect(await emissions.xAllocations()).to.equal(otherAccounts[3].address);
});
it("Should be able to change the Vote 2 Earn address", async () => {
const { emissions, otherAccounts, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(emissions.connect(owner).setVote2EarnAddress(ZERO_ADDRESS)).to.be.reverted;
await expect(emissions.connect(otherAccount).setVote2EarnAddress(otherAccounts[3].address)).to.be.reverted; // Not admin
const tx = await emissions.connect(owner).setVote2EarnAddress(otherAccounts[3].address);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("Vote2EarnAddressUpdated");
expect(await emissions.vote2Earn()).to.equal(otherAccounts[3].address);
});
it("Should be able to change the Treasury address", async () => {
const { emissions, otherAccounts, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(emissions.connect(owner).setTreasuryAddress(ZERO_ADDRESS)).to.be.reverted;
await expect(emissions.connect(otherAccount).setTreasuryAddress(otherAccounts[3].address)).to.be.reverted; // Not admin
const tx = await emissions.connect(owner).setTreasuryAddress(otherAccounts[3].address);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("TreasuryAddressUpdated");
expect(await emissions.treasury()).to.equal(otherAccounts[3].address);
});
it("Should not be able to change the X allocations address if not admin", async () => {
const { emissions, otherAccounts } = await getOrDeployContractInstances({
forceDeploy: true,
});
await catchRevert(emissions.connect(otherAccounts[0]).setXallocationsAddress(otherAccounts[3].address));
});
it("Treasury percentage should be between 0 and 10000", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await catchRevert(emissions.connect(owner).setTreasuryPercentage(10001));
try {
await emissions.connect(owner).setTreasuryPercentage(-1);
assert.fail("Should revert");
}
catch (e) {
/* empty */
}
await emissions.connect(owner).setTreasuryPercentage(10000);
await emissions.connect(owner).setTreasuryPercentage(0);
const tx = await emissions.connect(owner).setTreasuryPercentage(550);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("TreasuryPercentageUpdated");
await expect(emissions.connect(otherAccount).setTreasuryPercentage(55)).to.be.reverted; // Not admin
});
it("GM Percentage of Treasury Pool should be between 0 and 10000", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await catchRevert(emissions.connect(owner).setGmPercentage(10001));
try {
await emissions.connect(owner).setGmPercentage(-1);
assert.fail("Should revert");
}
catch (e) {
/* empty */
}
await emissions.connect(owner).setGmPercentage(10000);
await emissions.connect(owner).setGmPercentage(0);
const tx = await emissions.connect(owner).setGmPercentage(550);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("GMPercentageUpdated");
await expect(emissions.connect(otherAccount).setGmPercentage(55)).to.be.reverted; // Not admin
});
it("MaxVote2EarnDecay percentage should be between 0 and 100", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await catchRevert(emissions.connect(owner).setMaxVote2EarnDecay(101));
try {
await emissions.connect(owner).setMaxVote2EarnDecay(-1);
assert.fail("Should revert");
}
catch (e) {
/* empty */
}
await emissions.connect(owner).setMaxVote2EarnDecay(100);
await emissions.connect(owner).setMaxVote2EarnDecay(0);
const tx = await emissions.connect(owner).setMaxVote2EarnDecay(55);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("MaxVote2EarnDecayUpdated");
await expect(emissions.connect(otherAccount).setMaxVote2EarnDecay(55)).to.be.reverted; // Not admin
});
it("Vote2EarnDecay percentage should be between 0 and 100", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await catchRevert(emissions.connect(owner).setVote2EarnDecay(101));
try {
await emissions.connect(owner).setVote2EarnDecay(-1);
assert.fail("Should revert");
}
catch (e) {
/* empty */
}
await emissions.connect(owner).setVote2EarnDecay(100);
await emissions.connect(owner).setVote2EarnDecay(0);
const tx = await emissions.connect(owner).setVote2EarnDecay(55);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("Vote2EarnDecayUpdated");
await expect(emissions.connect(otherAccount).setVote2EarnDecay(55)).to.be.reverted; // Not admin
});
it("XAllocationsDecay percentage should be between 0 and 100", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await catchRevert(emissions.connect(owner).setXAllocationsDecay(101));
try {
await emissions.connect(owner).setXAllocationsDecay(-1);
assert.fail("Should revert");
}
catch (e) {
/* empty */
}
await emissions.connect(owner).setXAllocationsDecay(100);
await emissions.connect(owner).setXAllocationsDecay(0);
const tx = await emissions.connect(owner).setXAllocationsDecay(55);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("XAllocationsDecayUpdated");
await expect(emissions.connect(otherAccount).setXAllocationsDecay(55)).to.be.reverted; // Not admin
});
it("Should return correct x allocations governor address", async () => {
const { emissions, xAllocationVoting } = await getOrDeployContractInstances({
forceDeploy: true,
});
expect(await emissions.xAllocationsGovernor()).to.equal(await xAllocationVoting.getAddress());
});
it("Should return max vote 2 earn decay percentage", async () => {
const { emissions } = await getOrDeployContractInstances({
forceDeploy: true,
});
expect(await emissions.maxVote2EarnDecay()).to.equal(80);
});
it("Should return scaling factor", async () => {
const { emissions } = await getOrDeployContractInstances({
forceDeploy: true,
});
expect(await emissions.SCALING_FACTOR()).to.equal(10 ** 6);
});
it("Should be able to change cycle duration", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
const tx = await emissions.connect(owner).setCycleDuration(1000);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("EmissionCycleDurationUpdated");
expect(await emissions.cycleDuration()).to.equal(1000);
await expect(emissions.connect(otherAccount).setCycleDuration(1000)).to.be.reverted; // Not admin
await expect(emissions.connect(owner).setCycleDuration(0)).to.be.reverted; // At least 1 block
});
it("Should revert if cycle period is set less than voting period", async () => {
const { emissions, owner } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(emissions.connect(owner).setCycleDuration(10)).to.be.reverted;
});
it("Should be able to change x allocations decay period", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
const tx = await emissions.connect(owner).setXAllocationsDecayPeriod(1000);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("XAllocationsDecayPeriodUpdated");
expect(await emissions.xAllocationsDecayPeriod()).to.equal(1000);
await expect(emissions.connect(otherAccount).setXAllocationsDecayPeriod(1000)).to.be.reverted; // Not admin
await expect(emissions.connect(owner).setXAllocationsDecayPeriod(0)).to.be.reverted; // At least 1 block
});
it("Should be able to change vote 2 earn decay period", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
const tx = await emissions.connect(owner).setVote2EarnDecayPeriod(1000);
const receipt = await tx.wait();
const eventName = getEventName(receipt, emissions);
expect(eventName).to.equal("Vote2EarnDecayPeriodUpdated");
expect(await emissions.vote2EarnDecayPeriod()).to.equal(1000);
await expect(emissions.connect(otherAccount).setVote2EarnDecayPeriod(1000)).to.be.reverted; // Not admin
await expect(emissions.connect(owner).setVote2EarnDecayPeriod(0)).to.be.reverted; // At least 1 block
});
it("Should be able to change x allocations voting governor", async () => {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(emissions.connect(otherAccount).setXAllocationsGovernorAddress(otherAccount.address)).to.be.reverted; // Not admin
await expect(emissions.connect(owner).setXAllocationsGovernorAddress(ZERO_ADDRESS)).to.be.reverted; // Can't be zero address
});
// it("getScaledDecayPercentage: decay percentage should be between 0 and 99", async () => {
// const { emissions, owner } = await getOrDeployContractInstances({
// forceDeploy: true,
// })
// await expect(emissions.connect(owner).getScaledDecayPercentage(101)).to.be.reverted
// try {
// await emissions.connect(owner).getScaledDecayPercentage(-1)
// assert.fail("Should revert")
// } catch (e) {
// /* empty */
// }
// await expect(emissions.connect(owner).getScaledDecayPercentage(100)).to.be.reverted
// await expect(emissions.connect(owner).getScaledDecayPercentage(0)).not.to.be.reverted
// await expect(emissions.connect(owner).getScaledDecayPercentage(55)).not.to.be.reverted
// })
});
describe("Contract upgradeablity", () => {
it("Admin should be able to upgrade the contract", async function () {
const { emissions, owner } = await getOrDeployContractInstances({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await ethers.getContractFactory("Emissions");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await getImplementationAddress(ethers.provider, await emissions.getAddress());
const UPGRADER_ROLE = await emissions.UPGRADER_ROLE();
expect(await emissions.hasRole(UPGRADER_ROLE, owner.address)).to.eql(true);
await expect(emissions.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be
.reverted;
const newImplAddress = await getImplementationAddress(ethers.provider, await emissions.getAddress());
expect(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase());
expect(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase());
});
it("Only admin should be able to upgrade the contract", async function () {
const { emissions, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await ethers.getContractFactory("Emissions");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await getImplementationAddress(ethers.provider, await emissions.getAddress());
const UPGRADER_ROLE = await emissions.UPGRADER_ROLE();
expect(await emissions.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false);
await expect(emissions.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.be
.reverted;
const newImplAddress = await getImplementationAddress(ethers.provider, await emissions.getAddress());
expect(newImplAddress.toUpperCase()).to.eql(currentImplAddress.toUpperCase());
expect(newImplAddress.toUpperCase()).to.not.eql((await implementation.getAddress()).toUpperCase());
});
it("Admin can change UPGRADER_ROLE", async function () {
const { emissions, owner, otherAccount } = await getOrDeployContractInstances({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await ethers.getContractFactory("Emissions");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await getImplementationAddress(ethers.provider, await emissions.getAddress());
const UPGRADER_ROLE = await emissions.UPGRADER_ROLE();
expect(await emissions.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false);
await expect(emissions.connect(owner).grantRole(UPGRADER_ROLE, otherAccount.address)).to.not.be.reverted;
await expect(emissions.connect(owner).revokeRole(UPGRADER_ROLE, owner.address)).to.not.be.reverted;
await expect(emissions.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be
.reverted;
const newImplAddress = await getImplementationAddress(ethers.provider, await emissions.getAddress());
expect(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase());
expect(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase());
});
it("Should not be able to call initializer after deployment", async () => {
const config = createLocalConfig();
const { emissions, owner, minterAccount, xAllocationPool, voterRewards, treasury, b3tr } = await getOrDeployContractInstances({
forceDeploy: true,
config,
});
expect(emissions.initialize({
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: 0,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
})).to.be.reverted;
expect(emissions.initializeV3(config.GM_PERCENTAGE_OF_TREASURY)).to.be.reverted;
});
it("Should not be able to deploy with initial X Allocations zero", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: 0,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to deploy with cycle duration less or equal to 0", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: 0,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to deploy with treasury percentage not between 1 and 10000", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: 0,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to upgrade with GM percentage not between 1 and 10000", async () => {
const config = createLocalConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
const emissions = (await deployAndUpgrade(["EmissionsV1", "EmissionsV2"], [
[
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
],
[],
], {
versions: [undefined, 2],
logOutput: false,
}));
await expect(upgradeProxy("EmissionsV2", "Emissions", await emissions.getAddress(), [10001], {
version: 3,
})).to.be.reverted;
});
it("Should not be able to deploy with x allocations decay percentage not between 1 and 100", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
101,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to deploy with vote2Earn decay percentage not between 1 and 100", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
0,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to deploy with xAllocations decay delay period less than 1", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
0,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to deploy with vote2Earn decay delay period less than 1", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
0,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should not be able to deploy with max vote2Earn decay percentage not between 1 and 100", async () => {
const config = createTestConfig();
const { owner, minterAccount, b3tr, xAllocationPool, voterRewards, treasury } = await getOrDeployContractInstances({
forceDeploy: true,
});
await expect(deployProxy("Emissions", [
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: 0,
migrationAmount: config.MIGRATION_AMOUNT,
},
])).to.be.reverted;
});
it("Should return correct version of the contract", async () => {
const { emissions } = await getOrDeployContractInstances({
forceDeploy: true,
});
expect(await emissions.version()).to.equal("3");
});
it("Should not have any state conflicts after upgrading to V3", async function () {
const config = createTestConfig();
const { b3tr, minterAccount, owner, xAllocationPool, voterRewards, treasury, xAllocationVoting } = await getOrDeployContractInstances({
forceDeploy: true,
config,
});
let emissions = (await deployAndUpgrade(["EmissionsV1", "EmissionsV2"], [
[
{
minter: minterAccount.address,
admin: owner.address,
upgrader: owner.address,
contractsAddressManager: owner.address,
decaySettingsManager: owner.address,
b3trAddress: await b3tr.getAddress(),
destinations: [
await xAllocationPool.getAddress(),
await voterRewards.getAddress(),
await treasury.getAddress(),
config.MIGRATION_ADDRESS,
],
initialXAppAllocation: config.INITIAL_X_ALLOCATION,
cycleDuration: config.EMISSIONS_CYCLE_DURATION,
decaySettings: [
config.EMISSIONS_X_ALLOCATION_DECAY_PERCENTAGE,
config.EMISSIONS_VOTE_2_EARN_DECAY_PERCENTAGE,
config.EMISSIONS_X_ALLOCATION_DECAY_PERIOD,
config.EMISSIONS_VOTE_2_EARN_ALLOCATION_DECAY_PERIOD,
],
treasuryPercentage: config.EMISSIONS_TREASURY_PERCENTAGE,
maxVote2EarnDecay: config.EMISSIONS_MAX_VOTE_2_EARN_DECAY_PERCENTAGE,
migrationAmount: config.MIGRATION_AMOUNT,
},
],
[],
], {
versions: [undefined, 2],
logOutput: false,
}));
await b3tr.connect(owner).grantRole(await b3tr.MINTER_ROLE(), await emissions.getAddress());
await emissions.setXAllocationsGovernorAddress(await xAllocationVoting.getAddress());
await xAllocationVoting
.connect(owner)
.grantRole(await xAllocationVoting.ROUND_STARTER_ROLE(), await emissions.getAddress());
// Bootstrap emissions
await emissions.connect(minterAccount).bootstrap();
// Start emissions
await emissions.connect(minterAccount).start();
// Variables to hold calculated amounts for assertions
let xAllocationsAmount = BigInt(0);
let vote2EarnAmount = BigInt(0);
let treasuryAmount = BigInt(0);
let gmAmount = BigInt(0);
let _totalEmissions = config.MIGRATION_AMOUNT;
const cap = await b3tr.cap();
const b3trAllocations = await generateB3trAllocations(config, "./test/fixture/full-allocations.json");
// const b3trAllocations = await generateB3trAllocations(