@gooddollar/goodcontracts
Version:
GoodDollar Contracts
956 lines (889 loc) • 35.3 kB
JavaScript
const SimpleDAIStaking = artifacts.require("SimpleDAIStaking");
const GoodDollar = artifacts.require("GoodDollar");
const DAIMock = artifacts.require("DAIMock");
const cDAIMock = artifacts.require("cDAIMock");
const cDAINonMintableMock = artifacts.require("cDAINonMintableMock");
const cDAILowWorthMock = artifacts.require("cDAILowWorthMock");
const Identity = artifacts.require("IdentityMock");
const Formula = artifacts.require("FeeFormula");
const avatarMock = artifacts.require("AvatarMock");
const ControllerMock = artifacts.require("ControllerMock");
const BN = web3.utils.BN;
export const BLOCK_INTERVAL = 30;
export const NULL_ADDRESS = "0x0000000000000000000000000000000000000000";
async function evm_mine(blocks) {
for (let i = 0; i < blocks; ++i)
await web3.currentProvider.send(
{ jsonrpc: "2.0", method: "evm_mine", id: 123 },
() => {}
);
}
contract("SimpleDAIStaking - staking with DAI mocks", ([founder, staker]) => {
let dai;
let cDAI;
let simpleStaking;
let avatar, goodDollar, identity, formula, controller;
before(async () => {
dai = await DAIMock.new();
cDAI = await cDAIMock.new(dai.address);
[, formula, identity] = await Promise.all([
dai.mint(cDAI.address, web3.utils.toWei("100000000", "ether")),
Formula.new(0),
Identity.new()
]);
goodDollar = await GoodDollar.new(
"GoodDollar",
"GDD",
"0",
formula.address,
identity.address,
NULL_ADDRESS
);
avatar = await avatarMock.new("", goodDollar.address, NULL_ADDRESS);
controller = await ControllerMock.new(avatar.address);
await avatar.transferOwnership(controller.address);
simpleStaking = await SimpleDAIStaking.new(
dai.address,
cDAI.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
await simpleStaking.start();
});
it("should mock cdai exchange rate 1e28 precision", async () => {
let rate = await cDAI.exchangeRateStored();
expect(rate.toString()).to.be.equal("10101010101010101010101010101");
});
it("should mint new dai", async () => {
let balance = await dai.balanceOf(founder);
expect(balance.toString()).to.be.equal("0");
await dai.mint(staker, web3.utils.toWei("100", "ether"));
balance = await dai.balanceOf(staker);
expect(balance.toString()).to.be.at.equal(web3.utils.toWei("100", "ether"));
});
it("should mint new cdai", async () => {
let balance = await dai.balanceOf(staker);
expect(balance.toString()).to.be.at.equal(web3.utils.toWei("100", "ether"));
await dai.approve(cDAI.address, web3.utils.toWei("100", "ether"), {
from: staker
});
await cDAI.mint(web3.utils.toWei("100", "ether"), { from: staker });
balance = await dai.balanceOf(staker);
expect(balance.toString()).to.be.equal("0");
let cdaiBalance = await cDAI.balanceOf(staker);
expect(cdaiBalance.toString()).to.be.equal(web3.utils.toWei("9900", "mwei"));
});
it("should redeem cdai", async () => {
let cdaiBalance = await cDAI.balanceOf(staker);
await cDAI.redeem(cdaiBalance.toString(), { from: staker });
let balance = await dai.balanceOf(staker);
expect(balance.toString()).to.be.equal(web3.utils.toWei("100", "ether"));
dai.transfer(dai.address, balance.toString(), { from: staker });
});
it("should return an error if non avatar account is trying to execute recover", async () => {
const cdai1 = await cDAIMock.new(dai.address);
let error = await simpleStaking.recover(cdai1.address).catch(e => e);
expect(error.message).to.have.string("only Avatar can call this method");
});
it("should transfer funds when execute recover of token which the contract has some balance", async () => {
const cdai1 = await cDAIMock.new(dai.address);
await dai.mint(cdai1.address, web3.utils.toWei("100", "ether"));
const cdai1BalanceFounder = await cdai1.balanceOf(founder);
await cdai1.transfer(simpleStaking.address, cdai1BalanceFounder);
let balanceBefore = await cdai1.balanceOf(avatar.address);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[cdai1.address]
);
await controller.genericCall(simpleStaking.address, encodedCall, avatar.address, 0);
let balanceAfter = await cdai1.balanceOf(avatar.address);
expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(
cdai1BalanceFounder.toString()
);
});
it("should returns the exact amount of staked dai without any effect of having excessive dai tokens in the contract", async () => {
const cDAI1 = await cDAIMock.new(dai.address);
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI1.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
const weiAmount = web3.utils.toWei("1000", "ether");
// staking dai
await dai.mint(staker, weiAmount);
let stakerBalanceBefore = await dai.balanceOf(staker);
await dai.approve(simpleStaking1.address, weiAmount, {
from: staker
});
await simpleStaking1.stakeDAI(weiAmount, {
from: staker
});
// transfer excessive dai to the contract
await dai.mint(founder, weiAmount);
await dai.transfer(simpleStaking1.address, weiAmount);
let balanceBefore = await dai.balanceOf(avatar.address);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[dai.address]
);
await controller.genericCall(simpleStaking1.address, encodedCall, avatar.address, 0);
await simpleStaking1.withdrawStake({
from: staker
});
let balanceAfter = await dai.balanceOf(avatar.address);
let stakerBalanceAfter = await dai.balanceOf(staker);
// checks that the excessive dai tokens have recovered and that all of the staked
// tokens have returned to the staker
expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(weiAmount.toString());
expect(stakerBalanceAfter.toString()).to.be.equal(stakerBalanceBefore.toString());
});
it("should not transfer excessive cdai funds when total staked is more than 0 and not paused and execute recover", async () => {
const cDAI1 = await cDAIMock.new(dai.address);
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI1.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
const weiAmount = web3.utils.toWei("1000", "ether");
await dai.mint(founder, web3.utils.toWei("100", "ether"));
await dai.approve(cDAI1.address, web3.utils.toWei("100", "ether"));
await cDAI1.mint(web3.utils.toWei("100", "ether"));
const cdaiBalanceFounder = await cDAI1.balanceOf(founder);
await cDAI1.transfer(simpleStaking1.address, cdaiBalanceFounder);
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking1.address, weiAmount, {
from: staker
});
let balanceBefore = await cDAI1.balanceOf(avatar.address);
let stakerBalanceBefore = await dai.balanceOf(staker);
await simpleStaking1.stakeDAI(weiAmount, {
from: staker
});
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[cDAI1.address]
);
await controller.genericCall(simpleStaking1.address, encodedCall, avatar.address, 0);
await simpleStaking1.withdrawStake({
from: staker
});
let balanceAfter = await cDAI1.balanceOf(avatar.address);
let stakerBalanceAfter = await dai.balanceOf(staker);
expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal("0");
expect(stakerBalanceAfter.toString()).to.be.equal(stakerBalanceBefore.toString());
});
it("should be able to stake dai", async () => {
let totalStakedBefore = await simpleStaking.totalStaked();
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(simpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
await simpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(console.log);
let totalStakedAfter = await simpleStaking.totalStaked();
let balance = await simpleStaking.stakers(staker);
expect(balance.stakedDAI.toString()).to.be.equal(
web3.utils.toWei("100", "ether") //100 dai
);
expect(totalStakedAfter.sub(totalStakedBefore).toString()).to.be.equal(web3.utils.toWei("100", "ether"));
let stakedcDaiBalance = await cDAI.balanceOf(simpleStaking.address);
// 8 decimals precision (99 cdai because of the exchange rate dai <> cdai)
expect(stakedcDaiBalance.toString()).to.be.equal(
web3.utils.toWei("9900", "mwei")
);
});
it("should be able to withdraw stake by staker", async () => {
let stakedcDaiBalanceBefore = await cDAI.balanceOf(simpleStaking.address); // simpleStaking cDAI balance
let stakerDaiBalanceBefore = await dai.balanceOf(staker); // staker DAI balance
let balanceBefore = await simpleStaking.stakers(staker); // user staked balance in GoodStaking
let totalStakedBefore = await simpleStaking.totalStaked(); // total staked in GoodStaking
const transaction = await simpleStaking.withdrawStake({
from: staker
});
let stakedcDaiBalanceAfter = await cDAI.balanceOf(simpleStaking.address); // simpleStaking cDAI balance
let stakerDaiBalanceAfter = await dai.balanceOf(staker); // staker DAI balance
let balanceAfter = await simpleStaking.stakers(staker); // user staked balance in GoodStaking
let totalStakedAfter = await simpleStaking.totalStaked(); // total staked in GoodStaking
expect(stakedcDaiBalanceAfter.lt(stakedcDaiBalanceBefore)).to.be.true;
expect(stakerDaiBalanceAfter.gt(stakerDaiBalanceBefore)).to.be.true;
expect(balanceBefore.stakedDAI.toString()).to.be.equal(
(stakerDaiBalanceAfter - stakerDaiBalanceBefore).toString()
);
expect((totalStakedBefore - totalStakedAfter).toString()).to.be.equal(
balanceBefore.stakedDAI.toString()
);
expect(balanceAfter.stakedDAI.toString()).to.be.equal("0");
expect(stakedcDaiBalanceAfter.toString()).to.be.equal("0");
expect(transaction.logs[0].event).to.be.equal("DAIStakeWithdraw");
expect(transaction.logs[0].args.staker).to.be.equal(staker);
expect(transaction.logs[0].args.daiValue.toString()).to.be.equal(
(stakerDaiBalanceAfter - stakerDaiBalanceBefore).toString()
);
});
it("should be able to withdraw stake by staker when the worth is lower than the actual staked", async () => {
let cDAI1 = await cDAILowWorthMock.new(dai.address);
dai.mint(cDAI1.address, web3.utils.toWei("100000000", "ether"));
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI1.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
const weiAmount = web3.utils.toWei("1000", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking1.address, weiAmount, {
from: staker
});
await simpleStaking1.stakeDAI(weiAmount, {
from: staker
});
let balanceBefore = await simpleStaking1.stakers(staker); // user staked balance in GoodStaking
let stakerDaiBalanceBefore = await dai.balanceOf(staker); // staker DAI balance
await simpleStaking1.withdrawStake({ from: staker });
let balanceAfter = await simpleStaking.stakers(staker); // user staked balance in GoodStaking
let stakerDaiBalanceAfter = await dai.balanceOf(staker); // staker DAI balance
expect(balanceAfter.stakedDAI.toString()).to.be.equal("0");
expect(balanceBefore.stakedDAI.div(new BN(2)).toString()).to.be.equal(
(stakerDaiBalanceAfter - stakerDaiBalanceBefore).toString()
);
});
it("should return 0s for gains when the current cdai worth is lower than the inital worth", async () => {
let cDAI1 = await cDAILowWorthMock.new(dai.address);
dai.mint(cDAI1.address, web3.utils.toWei("100000000", "ether"));
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI1.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
const weiAmount = web3.utils.toWei("1000", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking1.address, weiAmount, {
from: staker
});
await simpleStaking1.stakeDAI(weiAmount, {
from: staker
});
let gains = await simpleStaking1.currentUBIInterest();
expect(gains["0"].toString()).to.be.equal("0"); // cdaiGains
expect(gains["1"].toString()).to.be.equal("0"); // daiGains
expect(gains["2"].toString()).to.be.equal("0"); // precisionLossDai
});
it("should convert user staked DAI to the equal value of cDAI owned by the staking contract", async () => {
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(simpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
await simpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(console.log);
let stakedcDaiBalance = await cDAI.balanceOf(simpleStaking.address);
let stakercDaiBalance = await cDAI.balanceOf(staker);
expect(stakedcDaiBalance.toString()).to.be.equal(
web3.utils.toWei("9900", "mwei") //8 decimals precision
);
let stakedDaiBalance = await dai.balanceOf(simpleStaking.address);
expect(stakedDaiBalance.isZero()).to.be.true;
expect(stakercDaiBalance.isZero()).to.be.true;
await simpleStaking.withdrawStake({
from: staker
});
});
it("should not change the staker DAI balance if the conversion failed", async () => {
let fakeDai = await DAIMock.new();
let fakecDAI = await cDAIMock.new(fakeDai.address);
await fakeDai.mint(fakecDAI.address, web3.utils.toWei("100000000", "ether"));
let fakeSimpleStaking = await SimpleDAIStaking.new(
dai.address,
fakecDAI.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
); // staking should failed
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(fakeSimpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
let stakerDaiBalanceBefore = await dai.balanceOf(staker);
const error = await fakeSimpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(e => e);
expect(error.message).not.to.be.empty;
let stakerDaiBalanceAfter = await dai.balanceOf(staker);
expect(stakerDaiBalanceAfter.toString()).to.be.equal(
stakerDaiBalanceBefore.toString()
);
});
it("should not change the totalStaked if the conversion failed", async () => {
let fakeDai = await DAIMock.new();
let fakecDAI = await cDAIMock.new(fakeDai.address);
await fakeDai.mint(fakecDAI.address, web3.utils.toWei("100000000", "ether"));
let fakeSimpleStaking = await SimpleDAIStaking.new(
dai.address,
fakecDAI.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
); // staking should failed
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(fakeSimpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
let totalStakedBefore = await fakeSimpleStaking.totalStaked();
const error = await fakeSimpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(e => e);
expect(error.message).not.to.be.empty;
let totalStakedAfter = await fakeSimpleStaking.totalStaked();
expect(totalStakedAfter.toString()).to.be.equal(totalStakedBefore.toString());
});
it("should not update the staker list if the conversion failed", async () => {
let fakeDai = await DAIMock.new();
let fakecDAI = await cDAIMock.new(fakeDai.address);
await fakeDai.mint(fakecDAI.address, web3.utils.toWei("100000000", "ether"));
let fakeSimpleStaking = await SimpleDAIStaking.new(
dai.address,
fakecDAI.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
); // staking should failed
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(fakeSimpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
const error = await fakeSimpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(e => e);
expect(error.message).not.to.be.empty;
let balance = await fakeSimpleStaking.stakers(staker);
expect(balance.stakedDAI.toString()).to.be.equal(
web3.utils.toWei("0", "ether") //100 dai
);
});
it("should not be able to stake 0 dai", async () => {
const error = await simpleStaking
.stakeDAI(web3.utils.toWei("0", "ether"), {
from: staker
})
.catch(e => e);
expect(error.message).to.have.string("You need to stake a positive token amount");
});
it("should be able to stake dai when the allowed dai amount is higher than the staked amount", async () => {
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(simpleStaking.address, web3.utils.toWei("200", "ether"), {
from: staker
});
let balanceBefore = await simpleStaking.stakers(staker);
let stakedcDaiBalanceBefore = await cDAI.balanceOf(simpleStaking.address);
await simpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(console.log);
let balanceAfter = await simpleStaking.stakers(staker);
expect((balanceAfter.stakedDAI - balanceBefore.stakedDAI).toString()).to.be.equal(
web3.utils.toWei("100", "ether") //100 dai
);
let stakedcDaiBalanceAfter = await cDAI.balanceOf(simpleStaking.address);
expect((stakedcDaiBalanceAfter - stakedcDaiBalanceBefore).toString()).to.be.equal(
web3.utils.toWei("9900", "mwei") //8 decimals precision (99 cdai)
);
await simpleStaking.withdrawStake({
from: staker
});
});
it("should not be able to stake when approved dai amount is too low", async () => {
let lowWeiAmount = web3.utils.toWei("99", "ether");
let weiAmount = web3.utils.toWei("100", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking.address, lowWeiAmount, {
from: staker
});
const error = await simpleStaking
.stakeDAI(weiAmount, {
from: staker
})
.catch(e => e);
expect(error.message).not.to.be.empty;
});
it("should not be able to stake when staker dai balance is too low", async () => {
let currentBalance = await dai.balanceOf(staker);
let weiAmount = web3.utils.toWei("100", "ether");
let approvedAmount = currentBalance.valueOf() + weiAmount;
await dai.approve(simpleStaking.address, approvedAmount, {
from: staker
});
const error = await simpleStaking
.stakeDAI(approvedAmount, {
from: staker
})
.catch(e => e);
expect(error.message).not.to.be.empty;
});
it("should emit a DAIStaked event", async () => {
const weiAmount = web3.utils.toWei("100", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking.address, weiAmount, {
from: staker
});
const transaction = await simpleStaking
.stakeDAI(weiAmount, {
from: staker
})
.catch(console.log);
assert(transaction.logs[0].event === "DAIStaked");
assert.equal(transaction.logs[0].args.daiValue.valueOf(), weiAmount);
await simpleStaking.withdrawStake({
from: staker
});
});
it("should not withdraw interest to owner if cDAI value is lower than the staked", async () => {
const weiAmount = web3.utils.toWei("1000", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking.address, weiAmount, {
from: staker
});
await simpleStaking
.stakeDAI(weiAmount, {
from: staker
})
.catch(console.log);
const gains = await simpleStaking.currentUBIInterest();
const cdaiGains = gains["0"];
const precisionLossDai = gains["2"].toString(); //last 10 decimals since cdai is only 8 decimals while dai is 18
const fundBalanceBefore = await cDAI.balanceOf(founder);
await evm_mine(BLOCK_INTERVAL);
await simpleStaking.collectUBIInterest(founder);
const fundBalanceAfter = await cDAI.balanceOf(founder);
expect(cdaiGains.toString()).to.be.equal("0");
expect(precisionLossDai.toString()).to.be.equal("0");
expect(fundBalanceAfter.toString()).to.be.equal(fundBalanceBefore.toString());
await simpleStaking.withdrawStake({
from: staker
});
});
it("should not be able to stake if the getting an error while minting new cdai", async () => {
let cDAI1 = await cDAINonMintableMock.new(dai.address);
dai.mint(cDAI1.address, web3.utils.toWei("100000000", "ether"));
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI1.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
const weiAmount = web3.utils.toWei("1000", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking1.address, weiAmount, {
from: staker
});
const error = await simpleStaking1
.stakeDAI(weiAmount, {
from: staker
})
.catch(e => e);
expect(error.message).to.have.string("Minting cDai failed, funds returned");
});
it("should mock cdai updated exchange rate", async () => {
await cDAI.exchangeRateCurrent();
let rate = await cDAI.exchangeRateStored();
expect(rate.toString()).to.be.equal("10201010101010101010101010101");
});
it("should report interest gains", async () => {
await dai.mint(staker, web3.utils.toWei("400", "ether"));
await dai.approve(simpleStaking.address, web3.utils.toWei("400", "ether"), {
from: staker
});
await simpleStaking
.stakeDAI(web3.utils.toWei("400", "ether"), {
from: staker
})
.catch(console.log);
await cDAI.exchangeRateCurrent();
const gains = await simpleStaking.currentUBIInterest();
const cdaiGains = gains["0"];
const precisionLossDai = gains["2"];
expect(cdaiGains.toString()).to.be.equal("380659786"); //8 decimals precision
expect(precisionLossDai.toString()).to.be.equal("5733333332"); //10 decimals precision lost
await simpleStaking.withdrawStake({
from: staker
});
});
it("should return for canCollect", async () => {
let canCollect = await simpleStaking.canCollect();
expect(canCollect).to.be.true;
});
it("should withdraw interest to owner", async () => {
const totalStaked = await simpleStaking.totalStaked();
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(simpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
await simpleStaking
.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
})
.catch(console.log);
const gains = await simpleStaking.currentUBIInterest();
const cdaiGains = gains["0"];
const precisionLossDai = gains["2"].toString(); //last 10 decimals since cdai is only 8 decimals while dai is 18
const canCollect = await simpleStaking.canCollect();
expect(canCollect).to.be.equal(true);
const fundBalance0 = await cDAI.balanceOf(founder);
const res = await simpleStaking.collectUBIInterest(founder);
const fundBalance1 = await cDAI.balanceOf(founder);
const fundDaiWorth = await simpleStaking.currentDAIWorth();
expect(cdaiGains.toString()).to.be.equal(fundBalance1.sub(fundBalance0).toString());
expect(fundDaiWorth.toString()).to.be.equal(
//10 gwei = 10 decimals + precisionLoss = 20 decimals = 100 ether of DAI
web3.utils.toWei("10", "gwei") + precisionLossDai
);
await simpleStaking.withdrawStake({
from: staker
});
});
it("should withdraw only by fundmanager", async () => {
const error = await simpleStaking
.collectUBIInterest(founder, {
from: staker
})
.catch(e => e);
expect(error.message).to.have.string("Only FundManager can call this method");
});
it("should be able to be called once per withdrawInterval", async () => {
const canCollect = await simpleStaking.canCollect();
expect(canCollect).to.be.equal(false);
const error = await simpleStaking.collectUBIInterest(founder).catch(e => e);
expect(error.message).to.have.string("Need to wait for the next interval");
});
it("should not be able to double withdraw stake", async () => {
await dai.mint(staker, web3.utils.toWei("100", "ether"));
await dai.approve(simpleStaking.address, web3.utils.toWei("100", "ether"), {
from: staker
});
await simpleStaking.stakeDAI(web3.utils.toWei("100", "ether"), {
from: staker
});
await simpleStaking
.withdrawStake({
from: staker
})
.catch(e => console.log({ e }));
const error = await simpleStaking
.withdrawStake({
from: staker
})
.catch(e => e);
expect(error.message).to.have.string("No DAI staked");
});
it("should not be able to withdraw if not a staker", async () => {
const error = await simpleStaking
.withdrawStake({
from: founder
})
.catch(e => e);
expect(error.message).to.have.string("No DAI staked");
});
it("should not be able to change the reserve cDAI balance in case of an error", async () => {
let stakedcDaiBalanceBefore = await cDAI.balanceOf(simpleStaking.address);
await simpleStaking
.withdrawStake({
from: founder
})
.catch(e => e);
let stakedcDaiBalanceAfter = await cDAI.balanceOf(simpleStaking.address);
expect(stakedcDaiBalanceAfter.toString()).to.be.equal(
stakedcDaiBalanceBefore.toString()
);
});
it("should be able to withdraw stake by staker and precision loss should not be equal to 0", async () => {
const weiAmount = web3.utils.toWei("100", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking.address, weiAmount, {
from: staker
});
await simpleStaking
.stakeDAI(weiAmount, {
from: staker
})
.catch(console.log);
let stakedcDaiBalanceBefore = await cDAI.balanceOf(simpleStaking.address); // simpleStaking cDAI balance
const transaction = await simpleStaking.withdrawStake({
from: staker
});
let stakedcDaiBalanceAfter = await cDAI.balanceOf(simpleStaking.address); // simpleStaking cDAI balance
expect(stakedcDaiBalanceAfter.lt(stakedcDaiBalanceBefore)).to.be.true;
expect(stakedcDaiBalanceAfter.toString()).to.not.be.equal("0"); //precision loss, so it wont be exactly 0
});
it("should withdraw interest to recipient specified by the owner", async () => {
const weiAmount = web3.utils.toWei("100", "ether");
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking.address, weiAmount, {
from: staker
});
await simpleStaking
.stakeDAI(weiAmount, {
from: staker
})
.catch(console.log);
const gains = await simpleStaking.currentUBIInterest();
const cdaiGains = gains["0"];
const precisionLossDai = gains["2"].toString(); //last 10 decimals since cdai is only 8 decimals while dai is 18
await evm_mine(BLOCK_INTERVAL);
const fundBalance0 = await cDAI.balanceOf(staker);
await simpleStaking.collectUBIInterest(staker);
const fundBalance1 = await cDAI.balanceOf(staker);
const fundDaiWorth = await simpleStaking.currentDAIWorth();
expect(cdaiGains.toString()).to.be.equal(fundBalance1.sub(fundBalance0).toString());
expect(fundDaiWorth.toString()).to.be.equal(
// 10 gwei = 10 decimals + precisionLoss = 20 decimals = 100 ether of DAI
web3.utils.toWei("10", "gwei") + precisionLossDai
);
});
it("should not withdraw interest if the recipient specified by the owner is the staking contract", async () => {
await evm_mine(BLOCK_INTERVAL);
const error = await simpleStaking
.collectUBIInterest(simpleStaking.address)
.catch(e => e);
expect(error.message).to.have.string("Recipient cannot be the staking contract");
});
it("should pause the contract", async () => {
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "end",
type: "function",
inputs: []
},
[]
);
await controller.genericCall(simpleStaking.address, encodedCall, avatar.address, 0);
const isPaused = await simpleStaking.paused();
expect(isPaused).to.be.true;
});
it("should not transfer excessive cdai funds when the contract is paused and the total staked is not 0", async () => {
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
const weiAmount = web3.utils.toWei("100", "ether");
// staking dai
await dai.mint(staker, weiAmount);
await dai.approve(simpleStaking1.address, weiAmount, {
from: staker
});
await simpleStaking1.stakeDAI(weiAmount, {
from: staker
});
// transfer excessive cdai to the contract
await dai.mint(founder, weiAmount);
await dai.approve(cDAI.address, weiAmount);
await cDAI.mint(weiAmount);
const cdaiBalanceFounder = await cDAI.balanceOf(founder);
await cDAI.transfer(simpleStaking1.address, cdaiBalanceFounder);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "end",
type: "function",
inputs: []
},
[]
);
await controller.genericCall(simpleStaking1.address, encodedCall, avatar.address, 0);
let avatarBalanceBefore = await cDAI.balanceOf(avatar.address);
encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[cDAI.address]
);
await controller.genericCall(simpleStaking1.address, encodedCall, avatar.address, 0);
let avatarBalanceAfter = await cDAI.balanceOf(avatar.address);
expect(avatarBalanceAfter.sub(avatarBalanceBefore).toString()).to.be.equal(
web3.utils.toWei("0", "ether")
);
});
it("should not transfer excessive cdai funds when the contract is not paused and the total staked is 0", async () => {
// totalStaked is equal to 0
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cDAI.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
let avatarBalanceBefore = await cDAI.balanceOf(avatar.address);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[cDAI.address]
);
await controller.genericCall(simpleStaking1.address, encodedCall, avatar.address, 0);
let avatarBalanceAfter = await cDAI.balanceOf(avatar.address);
expect(avatarBalanceAfter.sub(avatarBalanceBefore).toString()).to.be.equal(
web3.utils.toWei("0", "ether")
);
});
it("should transfer excessive cdai funds when execute recover", async () => {
await dai.mint(founder, web3.utils.toWei("100", "ether"));
await dai.approve(cDAI.address, web3.utils.toWei("100", "ether"));
const cdaiBalanceFounder1 = await cDAI.balanceOf(founder);
await cDAI.mint(web3.utils.toWei("100", "ether"));
const cdaiBalanceFounder2 = await cDAI.balanceOf(founder);
await cDAI.transfer(simpleStaking.address, cdaiBalanceFounder2.sub(cdaiBalanceFounder1).toString());
let avatarBalanceBefore = await cDAI.balanceOf(avatar.address);
await simpleStaking.withdrawStake({
from: staker
});
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[cDAI.address]
);
await controller.genericCall(simpleStaking.address, encodedCall, avatar.address, 0);
let avatarBalanceAfter = await cDAI.balanceOf(avatar.address);
let stakingBalance = await cDAI.balanceOf(simpleStaking.address);
// checks that something was recovered
expect(avatarBalanceAfter.sub(avatarBalanceBefore).toString()).to.not.equal("0");
expect(stakingBalance.toString()).to.be.equal("0");
});
it("should not transfer any funds if trying to execute recover of a token without balance", async () => {
const cdai1 = await cDAIMock.new(dai.address);
let simpleStaking1 = await SimpleDAIStaking.new(
dai.address,
cdai1.address,
founder,
BLOCK_INTERVAL,
avatar.address,
identity.address
);
await dai.mint(founder, web3.utils.toWei("100", "ether"));
await dai.approve(cdai1.address, web3.utils.toWei("100", "ether"));
await cdai1.balanceOf(founder);
await cdai1.mint(web3.utils.toWei("100", "ether"));
await cdai1.transfer(simpleStaking1.address, "0");
let balanceBefore = await cdai1.balanceOf(avatar.address);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "recover",
type: "function",
inputs: [
{
type: "address",
name: "_token"
}
]
},
[cdai1.address]
);
await controller.genericCall(simpleStaking1.address, encodedCall, avatar.address, 0);
let balanceAfter = await cdai1.balanceOf(avatar.address);
expect(balanceAfter.toString()).to.be.equal(balanceBefore.toString());
});
it("should not be able to change the fund manager address if not owner", async () => {
const e = await simpleStaking.setFundManager(NULL_ADDRESS).catch(e => e);
const newFM = await simpleStaking.fundManager();
expect(e.message).to.not.be.empty;
expect(newFM.toString()).to.not.be.equal(NULL_ADDRESS);
});
it("should be able to change the fund manager address", async () => {
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setFundManager",
type: "function",
inputs: [
{
type: "address",
name: "_fundManager"
}
]
},
[NULL_ADDRESS]
);
await controller.genericCall(simpleStaking.address, encodedCall, avatar.address, 0);
const newFM = await simpleStaking.fundManager();
expect(newFM.toString()).to.be.equal(NULL_ADDRESS);
});
});