import {expect} from "chai"; import hre from "hardhat"; import {getBytes, solidityPackedKeccak256, ZeroAddress} from "ethers"; import {loadFixture} from "@nomicfoundation/hardhat-toolbox/network-helpers"; import {expandDecimals, getBlockTime, increaseTime, mineBlock} from "./shared/utilities"; const secondsPerYear = 365 * 24 * 60 * 60; describe("Vester", function () { async function deployOneContract() { // Contracts are deployed using the first signer/account by default const [owner, otherAccount] = await hre.ethers.getSigners(); const verifier = owner.address; const chainId = hre.network.config.chainId; const Cec = await hre.ethers.getContractFactory("MintableBaseToken"); const cec = await Cec.deploy("test cec", "cec"); const esCec = await Cec.deploy("test esCec", "esCec"); await cec.setMinter(owner.address, true); await esCec.setMinter(owner.address, true); const RewardDistributor = await hre.ethers.getContractFactory("RewardDistributor"); const RewardTracker = await hre.ethers.getContractFactory("RewardTracker"); const stakedCecTracker = await RewardTracker.deploy("Staked CEC", "sCEC"); const stakedCecDistributor = await RewardDistributor.deploy(esCec.target, stakedCecTracker.target); await stakedCecTracker.initialize([cec.target, esCec.target], stakedCecDistributor.target); await stakedCecDistributor.updateLastDistributionTime(); const Vester = await hre.ethers.getContractFactory("Vester"); const vester = await Vester.deploy( "Vested CEC", "veCEC", secondsPerYear, esCec.target, ZeroAddress, cec.target, stakedCecTracker.target, true ); await cec.mint(owner.address, expandDecimals(100000, 18)); await esCec.mint(owner.address, expandDecimals(100000, 18)); return {owner, otherAccount, chainId, cec, esCec, vester, stakedCecTracker}; } describe("Deployment", function () { it("should deploy Vester", async function () { const {cec, esCec, vester, stakedCecTracker} = await loadFixture(deployOneContract); expect(await vester.name()).eq("Vested CEC"); expect(await vester.symbol()).eq("veCEC"); expect(await vester.vestingDuration()).eq(secondsPerYear); expect(await vester.esToken()).eq(esCec.target); expect(await vester.pairToken()).eq(ZeroAddress); expect(await vester.claimableToken()).eq(cec.target); expect(await vester.rewardTracker()).eq(stakedCecTracker.target); expect(await vester.hasPairToken()).eq(false); expect(await vester.hasRewardTracker()).eq(true); }); it("setCumulativeRewardDeductions", async () => { const {owner, otherAccount, cec, esCec, vester} = await loadFixture(deployOneContract); await expect(vester.setCumulativeRewardDeductions(owner.address, 200)).to.be.revertedWith("Vester: forbidden"); await vester.setHandler(owner.address, true); expect(await vester.cumulativeRewardDeductions(owner.address)).eq(0); await vester.setCumulativeRewardDeductions(owner.address, 200); expect(await vester.cumulativeRewardDeductions(owner.address)).eq(200); }); it("setBonusRewards", async () => { const {owner, otherAccount, cec, esCec, vester} = await loadFixture(deployOneContract); await expect(vester.setBonusRewards(owner.address, 200)).to.be.revertedWith("Vester: forbidden"); await vester.setHandler(owner.address, true); expect(await vester.bonusRewards(owner.address)).eq(0); await vester.setBonusRewards(owner.address, 200); expect(await vester.bonusRewards(owner.address)).eq(200); }); it("deposit, claim, withdraw", async () => { const {owner, otherAccount, cec, esCec, vester, stakedCecTracker} = await loadFixture(deployOneContract); const wallet = owner; const user0 = otherAccount; const provider = wallet.provider; await vester.setHandler(wallet.address, true); await esCec.setMinter(vester.target, true); // @ts-ignore await expect(vester.connect(user0).deposit(0)).to.be.revertedWith("Vester: invalid _amount"); // @ts-ignore await expect(vester.connect(user0).deposit(expandDecimals(1000, 18))).to.be.revertedWith( "ERC20: insufficient allowance", ); // @ts-ignore await esCec.connect(user0).approve(vester.target, expandDecimals(1000, 18)); // @ts-ignore await expect(vester.connect(user0).deposit(expandDecimals(1000, 18))).to.be.revertedWith( "ERC20: transfer amount exceeds balance", ); expect(await vester.balanceOf(user0.address)).eq(0); expect(await vester.getTotalVested(user0.address)).eq(0); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(0); expect(await vester.claimedAmounts(user0.address)).eq(0); expect(await vester.claimable(user0.address)).eq(0); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(0); await esCec.mint(user0.address, expandDecimals(1000, 18)); await vester.setBonusRewards(user0.address, expandDecimals(10_000, 18)); // @ts-ignore await expect(vester.connect(user0).deposit(expandDecimals(1000, 18))).to.be.revertedWith( "Vester: insufficient cec balance", ); await stakedCecTracker.setHandler(user0.address, true); await cec.setMinter(wallet.address, true); await cec.mint(user0.address, expandDecimals(1000, 18)); // @ts-ignore await cec.connect(user0).approve(stakedCecTracker.target, expandDecimals(1000, 18)); // @ts-ignore await stakedCecTracker.connect(user0).stakeForAccount(user0.address, user0.address, cec.target, expandDecimals(1000, 18)); // @ts-ignore await vester.connect(user0).deposit(expandDecimals(1000, 18)); let blockTime = await getBlockTime(provider); expect(await vester.balanceOf(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.getTotalVested(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(0); expect(await vester.claimedAmounts(user0.address)).eq(0); expect(await vester.claimable(user0.address)).eq(0); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(blockTime); await increaseTime(provider, 24 * 60 * 60); await mineBlock(provider); expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).eq(0); expect(await vester.balanceOf(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.getTotalVested(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(0); expect(await vester.claimedAmounts(user0.address)).eq(0); expect(await vester.claimable(user0.address)).gt("2730000000000000000"); // 1000 / 365 => ~2.739 expect(await vester.claimable(user0.address)).lt("2750000000000000000"); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(blockTime); // @ts-ignore await expect(vester.connect(user0).claim()).to.be.revertedWith("ERC20: transfer amount exceeds balance"); await cec.mint(vester.target, expandDecimals(2000, 18)); // @ts-ignore await vester.connect(user0).claim(); blockTime = await getBlockTime(provider); expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).gt("2730000000000000000"); expect(await cec.balanceOf(user0.address)).lt("2750000000000000000"); let cecAmount = await cec.balanceOf(user0.address); expect(await vester.balanceOf(user0.address)).eq(expandDecimals(1000, 18) - cecAmount); expect(await vester.getTotalVested(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(cecAmount); expect(await vester.claimedAmounts(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).eq(0); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(blockTime); await increaseTime(provider, 48 * 60 * 60); await mineBlock(provider); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(cecAmount); expect(await vester.claimedAmounts(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).gt("5478000000000000000"); // 1000 / 365 * 2 => ~5.479 expect(await vester.claimable(user0.address)).lt("5480000000000000000"); await increaseTime(provider, parseInt(365 / 2 - 1 + "") * 24 * 60 * 60); await mineBlock(provider); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(cecAmount); expect(await vester.claimedAmounts(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).gt(expandDecimals(500, 18)); // 1000 / 2 => 500 expect(await vester.claimable(user0.address)).lt(expandDecimals(502, 18)); // @ts-ignore await vester.connect(user0).claim(); blockTime = await getBlockTime(provider); expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).gt(expandDecimals(503, 18)); expect(await cec.balanceOf(user0.address)).lt(expandDecimals(505, 18)); cecAmount = await cec.balanceOf(user0.address); expect(await vester.balanceOf(user0.address)).eq(expandDecimals(1000, 18) - cecAmount); expect(await vester.getTotalVested(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(cecAmount); expect(await vester.claimedAmounts(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).eq(0); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(blockTime); await increaseTime(provider, 24 * 60 * 60); await mineBlock(provider); // vesting rate should be the same even after claiming expect(await vester.claimable(user0.address)).gt("2730000000000000000"); // 1000 / 365 => ~2.739 expect(await vester.claimable(user0.address)).lt("2750000000000000000"); await esCec.mint(user0.address, expandDecimals(500, 18)); // @ts-ignore await esCec.connect(user0).approve(vester.target, expandDecimals(500, 18)); // @ts-ignore await vester.connect(user0).deposit(expandDecimals(500, 18)); await increaseTime(provider, 24 * 60 * 60); await mineBlock(provider); expect(await vester.claimable(user0.address)).gt("6840000000000000000"); // 1000 / 365 + 1500 / 365 => 6.849 expect(await vester.claimable(user0.address)).lt("6860000000000000000"); expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).eq(cecAmount); // @ts-ignore await vester.connect(user0).withdraw(); expect(await esCec.balanceOf(user0.address)).gt(expandDecimals(989, 18)); expect(await esCec.balanceOf(user0.address)).lt(expandDecimals(990, 18)); expect(await cec.balanceOf(user0.address)).gt(expandDecimals(510, 18)); expect(await cec.balanceOf(user0.address)).lt(expandDecimals(512, 18)); expect(await vester.balanceOf(user0.address)).eq(0); expect(await vester.getTotalVested(user0.address)).eq(0); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(0); expect(await vester.claimedAmounts(user0.address)).eq(0); expect(await vester.claimable(user0.address)).eq(0); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(0); // @ts-ignore await esCec.connect(user0).approve(vester.target, expandDecimals(1000, 18)); await esCec.mint(user0.address, expandDecimals(1000, 18)); // @ts-ignore await vester.connect(user0).deposit(expandDecimals(1000, 18)); blockTime = await getBlockTime(provider); await increaseTime(provider, 24 * 60 * 60); await mineBlock(provider); expect(await vester.balanceOf(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.getTotalVested(user0.address)).eq(expandDecimals(1000, 18)); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(0); expect(await vester.claimedAmounts(user0.address)).eq(0); expect(await vester.claimable(user0.address)).gt("2730000000000000000"); // 1000 / 365 => ~2.739 expect(await vester.claimable(user0.address)).lt("2750000000000000000"); expect(await vester.pairAmounts(user0.address)).eq(0); expect(await vester.lastVestingTimes(user0.address)).eq(blockTime); // @ts-ignore await vester.connect(user0).claim(); }); }); });