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 = BigInt(365 * 24 * 60 * 60); const rewardPerSecond = BigInt(1.5 * 10 ** 18) / secondsPerYear ; 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("EsToken"); 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 RewardTracker = await hre.ethers.getContractFactory("RewardTracker"); const stakedCecTracker = await RewardTracker.deploy(esCec.target, cec.target, rewardPerSecond, 18); const Vester = await hre.ethers.getContractFactory("Vester"); const vester = await Vester.deploy( "Vested CEC", "veCEC", secondsPerYear, esCec.target, cec.target, stakedCecTracker.target, ZeroAddress, 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.claimableToken()).eq(cec.target); expect(await vester.rewardTrackerCEC()).eq(stakedCecTracker.target); expect(await vester.hasRewardTrackerCEC()).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", ); await esCec.setHandler(vester.target, true); await esCec.setInPrivateTransferMode(true); await esCec.setHandler(wallet.address, true); // @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.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.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); let lastDepositTime = await vester.lastDepositTimes(user0.address); let lastVestingTime = BigInt(await vester.lastVestingTimes(user0.address)); expect(lastDepositTime).eq(lastVestingTime); let now = await getBlockTime(provider); let total = BigInt(1000) * BigInt(10 ** 18) * (BigInt(now) - lastDepositTime) / BigInt(secondsPerYear); expect(await vester.claimable(user0.address)).eq(total); 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); lastDepositTime = await vester.lastDepositTimes(user0.address); now = await getBlockTime(provider); total = BigInt(1000) * (now - lastDepositTime) * BigInt(10 ** 18) / BigInt(secondsPerYear); expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).eq(total); let cecAmount = await cec.balanceOf(user0.address); let claimed = cecAmount; expect(await vester.balanceOf(user0.address)).eq(expandDecimals(1000, 18) - cecAmount); expect(cecAmount).eq(total); 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.lastVestingTimes(user0.address)).eq(blockTime); await increaseTime(provider, 48 * 60 * 60); await mineBlock(provider); lastDepositTime = await vester.lastDepositTimes(user0.address); let pre = now now = await getBlockTime(provider); let _claimable = BigInt(1000) * (now - pre) * BigInt(10 ** 18) / BigInt(secondsPerYear); lastVestingTime = BigInt(await vester.lastVestingTimes(user0.address)); expect(pre).eq(lastVestingTime); expect(await vester.claimedAmounts(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).eq(total - claimed + _claimable); await increaseTime(provider, parseInt(365 / 2 - 1 + "") * 24 * 60 * 60); await mineBlock(provider); let tmpNow = await getBlockTime(provider); _claimable = BigInt(1000) * (tmpNow - pre) * BigInt(10 ** 18) / BigInt(secondsPerYear); expect(await vester.cumulativeClaimAmounts(user0.address)).eq(cecAmount); expect(await vester.claimedAmounts(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).eq(total - cecAmount + _claimable); // @ts-ignore await vester.connect(user0).claim(); blockTime = await getBlockTime(provider); now = await getBlockTime(provider); _claimable = BigInt(1000) * (now - pre) * BigInt(10 ** 18) / BigInt(secondsPerYear); total += _claimable; pre = now; cecAmount += (total - cecAmount) expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).eq(cecAmount); 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.lastVestingTimes(user0.address)).eq(blockTime); await increaseTime(provider, 24 * 60 * 60); await mineBlock(provider); // vesting rate should be the same even after claiming tmpNow = await getBlockTime(provider); _claimable = BigInt(1000) * (tmpNow - pre) * BigInt(10 ** 18) / BigInt(secondsPerYear); expect(await vester.claimable(user0.address)).eq(total - cecAmount + _claimable); // 1000 / 365 => ~2.739 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)); tmpNow = await getBlockTime(provider); _claimable = BigInt(1000) * (tmpNow - pre) * BigInt(10 ** 18) / BigInt(secondsPerYear); let _balance1 = await vester.balanceOf(user0.address); let _balance2 = (BigInt(1000) * BigInt(10 ** 18) - cecAmount) + BigInt(500) * BigInt(10 ** 18) - _claimable expect(_balance1).eq(_balance2); let vestTotal = await vester.vestingTotal(user0.address); expect(vestTotal).eq(_balance2); total += _claimable; pre = tmpNow; lastVestingTime = BigInt(await vester.lastVestingTimes(user0.address)); expect(pre).eq(lastVestingTime); await increaseTime(provider, 24 * 60 * 60); await mineBlock(provider); tmpNow = await getBlockTime(provider); _claimable = BigInt(_balance2) * (tmpNow - pre) / BigInt(secondsPerYear); expect(await esCec.balanceOf(user0.address)).eq(0); expect(await cec.balanceOf(user0.address)).eq(cecAmount); expect(await vester.claimable(user0.address)).eq(total - cecAmount + _claimable); // @ts-ignore await vester.connect(user0).withdraw(); tmpNow = await getBlockTime(provider); _claimable = BigInt(_balance2) * (tmpNow - pre) / BigInt(secondsPerYear); cecAmount += (total - cecAmount + _claimable); expect(await cec.balanceOf(user0.address)).eq(cecAmount); 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.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.lastVestingTimes(user0.address)).eq(blockTime); // @ts-ignore await vester.connect(user0).claim(); }); }); });