From e0f004ea5d02206d972b909eb4c0abaf5da99891 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Wed, 28 Apr 2021 23:13:22 +0300 Subject: [PATCH] Cover DioneStaking by 100% with unit tests --- eth-contracts/.solcover.js | 2 +- eth-contracts/test/DioneStaking.ts | 219 +++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 eth-contracts/test/DioneStaking.ts diff --git a/eth-contracts/.solcover.js b/eth-contracts/.solcover.js index 585f346..b74c098 100644 --- a/eth-contracts/.solcover.js +++ b/eth-contracts/.solcover.js @@ -1,3 +1,3 @@ module.exports = { - skipFiles: ['Timelock.sol', 'Mediator.sol', 'vendor/Ownable.sol'] + skipFiles: ['Timelock.sol', 'Mediator.sol', 'vendor/Ownable.sol', 'DioneToken.sol'] }; \ No newline at end of file diff --git a/eth-contracts/test/DioneStaking.ts b/eth-contracts/test/DioneStaking.ts new file mode 100644 index 0000000..a46dd84 --- /dev/null +++ b/eth-contracts/test/DioneStaking.ts @@ -0,0 +1,219 @@ +import { BigNumber } from "@ethersproject/bignumber"; +import { Contract } from "@ethersproject/contracts"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; + +describe("DioneStaking", () => { + let signers: SignerWithAddress[]; + let allSigners: SignerWithAddress[]; + let dioneToken: Contract; + let dioneStaking: Contract; + const rewardAmount = 100; + const minStakeAmount = 500; + + beforeEach(async () => { + allSigners = await ethers.getSigners(); + signers = allSigners.slice(0, 4); + + const DioneToken = await ethers.getContractFactory("DioneToken"); + const DioneStaking = await ethers.getContractFactory("DioneStaking"); + + dioneToken = await DioneToken.deploy(); + await dioneToken.deployed(); + + dioneStaking = await DioneStaking.deploy( + dioneToken.address, + toWei(rewardAmount), + 0, + toWei(minStakeAmount) + ); + await dioneStaking.deployed(); + + for (var s of signers) { + await dioneToken.mint(s.address, toWei(5000)); + } + + await dioneToken.transferOwnership(dioneStaking.address); + }); + + it("should successfully stake some of tokens", async () => { + for (var s of signers) { + await dioneToken.connect(s).approve(dioneStaking.address, toWei(666)); + await expect(dioneStaking.connect(s).stake(toWei(666))) + .to.emit(dioneStaking, "Stake") + .withArgs(s.address, toWei(666)); + expect(await dioneStaking.minerStake(s.address)) + .to.be.equal(toWei(666)); + expect(await dioneStaking.isMiner(s.address)) + .to.be.equal(true); + } + }); + + it("check miner balance without stake", async () => { + expect(await dioneStaking.minerStake(signers[0].address)) + .to.be.equal(toWei(0)); + expect(await dioneStaking.isMiner(signers[0].address)) + .to.be.equal(false); + }); + + it("should mine reward successfully", async () => { + await dioneStaking.setOracleContractAddress(signers[0].address) + + await dioneToken.approve(dioneStaking.address, toWei(500)); + await dioneStaking.stake(toWei(500)); + const res = await dioneStaking.mine(signers[0].address); + await expect(res) + .to.emit(dioneStaking, "Mine") + .withArgs(signers[0].address, res.blockNumber); + + expect(await dioneToken.balanceOf(signers[0].address)) + .to.be.equal(toWei(4600)); + }); + + it("should mine and stake reward successfully", async () => { + await dioneStaking.setOracleContractAddress(signers[0].address); + + await dioneToken.approve(dioneStaking.address, toWei(500)); + await dioneStaking.stake(toWei(500)); + + await dioneStaking.mineAndStake(signers[0].address); + expect(await dioneStaking.minerStake(signers[0].address)) + .to.be.equal(toWei(600)); + }); + + it("should fail if caller of mine methods isn't desired address", async () => { + await expect(dioneStaking.mine(signers[0].address)) + .to.be.revertedWith("not oracle contract"); + await expect(dioneStaking.mineAndStake(signers[0].address)) + .to.be.revertedWith("not oracle contract"); + }); + + it("check change of total stake after staking", async () => { + const expectedTotalStake = toWei(666*4); + + for (var s of signers) { + await dioneToken.connect(s).approve(dioneStaking.address, toWei(666)); + await dioneStaking.connect(s).stake(toWei(666)); + } + + expect(await dioneStaking.totalStake()) + .to.be.equal(expectedTotalStake); + }); + + it("should withdraw funds successfully", async () => { + for (var s of signers) { + await dioneToken.connect(s).approve(dioneStaking.address, toWei(500)); + await dioneStaking.connect(s).stake(toWei(500)); + } + + for (var s of signers) { + await expect(dioneStaking.connect(s).withdraw(toWei(500))) + .to.emit(dioneStaking, "Withdraw") + .withArgs(s.address, toWei(500)); + + expect(await dioneToken.balanceOf(s.address)) + .to.be.equal(toWei(5000)); + } + }); + + it("should fail withdrawing invalid values", async () => { + await dioneToken.approve(dioneStaking.address, toWei(500)); + await dioneStaking.stake(toWei(500)); + + await expect(dioneStaking.withdraw(toWei(666))) + .to.be.revertedWith("withdraw: not enough tokens"); + + await expect(dioneStaking.withdraw(0)) + .to.be.revertedWith("cannot withdraw zero"); + }); + + it("check changing miner reward", async () => { + await expect(dioneStaking.setMinerReward(0)) + .to.be.revertedWith("reward must not be zero"); + + await expect(dioneStaking.setMinerReward(toWei(5))) + .to.emit(dioneStaking, "RewardChanged") + .withArgs(toWei(100), toWei(5)); + + expect(await dioneStaking.minerReward()) + .to.be.equal(toWei(5)); + + await expect(dioneStaking.connect(signers[1]).setMinerReward(toWei(5))) + .to.be.revertedWith("Ownable: caller is not the owner"); + }); + + it("check changing minimum stake", async () => { + await expect(dioneStaking.setMinimumStake(0)) + .to.be.revertedWith("minimum stake must not be zero"); + + await expect(dioneStaking.setMinimumStake(toWei(5))) + .to.emit(dioneStaking, "MinimumStakeChanged") + .withArgs(toWei(500), toWei(5)); + + expect(await dioneStaking.minimumStake()) + .to.be.equal(toWei(5)); + + await expect(dioneStaking.connect(signers[1]).setMinimumStake(toWei(5))) + .to.be.revertedWith("Ownable: caller is not the owner"); + }); + + it("should fail when staking invalid values", async () => { + await expect(dioneStaking.stake(0)) + .to.be.revertedWith("cannot stake zero"); + + await expect(dioneStaking.stake(toWei(400))) + .to.be.revertedWith("actual stake amount is less than minimum stake amount"); + }); + + it("should fail when calling slashMiner with invalid values (initial checks)", async () => { + const zeroAddress = "0x0000000000000000000000000000000000000000"; + + await expect(dioneStaking.slashMiner(zeroAddress, [signers[0].address])) + .to.be.revertedWith("caller is not the dispute contract"); + + await dioneStaking.setDisputeContractAddress(signers[0].address); + + await expect(dioneStaking.slashMiner(zeroAddress, [signers[0].address])) + .to.be.revertedWith("slashing address must not be zero"); + + await expect(dioneStaking.slashMiner(signers[1].address, [signers[0].address])) + .to.be.revertedWith("slashing address isn't dione miner"); + + for (var s of signers) { + await dioneToken.connect(s).approve(dioneStaking.address, toWei(500)); + await dioneStaking.connect(s).stake(toWei(500)); + } + + await expect(dioneStaking.slashMiner(signers[1].address, [signers[0].address, signers[2].address, allSigners[4].address])) + .to.be.revertedWith("receipent address isn't dione miner"); + + await expect(dioneStaking.slashMiner(signers[1].address, [signers[0].address, signers[2].address, signers[1].address])) + .to.be.revertedWith("receipent address must not be slashing address"); + }); + + it("should fail set*Address methods when passing invalid values", async () => { + const zeroAddress = "0x0000000000000000000000000000000000000000"; + + await expect(dioneStaking.setDisputeContractAddress(zeroAddress)) + .to.be.revertedWith("address must not be zero"); + await expect(dioneStaking.setOracleContractAddress(zeroAddress)) + .to.be.revertedWith("address must not be zero"); + }); + + it("should stake few time successfully", async () => { + const expectedStake = toWei(500*3); + + for (var i = 0; i < 3; i++) { + await dioneToken.approve(dioneStaking.address, toWei(500)); + await dioneStaking.stake(toWei(500)); + } + + expect(await dioneStaking.minerStake(signers[0].address)) + .to.be.equal(expectedStake); + }); +}) + +function toWei(eth: number): BigNumber { + return ethers.constants.WeiPerEther.mul(eth); +} \ No newline at end of file