UNPKG

@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
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(