@gooddollar/goodcontracts
Version:
GoodDollar Contracts
737 lines (695 loc) • 31.9 kB
JavaScript
require("openzeppelin-solidity/build/contracts/TokenTimelock.json");
const GoodReserve = artifacts.require("GoodReserveCDai");
const MarketMaker = artifacts.require("GoodMarketMaker");
const Avatar = artifacts.require("Avatar");
const GoodDollar = artifacts.require("GoodDollar");
const DAIMock = artifacts.require("DAIMock");
const cDAIMock = artifacts.require("cDAIMock");
const avatarMock = artifacts.require("AvatarMock");
const ControllerMock = artifacts.require("ControllerMock");
const Identity = artifacts.require("Identity");
const Formula = artifacts.require("FeeFormula");
const ContributionCalculation = artifacts.require("ContributionCalculation");
const BN = web3.utils.BN;
export const BLOCK_INTERVAL = 1;
export const NULL_ADDRESS = "0x0000000000000000000000000000000000000000";
contract("GoodReserve - staking with cDAI mocks", ([founder, staker]) => {
let dai;
let cDAI;
let goodReserve;
let goodDollar, avatar, identity, formula, marketMaker, contribution, controller;
before(async () => {
dai = await DAIMock.new();
[cDAI, identity, formula] = await Promise.all([
cDAIMock.new(dai.address),
Identity.new(),
Formula.new(0)
]);
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);
marketMaker = await MarketMaker.new(
avatar.address,
999388834642296,
1e15
);
contribution = await ContributionCalculation.new(avatar.address, 0, 1e15);
goodReserve = await GoodReserve.new(
dai.address,
cDAI.address,
NULL_ADDRESS,
avatar.address,
identity.address,
marketMaker.address,
contribution.address,
BLOCK_INTERVAL
);
await goodReserve.start();
dai.mint(cDAI.address, web3.utils.toWei("100000000", "ether"));
await marketMaker.initializeToken(
cDAI.address,
"100", //1gd
"10000", //0.0001 cDai
"1000000" //100% rr
);
await marketMaker.initializeToken(dai.address, "100", "1000000000", "1000000");
});
it("should set marketmaker in the reserve by avatar", async () => {
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setMarketMaker",
type: "function",
inputs: [
{
type: "address",
name: "_marketMaker"
}
]
},
[marketMaker.address]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
const newMM = await goodReserve.marketMaker();
expect(newMM.toString()).to.be.equal(marketMaker.address);
});
it("should set fundManager in the reserve by avatar", async () => {
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setFundManager",
type: "function",
inputs: [
{
type: "address",
name: "_fundManager"
}
]
},
[founder]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
const newFM = await goodReserve.fundManager();
expect(newFM.toString()).to.be.equal(founder);
});
it("should returned true for isActive", async () => {
const isActive = await goodReserve.isActive();
expect(isActive.toString()).to.be.equal("true");
});
it("should returned fixed 0.0001 market price", async () => {
const gdPrice = await goodReserve.currentPrice(cDAI.address);
const cdaiWorthInGD = gdPrice.mul(new BN("100000000", 10));
const gdFloatPrice = gdPrice.toNumber() / 10 ** 8; //cdai 8 decimals
expect(gdFloatPrice).to.be.equal(0.0001);
expect(cdaiWorthInGD.toString()).to.be.equal("1000000000000"); //in 8 decimals precision
expect(cdaiWorthInGD.toNumber() / 10 ** 8).to.be.equal(10000);
});
it("should not be able to buy gd if the minter is not the reserve", async () => {
await marketMaker.transferOwnership(goodReserve.address);
let amount = 1e8;
await cDAI.approve(goodReserve.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
let error = await goodReserve.buy(cDAI.address, amount, 0).catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
goodDollar.addMinter(goodReserve.address);
expect(error.message).not.to.be.empty;
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(cDAIBalanceBefore.toString());
});
it("should calculate mint UBI correctly for 18 decimals precision and no interest", async () => {
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
const gdPriceBefore = await marketMaker.currentPrice(cDAI.address);
const tx = await goodReserve.mintInterestAndUBI(
cDAI.address,
web3.utils.toWei("1", "ether"),
"0"
);
const gdBalanceFund = await goodDollar.balanceOf(founder);
const gdPriceAfter = await marketMaker.currentPrice(cDAI.address);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
// expected that minted token will be added to the previous supply
expect(supplyAfter.toString()).to.be.equal(
tx.logs[0].args.gdInterestMinted
.add(tx.logs[0].args.gdExpansionMinted)
.add(supplyBefore)
.toString()
);
// expected that the new reserve balance will include
// the new 1e18 cdai which transferred
expect(reserveBalanceAfter.toString()).to.be.equal(
(reserveBalanceBefore.toNumber() + 1e18).toString()
);
// the new reserve ratio should be effected from the mintExpansion by:
// the daily change that was set up in the constructor (999388834642296)
expect(rrAfter.toString()).to.be.equal("999388");
// the price should be the same
expect(gdPriceAfter.toString()).to.be.equal(gdPriceBefore.toString());
expect(gdBalanceFund.toString()).to.be.equal(
tx.logs[0].args.gdInterestMinted.add(tx.logs[0].args.gdExpansionMinted).toString()
);
});
it("should calculate mint UBI correctly for 18 decimals precision and partial interest", async () => {
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
const gdBalanceFundBefore = await goodDollar.balanceOf(founder);
const gdBalanceAvatarBefore = await goodDollar.balanceOf(avatar.address);
const gdPriceBefore = await marketMaker.currentPrice(cDAI.address);
const tx = await goodReserve.mintInterestAndUBI(
cDAI.address,
web3.utils.toWei("10000", "gwei"),
"10000"
); // interest is 0.0001 cDai which equal to 1 gd
const gdBalanceFundAfter = await goodDollar.balanceOf(founder);
const gdBalanceAvatarAfter = await goodDollar.balanceOf(avatar.address);
const gdPriceAfter = await marketMaker.currentPrice(cDAI.address);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
let et = new BN(web3.utils.toWei("10000", "gwei"));
const toMint = tx.logs[0].args.gdInterestMinted.add(
tx.logs[0].args.gdExpansionMinted
);
expect(reserveBalanceAfter.toString()).to.be.equal(
et.add(reserveBalanceBefore).toString()
);
expect(supplyAfter.toString()).to.be.equal(toMint.add(supplyBefore).toString());
expect(gdPriceAfter.toString()).to.be.equal(gdPriceBefore.toString());
expect((gdBalanceAvatarAfter - gdBalanceAvatarBefore).toString()).to.be.equal("0"); // 1 gd
expect((gdBalanceFundAfter - gdBalanceFundBefore).toString()).to.be.equal(
toMint.toString()
);
expect(rrAfter.toString()).to.be.equal("998777");
});
it("should not mint UBI if the reserve is not cDAI", async () => {
let error = await goodReserve
.mintInterestAndUBI(dai.address, web3.utils.toWei("1", "ether"), "0")
.catch(e => e);
expect(error.message).not.to.be.empty;
});
it("should not mint UBI if the caller is not the fund manager", async () => {
let error = await goodReserve
.mintInterestAndUBI(cDAI.address, web3.utils.toWei("1", "ether"), "0", {
from: staker
})
.catch(e => e);
expect(error.message).not.to.be.empty;
});
it("should set block interval by avatar", async () => {
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setBlockInterval",
type: "function",
inputs: [
{
type: "uint256",
name: "_blockInterval"
}
]
},
[100]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
const newBI = await goodReserve.blockInterval();
expect(newBI.toString()).to.be.equal("100");
});
it("should not mint UBI if not in the interval", async () => {
const gdBalanceFundBefore = await goodDollar.balanceOf(founder);
const gdBalanceAvatarBefore = await goodDollar.balanceOf(avatar.address);
const error = await goodReserve
.mintInterestAndUBI(cDAI.address, web3.utils.toWei("10000", "gwei"), "10000")
.catch(e => e);
const gdBalanceFundAfter = await goodDollar.balanceOf(founder);
const gdBalanceAvatarAfter = await goodDollar.balanceOf(avatar.address);
expect(error.message).to.have.string("wait for the next interval");
expect(gdBalanceFundAfter.toString()).to.be.equal(gdBalanceFundBefore.toString());
expect(gdBalanceAvatarAfter.toString()).to.be.equal(gdBalanceAvatarBefore.toString());
});
it("should be able to buy gd with cDAI", async () => {
let amount = 1e8;
await dai.mint(web3.utils.toWei("100", "ether"));
dai.approve(cDAI.address, web3.utils.toWei("100", "ether"));
await cDAI.mint(web3.utils.toWei("100", "ether"));
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
let rrBefore = reserveToken.reserveRatio;
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve.currentPrice(cDAI.address);
await cDAI.approve(goodReserve.address, amount);
let transaction = await goodReserve.buy(cDAI.address, amount, 0);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve.currentPrice(cDAI.address);
expect((cDAIBalanceReserveAfter - cDAIBalanceReserveBefore).toString()).to.be.equal(
amount.toString()
);
expect((reserveBalanceAfter - reserveBalanceBefore).toString()).to.be.equal(
amount.toString()
);
expect(supplyAfter.sub(supplyBefore).toString()).to.be.equal(
gdBalanceAfter.sub(gdBalanceBefore).toString()
);
expect(rrAfter.toString()).to.be.equal(rrBefore.toString());
expect(gdBalanceAfter.gt(gdBalanceBefore)).to.be.true;
expect(cDAIBalanceBefore.gt(cDAIBalanceAfter)).to.be.true;
expect(priceAfter.toString()).to.be.equal(priceBefore.toString());
expect(transaction.logs[0].event).to.be.equal("TokenPurchased");
});
it("should be able to buy gd with cDAI with generic amount of cdai tokens", async () => {
await dai.mint(web3.utils.toWei("4895", "ether"));
dai.approve(cDAI.address, web3.utils.toWei("4895", "ether"));
let cdaibefore = await cDAI.balanceOf(founder);
await cDAI.mint(web3.utils.toWei("4895", "ether"));
let cdaiafter = await cDAI.balanceOf(founder);
let amount = (cdaiafter.sub(cdaibefore)).toNumber();
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await cDAI.approve(goodReserve.address, amount);
let transaction = await goodReserve.buy(cDAI.address, amount, 0);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
// actual cdai balance
expect((cDAIBalanceReserveAfter.sub(cDAIBalanceReserveBefore)).toString()).to.be.equal(
amount.toString()
);
// cdai balance according to the market maker
expect(reserveBalanceAfter.sub(reserveBalanceBefore).toString()).to.be.equal(
amount.toString()
);
expect(supplyAfter.sub(supplyBefore).toString()).to.be.equal(
gdBalanceAfter.sub(gdBalanceBefore).toString()
);
expect(transaction.logs[0].event).to.be.equal("TokenPurchased");
});
it("should not be able to buy gd with other tokens beside cDAI", async () => {
let amount = 1e8;
await dai.approve(goodReserve.address, amount);
let error = await goodReserve.buy(dai.address, amount, 0).catch(e => e);
expect(error.message).to.have.string("Only cDAI is supported");
});
it("should not be able to buy gd without cDAI allowance", async () => {
let amount = 1e8;
await cDAI.approve(goodReserve.address, "0");
let error = await goodReserve.buy(cDAI.address, amount, 0).catch(e => e);
expect(error.message).to.have.string("You need to approve cDAI transfer first");
});
it("should not be able to buy gd without enough cDAI funds", async () => {
let amount = 1e8;
const cDAIBalanceBeforeTransfer = await cDAI.balanceOf(founder);
await cDAI.transfer(staker, cDAIBalanceBeforeTransfer.toString());
await cDAI.approve(goodReserve.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
let error = await goodReserve.buy(cDAI.address, amount, 0).catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
expect(error.message).not.to.be.empty;
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(cDAIBalanceBefore.toString());
await cDAI.transfer(founder, cDAIBalanceBeforeTransfer.toString(), {
from: staker
});
});
it("should not be able to buy gd when the minimum return is higher than the actual return", async () => {
let amount = 1e8;
await cDAI.approve(goodReserve.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
let error = await goodReserve.buy(cDAI.address, amount, 2000000).catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
expect(error.message).to.have.string("GD return must be above the minReturn");
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(cDAIBalanceBefore.toString());
});
it("should be able to sell gd to cDAI without contribution", async () => {
let amount = 1e4;
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
let reserveRatio = reserveToken.reserveRatio;
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await goodDollar.approve(goodReserve.address, amount);
let transaction = await goodReserve.sell(cDAI.address, amount, 0);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
// according to the initialization settings reserve ratio is 100%. the calculation is:
// Return = _reserveBalance * (1 - (1 - _sellAmount / _supply) ^ (1000000 / _reserveRatio))
const expectedReturn = await marketMaker.calculateSaleReturn(
supplyBefore.toString(),
reserveBalanceBefore.toString(),
reserveRatio.toString(),
amount);
expect((cDAIBalanceAfter - cDAIBalanceBefore).toString()).to.be.equal(expectedReturn.toString());
expect((cDAIBalanceReserveBefore - cDAIBalanceReserveAfter).toString()).to.be.equal(
expectedReturn.toString()
);
expect(reserveBalanceBefore.sub(reserveBalanceAfter).toString()).to.be.equal(
expectedReturn.toString()
);
// 1e4 gd sold (burn from the supply)
expect((supplyBefore - supplyAfter).toString()).to.be.equal(amount.toString());
expect(gdBalanceBefore.gt(gdBalanceAfter)).to.be.true;
expect(cDAIBalanceAfter.gt(cDAIBalanceBefore)).to.be.true;
expect(transaction.logs[0].event).to.be.equal("TokenSold");
});
it("should set sell contribution ratio by avatar", async () => {
let nom = new BN(2e14).toString();
let denom = new BN(1e15).toString();
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setContributionRatio",
type: "function",
inputs: [
{
type: "uint256",
name: "_nom"
},
{
type: "uint256",
name: "_denom"
}
]
},
[nom, denom]
);
await controller.genericCall(contribution.address, encodedCall, avatar.address, 0);
const newRatio = await contribution.sellContributionRatio();
expect(newRatio.toString()).to.be.equal("200000000000000000000000000");
});
it("should not be able to set the sell contribution ratio if not avatar", async () => {
let error = await contribution.setContributionRatio(2e14, 1e15).catch(e => e);
expect(error.message).to.have.string("only Avatar can call this method");
});
it("should not be able to set the contribution contract address if not avatar", async () => {
let error = await goodReserve.setContributionAddress(NULL_ADDRESS).catch(e => e);
expect(error.message).to.have.string("only Avatar can call this method");
});
it("should set contribution contract address by avatar", async () => {
const currentAddress = await goodReserve.contribution();
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setContributionAddress",
type: "function",
inputs: [
{
type: "address",
name: "_contribution"
}
]
},
[NULL_ADDRESS]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
let newAddress = await goodReserve.contribution();
expect(newAddress).to.be.equal(NULL_ADDRESS);
encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "setContributionAddress",
type: "function",
inputs: [
{
type: "address",
name: "_contribution"
}
]
},
[currentAddress]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
newAddress = await goodReserve.contribution();
expect(newAddress).to.be.equal(currentAddress);
});
it("should calculate the sell contribution", async () => {
let nom = new BN(2e14).toString();
let denom = new BN(1e15).toString();
let actual = await contribution.calculateContribution(
marketMaker.address,
goodReserve.address,
founder,
cDAI.address,
1e4
);
expect(actual.toString()).to.be.equal(((1e4 * nom) / denom).toString());
});
it("should be able to sell gd to cDAI with contribution of 20%", async () => {
let amount = 1e4; // 100 gd
// deduced amount, ie. return minus contribution. reserve ratio is 100%.
// example without deduction:
// 1 gd (100) equals to 0.0001 cDai (10000) so 100 gd (10k) equals to 0.01 cDai (1m)
// since there is 20% contribution the return is 0.008 cDai (800k)
let expectedReturn = 800000;
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
let rrBefore = reserveToken.reserveRatio;
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve.currentPrice(cDAI.address);
await goodDollar.approve(goodReserve.address, amount);
let transaction = await goodReserve.sell(cDAI.address, amount, 0);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve.currentPrice(cDAI.address);
expect((cDAIBalanceAfter - cDAIBalanceBefore).toString()).to.be.equal(
expectedReturn.toString()
);
expect((cDAIBalanceReserveBefore - cDAIBalanceReserveAfter).toString()).to.be.equal(
expectedReturn.toString()
);
expect(reserveBalanceBefore.sub(reserveBalanceAfter).toString()).to.be.equal(
expectedReturn.toString()
);
expect((supplyBefore - supplyAfter).toString()).to.be.equal(amount.toString());
expect(rrAfter.toString()).to.be.equal(rrBefore.toString());
expect(gdBalanceBefore.gt(gdBalanceAfter)).to.be.true;
expect(cDAIBalanceAfter.gt(cDAIBalanceBefore)).to.be.true;
expect(priceAfter.toString()).to.be.equal(priceBefore.toString());
expect(transaction.logs[0].event).to.be.equal("TokenSold");
});
it("should not be able to sell gd to other tokens beside cDAI", async () => {
let amount = 1e8;
await dai.approve(goodReserve.address, amount);
let error = await goodReserve.sell(dai.address, amount, 0).catch(e => e);
expect(error.message).to.have.string("Only cDAI is supported");
});
it("should not be able to sell gd without gd allowance", async () => {
let amount = 1e4;
await goodDollar.approve(goodReserve.address, "0");
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
let error = await goodReserve.sell(cDAI.address, amount, 0).catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
expect(error.message).not.to.be.empty;
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(cDAIBalanceBefore.toString());
});
it("should not be able to sell gd without enough gd funds", async () => {
let amount = 1e4;
const gdBalanceBeforeTransfer = await goodDollar.balanceOf(founder);
await goodDollar.transfer(staker, gdBalanceBeforeTransfer.toString());
await goodDollar.approve(goodReserve.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
let error = await goodReserve.sell(cDAI.address, amount, 0).catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
expect(error.message).not.to.be.empty;
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(cDAIBalanceBefore.toString());
await goodDollar.transfer(founder, gdBalanceBeforeTransfer.toString(), {
from: staker
});
});
it("should not be able to sell gd when the minimum return is higher than the actual return", async () => {
let amount = 1e4;
await goodDollar.approve(goodReserve.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder);
const cDAIBalanceBefore = await cDAI.balanceOf(founder);
let error = await goodReserve.sell(cDAI.address, amount, 2000000).catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder);
const cDAIBalanceAfter = await cDAI.balanceOf(founder);
expect(error.message).to.have.string("Token return must be above the minReturn");
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(cDAIBalanceBefore.toString());
});
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 goodReserve.recover(cdai1.address).catch(e => e);
expect(error.message).to.have.string("only Avatar can call this method");
});
it("should not transfer any funds if trying to execute recover of token without balance", async () => {
const cdai1 = await cDAIMock.new(dai.address);
await dai.mint(cdai1.address, web3.utils.toWei("100", "ether"));
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(goodReserve.address, encodedCall, avatar.address, 0);
let balanceAfter = await cdai1.balanceOf(avatar.address);
expect(balanceAfter.toString()).to.be.equal(balanceBefore.toString());
});
it("should transfer funds when execute recover of token which the reserve 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(goodReserve.address, cdai1BalanceFounder.toString());
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(goodReserve.address, encodedCall, avatar.address, 0);
let balanceAfter = await cdai1.balanceOf(avatar.address);
expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(
cdai1BalanceFounder.toString()
);
});
it("should not be able to destroy if not avatar", async () => {
let avatarBalanceBefore = await cDAI.balanceOf(avatar.address);
let reserveBalanceBefore = await cDAI.balanceOf(goodReserve.address);
let error = await goodReserve.end().catch(e => e);
expect(error.message).to.have.string("only Avatar can call this method");
let avatarBalanceAfter = await cDAI.balanceOf(avatar.address);
let reserveBalanceAfter = await cDAI.balanceOf(goodReserve.address);
let isActive = await goodReserve.isActive();
let newMMOwner = await marketMaker.owner();
expect((avatarBalanceAfter - avatarBalanceBefore).toString()).to.be.equal("0");
expect(reserveBalanceAfter.toString()).to.be.equal(reserveBalanceBefore.toString());
expect(newMMOwner).to.be.equal(goodReserve.address);
expect(isActive.toString()).to.be.equal("true");
});
it("should destroy the contract and transfer cDAI funds to the given destination and transfer marker maker ownership", async () => {
let avatarBalanceBefore = await cDAI.balanceOf(avatar.address);
let reserveBalanceBefore = await cDAI.balanceOf(goodReserve.address);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "end",
type: "function",
inputs: []
},
[]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
let avatarBalanceAfter = await cDAI.balanceOf(avatar.address);
let reserveBalanceAfter = await cDAI.balanceOf(goodReserve.address);
let code = await web3.eth.getCode(goodReserve.address);
let newMMOwner = await marketMaker.owner();
expect((avatarBalanceAfter - avatarBalanceBefore).toString()).to.be.equal(
reserveBalanceBefore.toString()
);
expect(reserveBalanceAfter.toString()).to.be.equal("0");
expect(newMMOwner).to.be.equal(avatar.address);
expect(code.toString()).to.be.equal("0x");
});
it("should destroy the reserve contract when it holds 0 cdai", async () => {
const marketMaker1 = await MarketMaker.new(
avatar.address,
999388834642296,
1e15
);
const goodReserve1 = await GoodReserve.new(
dai.address,
cDAI.address,
founder,
avatar.address,
identity.address,
marketMaker1.address,
contribution.address,
BLOCK_INTERVAL
);
goodDollar.addMinter(goodReserve1.address);
await marketMaker1.transferOwnership(goodReserve1.address);
await goodReserve1.start();
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "end",
type: "function",
inputs: []
},
[]
);
await controller.genericCall(goodReserve1.address, encodedCall, avatar.address, 0);
let code = await web3.eth.getCode(goodReserve1.address);
expect(code.toString()).to.be.equal("0x");
});
it("should not be able to call destory twice", async () => {
let avatarBalanceBefore = await cDAI.balanceOf(avatar.address);
let reserveBalanceBefore = await cDAI.balanceOf(goodReserve.address);
let encodedCall = web3.eth.abi.encodeFunctionCall(
{
name: "end",
type: "function",
inputs: []
},
[]
);
await controller.genericCall(goodReserve.address, encodedCall, avatar.address, 0);
let avatarBalanceAfter = await cDAI.balanceOf(avatar.address);
let reserveBalanceAfter = await cDAI.balanceOf(goodReserve.address);
let newMMOwner = await marketMaker.owner();
expect((avatarBalanceAfter - avatarBalanceBefore).toString()).to.be.equal("0");
expect(reserveBalanceAfter.toString()).to.be.equal(reserveBalanceBefore.toString());
expect(newMMOwner).to.be.equal(avatar.address);
});
});