@gooddollar/goodprotocol
Version:
GoodDollar Protocol
1,143 lines (1,061 loc) • 50 kB
text/typescript
import { default as hre, ethers, upgrades } from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { BigNumber, Contract, Signer } from "ethers";
import { expect } from "chai";
import { GoodMarketMaker } from "../../types";
import { createDAO, increaseTime, advanceBlocks } from "../helpers";
import ContributionCalculation from "@gooddollar/goodcontracts/stakingModel/build/contracts/ContributionCalculation.json";
const BN = ethers.BigNumber;
export const NULL_ADDRESS = ethers.constants.AddressZero;
export const BLOCK_INTERVAL = 1;
describe("GoodReserve - staking with cDAI mocks", () => {
let dai: Contract;
let cDAI;
let goodReserve: Contract;
let goodDollar,
avatar,
identity,
marketMaker: GoodMarketMaker,
contribution,
controller,
founder,
staker,
schemeMock,
signers,
exchangeHelper: Contract,
setDAOAddress,
nameService,
runAsAvatarOnly;
before(async () => {
[founder, staker, ...signers] = await ethers.getSigners();
schemeMock = signers.pop();
let {
controller: ctrl,
avatar: av,
gd,
identity,
daoCreator,
nameService: ns,
setDAOAddress: sda,
setSchemes,
marketMaker: mm,
daiAddress,
cdaiAddress,
reserve,
runAsAvatarOnly: raao
} = await loadFixture(createDAO);
dai = await ethers.getContractAt("DAIMock", daiAddress);
cDAI = await ethers.getContractAt("cDAIMock", cdaiAddress);
avatar = av;
controller = ctrl;
setDAOAddress = sda;
nameService = ns;
runAsAvatarOnly = raao;
await setDAOAddress("UNISWAP_ROUTER", signers[0].address);
const exchangeHelperFactory = await ethers.getContractFactory(
"ExchangeHelper"
);
exchangeHelper = await upgrades.deployProxy(
exchangeHelperFactory,
[nameService.address],
{ kind: "uups" }
);
await exchangeHelper.setAddresses();
await setDAOAddress("EXCHANGE_HELPER", exchangeHelper.address);
console.log("deployed dao", {
founder: founder.address,
gd,
identity,
controller,
avatar
});
goodDollar = await ethers.getContractAt("IGoodDollar", gd);
contribution = await ethers.getContractAt(
ContributionCalculation.abi,
await nameService.getAddress("CONTRIBUTION_CALCULATION")
);
marketMaker = mm;
console.log("deployed contribution, deploying reserve...", {
founder: founder.address
});
goodReserve = reserve;
});
it("should get g$ minting permissions", async () => {
expect(await goodReserve.dao()).to.be.equal(controller);
expect(await goodReserve.avatar()).to.be.equal(avatar);
});
it("should set marketmaker in the reserve by avatar", async () => {
await setDAOAddress("MARKET_MAKER", marketMaker.address);
// const rFactory = await ethers.getContractFactory("GoodReserveCDai");
// const ctrl = await ethers.getContractAt(
// "Controller",
// controller,
// schemeMock
// );
// const encodedCall = rFactory.interface.encodeFunctionData(
// "setMarketMaker",
// [marketMaker.address]
// );
// await ctrl.genericCall(goodReserve.address, encodedCall, avatar, 0);
// const newMM = await goodReserve.marketMaker();
// expect(newMM.toString()).to.be.equal(marketMaker.address);
});
it("should set fundManager in the reserve by avatar", async () => {
await setDAOAddress("FUND_MANAGER", founder.address);
});
it("should returned fixed 0.0001 market price", async () => {
const gdPrice = await goodReserve["currentPrice()"]();
const cdaiWorthInGD = gdPrice.mul(BN.from("100000000"));
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 returned market price of 1gd in DAI", async () => {
const gdPrice = await goodReserve["currentPriceDAI()"]();
const daiWorthInGD = gdPrice.mul(BN.from("500000")); // 1gd = 0.000002 so 1DAI = 50k gd
const gdFloatPrice = gdPrice.toNumber() / 10 ** 18; //dai 18 decimals
expect(gdFloatPrice).to.be.equal(0.000002);
expect(daiWorthInGD.toString()).to.be.equal(ethers.utils.parseEther("1")); //in 18 decimals precision
//expect(daiWorthInGD.toNumber() / 10 ** 18).to.be.equal(9899.999999999999010000);
});
// it("should not be able to buy gd if the minter is not the reserve", async () => {
// let amount = 1e8;
// await dai["mint(uint256)"](ethers.utils.parseEther("100"));
// dai.approve(cDAI.address, ethers.utils.parseEther("100"));
// await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
// await cDAI.approve(goodReserve.address, amount);
// const gdBalanceBefore = await goodDollar.balanceOf(founder.address);
// const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
// let tx = (await goodReserve.buy(cDAI.address, amount, 0)).wait();
// const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
// const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
// expect(tx).to.be.revertedWith(/not minter/);
// expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
// expect(cDAIBalanceAfter.toString()).to.be.equal(
// cDAIBalanceBefore.toString()
// );
// // //for following tests
// // await goodDollar.addMinter(goodReserve.address);
// });
it("should 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 goodReserve["currentPrice()"]();
await increaseTime(24 * 60 * 60); //required for reserve ratio advance
const er = await cDAI.exchangeRateStored();
const daiAmount = ethers.utils
.parseEther("0.1")
.mul(BN.from("10").pow(10))
.mul(er)
.div(BN.from("10").pow(28));
await dai["mint(address,uint256)"](goodReserve.address, daiAmount);
const tx = await (
await goodReserve.mintUBI(daiAmount, 0, cDAI.address)
).wait();
const gdBalanceFund = await goodDollar.balanceOf(
await goodReserve.distributionHelper()
);
const gdPriceAfter = await goodReserve["currentPrice()"]();
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
const mintEvent = tx.events.find(_ => _.event === "UBIMinted");
console.log(
gdPriceBefore.toNumber(),
supplyAfter.toNumber(),
supplyBefore.toNumber()
);
expect(supplyAfter).to.be.equal(
mintEvent.args.gdInterestMinted
.add(mintEvent.args.gdExpansionMinted)
.add(supplyBefore)
);
// expected that the new reserve balance will include
// the new 1e18 cdai which transferred
expect(reserveBalanceAfter).to.be.equal(
reserveBalanceBefore.add(BN.from("10").pow(17))
);
// the new reserve ratio should be effected from the mintExpansion by:
// the daily change that was set up in the constructor (999388834642296)
// requires the time advance simulation above
expect(rrAfter.toString()).to.be.equal("999388");
// the price should be the same
expect(gdPriceAfter).to.be.equal(gdPriceBefore);
expect(gdBalanceFund).to.be.equal(
mintEvent.args.gdInterestMinted
.add(mintEvent.args.gdExpansionMinted)
.toString()
);
});
it("should not mint UBI if the reserve is not cDAI", async () => {
let error = await goodReserve.mintUBI(1, 0, dai.address).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 tx = goodReserve.connect(staker).mintUBI(0, 0, cDAI.address);
await expect(tx).to.be.revertedWith(/GoodReserve: not a minter/);
});
it("should be able to buy gd with DAI", async () => {
let daiAmount = ethers.utils.parseEther("100");
const cdaiRateStored = await cDAI.exchangeRateStored();
let amount = daiAmount
.div(BigNumber.from(10).pow(10))
.mul(BigNumber.from(10).pow(28))
.div(cdaiRateStored);
await dai["mint(uint256)"](daiAmount);
await dai.approve(exchangeHelper.address, daiAmount);
expect(await nameService.getAddress("DAI")).to.be.equal(dai.address);
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.address);
const daiBalanceBefore = await dai.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve["currentPrice()"]();
let transaction = await (
await exchangeHelper.buy([dai.address], daiAmount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const daiBalanceAfter = await dai.balanceOf(founder.address);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(
(cDAIBalanceReserveAfter - cDAIBalanceReserveBefore).toString()
).to.be.equal(amount.toString());
expect(
reserveBalanceAfter.sub(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(daiBalanceBefore.gt(daiBalanceAfter)).to.be.true;
expect(priceAfter.toString()).to.be.equal(priceBefore.toString());
expect(transaction.events.find(_ => _.event === "TokenPurchased")).to.be.not
.empty;
});
it("should be able to buy gd with cDAI through buy", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
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.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve["currentPrice()"]();
await cDAI.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(
(cDAIBalanceReserveAfter - cDAIBalanceReserveBefore).toString()
).to.be.equal(amount.toString());
expect(
reserveBalanceAfter.sub(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.events.find(_ => _.event === "TokenPurchased")).to.be.not
.empty;
});
it("should be able to buy gd with cDAI", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
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.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve["currentPrice()"]();
await cDAI.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(
(cDAIBalanceReserveAfter - cDAIBalanceReserveBefore).toString()
).to.be.equal(amount.toString());
expect(
reserveBalanceAfter.sub(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.events.find(_ => _.event === "TokenPurchased")).to.be.not
.empty;
});
it("should be able to buy gd with cDAI with generic amount of cdai tokens", async () => {
await dai["mint(uint256)"](ethers.utils.parseEther("4895"));
await dai.approve(cDAI.address, ethers.utils.parseEther("4895"));
let cdaibefore = await cDAI.balanceOf(founder.address);
await cDAI["mint(uint256)"](ethers.utils.parseEther("4895"));
let cdaiafter = await cDAI.balanceOf(founder.address);
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.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await cDAI.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
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.events.find(_ => _.event === "TokenPurchased")).to.be.not
.empty;
});
it("should be able to buy gd with other non initialized tokens beside cDAI", async () => {
let amount = ethers.utils.parseEther("1");
await dai["mint(uint256)"](amount);
await dai.approve(exchangeHelper.address, amount);
let tx = exchangeHelper.buy([dai.address], amount, 0, 0, NULL_ADDRESS);
await expect(tx).to.not.reverted;
});
it("should not be able to buy gd without cDAI allowance", async () => {
let amount = 1e8;
await cDAI.approve(exchangeHelper.address, "0");
let error = await exchangeHelper
.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS)
.catch(e => e);
expect(error.message).to.have.string("ERC20: insufficient allowance");
});
it("should not be able to buy gd without enough cDAI funds", async () => {
let amount = 1e8;
const cDAIBalanceBeforeTransfer = await cDAI.balanceOf(founder.address);
await cDAI.transfer(staker.address, cDAIBalanceBeforeTransfer.toString());
await cDAI.approve(exchangeHelper.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
let error = await exchangeHelper
.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS)
.catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
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
.connect(staker)
.transfer(founder.address, cDAIBalanceBeforeTransfer.toString());
});
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(exchangeHelper.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
let error = await exchangeHelper
.buy([cDAI.address], amount, 2000000, 0, NULL_ADDRESS)
.catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
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 = BN.from("10000");
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.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await goodDollar.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
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 bancor = await ethers.getContractAt(
"BancorFormula",
await marketMaker.getBancor()
);
const expectedReturn = await bancor.calculateSaleReturn(
supplyBefore.toString(),
reserveBalanceBefore.toString(),
reserveRatio.toString(),
amount
);
expect(cDAIBalanceAfter - cDAIBalanceBefore).to.be.equal(expectedReturn);
expect(cDAIBalanceReserveBefore - cDAIBalanceReserveAfter).to.be.equal(
expectedReturn
);
expect(reserveBalanceBefore.sub(reserveBalanceAfter)).to.be.equal(
expectedReturn
);
// 1e4 gd sold (burn from the supply)
expect(supplyBefore.sub(supplyAfter)).to.be.equal(amount);
expect(gdBalanceBefore.gt(gdBalanceAfter)).to.be.true;
expect(cDAIBalanceAfter.gt(cDAIBalanceBefore)).to.be.true;
expect(transaction.events.find(_ => _.event === "TokenSold")).to.be.not
.empty;
});
it("should be able to sell gd to cDAI without contribution through sell function", async () => {
let amount = BN.from("10000");
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.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await goodDollar.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
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 bancor = await ethers.getContractAt(
"BancorFormula",
await marketMaker.getBancor()
);
const expectedReturn = await bancor.calculateSaleReturn(
supplyBefore.toString(),
reserveBalanceBefore.toString(),
reserveRatio.toString(),
amount
);
expect(cDAIBalanceAfter - cDAIBalanceBefore).to.be.equal(expectedReturn);
expect(cDAIBalanceReserveBefore - cDAIBalanceReserveAfter).to.be.equal(
expectedReturn
);
expect(reserveBalanceBefore.sub(reserveBalanceAfter)).to.be.equal(
expectedReturn
);
// 1e4 gd sold (burn from the supply)
expect(supplyBefore.sub(supplyAfter)).to.be.equal(amount);
expect(gdBalanceBefore.gt(gdBalanceAfter)).to.be.true;
expect(cDAIBalanceAfter.gt(cDAIBalanceBefore)).to.be.true;
expect(transaction.events.find(_ => _.event === "TokenSold")).to.be.not
.empty;
});
it("should be able to sell gd to DAI without contribution through sell function", async () => {
let amount = BN.from("10000");
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.address);
const daiBalanceBefore = await dai.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await goodDollar.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.sell([dai.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const daiBalanceAfter = await dai.balanceOf(founder.address);
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 bancor = await ethers.getContractAt(
"BancorFormula",
await marketMaker.getBancor()
);
const expectedReturn = await bancor.calculateSaleReturn(
supplyBefore.toString(),
reserveBalanceBefore.toString(),
reserveRatio.toString(),
amount
);
// expect(cDAIBalanceAfter - cDAIBalanceBefore).to.be.equal(expectedReturn);
// expect(cDAIBalanceReserveBefore - cDAIBalanceReserveAfter).to.be.equal(
// expectedReturn
// );
//expect(reserveBalanceBefore.sub(reserveBalanceAfter)).to.be.equal(
// expectedReturn
// );
// 1e4 gd sold (burn from the supply)
//expect(supplyBefore.sub(supplyAfter)).to.be.equal(amount);
//expect(gdBalanceBefore.gt(gdBalanceAfter)).to.be.true;
expect(daiBalanceAfter.gt(daiBalanceBefore)).to.be.true;
expect(transaction.events.find(_ => _.event === "TokenSold")).to.be.not
.empty;
});
it("should set sell contribution ratio by avatar", async () => {
let nom = ethers.utils.parseUnits("2", 14);
let denom = ethers.utils.parseUnits("1", 15);
let ccFactory = await ethers.getContractFactory(
ContributionCalculation.abi,
ContributionCalculation.bytecode
);
let encodedCall = ccFactory.interface.encodeFunctionData(
"setContributionRatio",
[nom, denom]
);
const ctrl = await ethers.getContractAt(
"Controller",
controller,
schemeMock
);
await ctrl.genericCall(contribution.address, encodedCall, avatar, 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]
// );
// const ctrl = await ethers.getContractAt(
// "Controller",
// controller,
// schemeMock
// );
// await ctrl.genericCall(
// goodReserve.address,
// encodedCall,
// avatar,
// 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]
// );
// const ctrl = await ethers.getContractAt(
// "Controller",
// controller,
// schemeMock
// );
// await ctrl.genericCall(
// goodReserve.address,
// encodedCall,
// avatar,
// 0
// );
// newAddress = await goodReserve.contribution();
// expect(newAddress).to.be.equal(currentAddress);
// });
it("should calculate the sell contribution", async () => {
let nom = ethers.utils.parseUnits("2", 14);
let denom = ethers.utils.parseUnits("1", 15);
let actual = await contribution.calculateContribution(
marketMaker.address,
goodReserve.address,
founder.address,
cDAI.address,
1e4
);
expect(actual).to.be.equal(nom.mul(BN.from("10000")).div(denom));
});
it("should be able to sell gd to cDAI with contribution of 20%", async () => {
let amount = 1e4; // 100 gd
await goodDollar.transfer(staker.address, amount);
// 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(staker.address);
const cDAIBalanceBefore = await cDAI.balanceOf(staker.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve["currentPrice()"]();
await goodDollar.connect(staker).approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper
.connect(staker)
.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(staker.address);
const cDAIBalanceAfter = await cDAI.balanceOf(staker.address);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(cDAIBalanceAfter.sub(cDAIBalanceBefore)).to.be.equal(
expectedReturn,
"seller return mismatch"
);
expect(cDAIBalanceReserveBefore.sub(cDAIBalanceReserveAfter)).to.be.equal(
expectedReturn,
"reserve balance mismatch"
);
expect(reserveBalanceBefore.sub(reserveBalanceAfter)).to.be.equal(
expectedReturn,
"reserve token data mismatch"
);
expect(supplyBefore.sub(supplyAfter)).to.be.equal(amount);
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.events.find(_ => _.event === "TokenSold")).to.be.not
.empty;
});
it("should able to sell gd to DAI or cDAI token", async () => {
let amount = 1e4;
await goodDollar.approve(exchangeHelper.address, amount);
let tx = exchangeHelper.sell([dai.address], amount, 0, 0, NULL_ADDRESS);
await expect(tx).to.not.reverted;
});
it("should not be able to sell gd without gd allowance", async () => {
let amount = 1e4;
await goodDollar.approve(exchangeHelper.address, "0");
const gdBalanceBefore = await goodDollar.balanceOf(founder.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
let error = await exchangeHelper
.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS)
.catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.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 not be able to sell gd without enough gd funds", async () => {
let amount = 1e4;
const gdBalanceBeforeTransfer = await goodDollar.balanceOf(founder.address);
//reset gd holdings
await goodDollar.transfer(
staker.address,
gdBalanceBeforeTransfer.toString()
);
await goodDollar.approve(exchangeHelper.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
let error = await exchangeHelper
.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS)
.catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
expect(error.message).not.to.be.empty;
expect(gdBalanceAfter.toString()).to.be.equal(gdBalanceBefore.toString());
expect(cDAIBalanceAfter.toString()).to.be.equal(
cDAIBalanceBefore.toString()
);
//restore gd holdings
await goodDollar
.connect(staker)
.transfer(founder.address, gdBalanceBeforeTransfer.toString());
});
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(exchangeHelper.address, amount);
const gdBalanceBefore = await goodDollar.balanceOf(founder.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
let error = await exchangeHelper
.sell([cDAI.address], amount, 2000000, 0, NULL_ADDRESS)
.catch(e => e);
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
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 () => {
let error = await goodReserve.recover(cDAI.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 reserve has some balance", async () => {
await dai["mint(address,uint256)"](
goodReserve.address,
ethers.utils.parseEther("100")
);
let reserveBalance = await dai.balanceOf(goodReserve.address);
const reserveFactory = await ethers.getContractFactory("GoodReserveCDai");
let encodedCall = reserveFactory.interface.encodeFunctionData("recover", [
dai.address
]);
const ctrl = await ethers.getContractAt(
"Controller",
controller,
schemeMock
);
await ctrl.genericCall(goodReserve.address, encodedCall, avatar, 0);
let recoveredBalance = await dai.balanceOf(avatar);
expect(recoveredBalance).to.be.equal(reserveBalance);
});
it("should not be able to destroy if not avatar", async () => {
let tx = goodReserve.end();
await expect(tx).to.revertedWith(/only avatar can call this method/);
});
it("should be able to buy gd with cDAI and reserve should be correct", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await cDAI.approve(exchangeHelper.address, amount);
await exchangeHelper.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
expect(cDAIBalanceReserveAfter.sub(cDAIBalanceReserveBefore)).to.be.equal(
amount.toString()
);
expect(reserveBalanceAfter.sub(reserveBalanceBefore)).to.be.equal(amount);
});
it("should be able to buy gd with cDAI directly through reserve", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
await cDAI.approve(goodReserve.address, ethers.utils.parseEther("10"));
const founderBalanceBeforeBuy = await goodDollar.balanceOf(founder.address);
await goodReserve.buy(amount, 0, founder.address);
const founderBalanceAfterBuy = await goodDollar.balanceOf(founder.address);
expect(founderBalanceAfterBuy).to.be.gt(founderBalanceBeforeBuy);
});
it("should not be able to buy gd through reserve when there is no enough allowance", async () => {
let amount = 1e8;
await cDAI.approve(goodReserve.address, 0);
const tx = await goodReserve.buy(amount, 0, founder.address).catch(e => e);
expect(tx.message).to.have.string("ERC20: insufficient allowance");
});
it("should be able to sell gd directly through reserve", async () => {
const cdaiBalanceBeforeSell = await cDAI.balanceOf(founder.address);
await goodDollar.approve(goodReserve.address, "100");
await goodReserve.sell("100", 0, founder.address, staker.address);
const cdaiBalanceAfterSell = await cDAI.balanceOf(founder.address);
expect(cdaiBalanceAfterSell).to.be.gt(cdaiBalanceBeforeSell);
});
it("should not be able to sell gd through reserve when there is no enough allowance", async () => {
await goodDollar.approve(goodReserve.address, "0");
const tx = await goodReserve
.sell("100", 0, founder.address, staker.address)
.catch(e => e);
expect(tx.message).to.be.not.empty;
});
it("seller parameter should not matter if caller is not exchange helper", async () => {
const stakerGDBalanceBeforeSell = await goodDollar.balanceOf(
staker.address
);
await goodDollar.approve(goodReserve.address, "100");
await goodReserve.sell("100", 0, founder.address, staker.address);
const stakerGDBalanceAfterSell = await goodDollar.balanceOf(staker.address);
expect(stakerGDBalanceAfterSell).to.be.equal(stakerGDBalanceBeforeSell);
});
it("should be able to buy gd with cDAI and the total gd should be increased", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let gdSupplyBefore = reserveToken.gdSupply;
await cDAI.approve(exchangeHelper.address, amount);
await exchangeHelper.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let gdSupplyAfter = reserveToken.gdSupply;
expect(gdSupplyAfter.gt(gdSupplyBefore)).to.be.true;
});
it("should be able to retain the precision when buying a low quantity of tokens", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
const priceBefore = await goodReserve["currentPrice()"]();
await cDAI.approve(exchangeHelper.address, amount);
await exchangeHelper.buy([cDAI.address], amount, 0, 0, NULL_ADDRESS);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(Math.floor(priceAfter.toNumber() / 100).toString()).to.be.equal(
Math.floor(priceBefore.toNumber() / 100).toString()
);
});
it("should be able to sell gd to cDAI and reserve should be correct", async () => {
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalance = reserveToken.reserveSupply;
let supply = reserveToken.gdSupply;
let amount = BN.from("10000");
await goodDollar.transfer(staker.address, amount);
const cDAIBalanceBefore = await cDAI.balanceOf(staker.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await goodDollar.connect(staker).approve(exchangeHelper.address, amount);
const transaction = await (
await exchangeHelper
.connect(staker)
.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS)
).wait();
const cDAIBalanceAfter = await cDAI.balanceOf(staker.address);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
// return = reserveBalance * (1 - (1 - sellAmount / supply) ^ (1000000 / reserveRatio))
// if reserve ratio is 100% so:
// return = reserve balance * (1 - (1 - sellAmount / supply))
// the contribution ratio is 20%
let expected =
parseInt(reserveBalance.toString()) *
(1 -
(1 - amount.toNumber() / parseInt(supply.toString())) **
(1000000 / reserveToken.reserveRatio));
expected = Math.ceil((0.8 * expected) / 100) * 100; //deduct 20% contribution, allow 5 points precission mismatch (due to bancor pow estimation?), match solidity no floating point
//expected = Math.floor(0.8 * expected);
expect(cDAIBalanceAfter.sub(cDAIBalanceBefore)).to.be.equal(expected);
expect(cDAIBalanceReserveBefore.sub(cDAIBalanceReserveAfter)).to.be.equal(
expected
);
});
it("should be able to buy gd with cDAI for some other address through buy", async () => {
let amount = 1e8;
await dai["mint(uint256)"](ethers.utils.parseEther("100"));
await dai.approve(cDAI.address, ethers.utils.parseEther("100"));
await cDAI["mint(uint256)"](ethers.utils.parseEther("100"));
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceBefore = reserveToken.reserveSupply;
let supplyBefore = reserveToken.gdSupply;
let rrBefore = reserveToken.reserveRatio;
const gdBalanceBefore = await goodDollar.balanceOf(staker.address);
const cDAIBalanceBefore = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
const priceBefore = await goodReserve["currentPrice()"]();
await cDAI.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.buy([cDAI.address], amount, 0, 0, staker.address)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
let rrAfter = reserveToken.reserveRatio;
const gdBalanceAfter = await goodDollar.balanceOf(staker.address);
const cDAIBalanceAfter = await cDAI.balanceOf(founder.address);
const cDAIBalanceReserveAfter = await cDAI.balanceOf(goodReserve.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(
(cDAIBalanceReserveAfter - cDAIBalanceReserveBefore).toString()
).to.be.equal(amount.toString());
expect(
reserveBalanceAfter.sub(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.events.find(_ => _.event === "TokenPurchased")).to.be.not
.empty;
});
it("should be able to sell gd to cDAI without contribution through sell function for some other address", async () => {
let amount = BN.from("10000");
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.address);
const cDAIBalanceBefore = await cDAI.balanceOf(staker.address);
const cDAIBalanceReserveBefore = await cDAI.balanceOf(goodReserve.address);
await goodDollar.approve(exchangeHelper.address, amount);
let transaction = await (
await exchangeHelper.sell([cDAI.address], amount, 0, 0, staker.address)
).wait();
reserveToken = await marketMaker.reserveTokens(cDAI.address);
let reserveBalanceAfter = reserveToken.reserveSupply;
let supplyAfter = reserveToken.gdSupply;
const gdBalanceAfter = await goodDollar.balanceOf(founder.address);
const cDAIBalanceAfter = await cDAI.balanceOf(staker.address);
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 bancor = await ethers.getContractAt(
"BancorFormula",
await marketMaker.getBancor()
);
const expectedReturn = await bancor.calculateSaleReturn(
supplyBefore.toString(),
reserveBalanceBefore.toString(),
reserveRatio.toString(),
amount
);
expect(cDAIBalanceAfter - cDAIBalanceBefore).to.be.equal(expectedReturn);
expect(cDAIBalanceReserveBefore - cDAIBalanceReserveAfter).to.be.equal(
expectedReturn
);
expect(reserveBalanceBefore.sub(reserveBalanceAfter)).to.be.equal(
expectedReturn
);
// 1e4 gd sold (burn from the supply)
expect(supplyBefore.sub(supplyAfter)).to.be.equal(amount);
expect(gdBalanceBefore.gt(gdBalanceAfter)).to.be.true;
expect(cDAIBalanceAfter.gt(cDAIBalanceBefore)).to.be.true;
expect(transaction.events.find(_ => _.event === "TokenSold")).to.be.not
.empty;
});
it("should be able to retain the precision when selling a low quantity of tokens", async () => {
let amount = 1e1;
let reserveToken = await marketMaker.reserveTokens(cDAI.address);
const priceBefore = await goodReserve["currentPrice()"]();
await goodDollar.approve(exchangeHelper.address, amount);
await exchangeHelper.sell([cDAI.address], amount, 0, 0, NULL_ADDRESS);
reserveToken = await marketMaker.reserveTokens(cDAI.address);
const priceAfter = await goodReserve["currentPrice()"]();
expect(Math.floor(priceAfter.toNumber() / 100).toString()).to.be.equal(
Math.floor(priceBefore.toNumber() / 100).toString()
);
});
//keep this test last as it ends the reserve
it("should transfer cDAI funds to the given destination and transfer marker maker ownership", async () => {
expect(await goodReserve.avatar()).to.equal(avatar);
let avatarBalanceBefore = await cDAI.balanceOf(avatar);
let reserveBalanceBefore = await cDAI.balanceOf(goodReserve.address);
const reserveFactory = await ethers.getContractFactory("GoodReserveCDai");
let encodedCall = reserveFactory.interface.encodeFunctionData("end");
const ctrl = await ethers.getContractAt(
"Controller",
controller,
schemeMock
);
const tx = await (
await ctrl.genericCall(goodReserve.address, encodedCall, avatar, 0)
).wait();
let avatarBalanceAfter = await cDAI.balanceOf(avatar);
let reserveBalanceAfter = await cDAI.balanceOf(goodReserve.address);
expect(avatarBalanceAfter.sub(avatarBalanceBefore)).to.be.equal(
reserveBalanceBefore
);
expect(reserveBalanceAfter.toString()).to.be.equal("0");
expect(await goodDollar.isMinter(goodReserve.address)).to.be.false;
expect(await goodDollar.isMinter(avatar)).to.be.true;
});
it("should set reserve ratio daily expansion by avatar", async () => {
let currentReserveRatioDailyExpansion =
await marketMaker.reserveRatioDailyExpansion();
console.log(currentReserveRatioDailyExpansion.toString());
await runAsAvatarOnly(
goodReserve,
"setReserveRatioDailyExpansion(uint256,uint256)",
1,
1e15
);
let newReserveRatioDailyExpansion =
await marketMaker.reserveRatioDailyExpansion();
console.log(newReserveRatioDailyExpansion.toString());
expect(newReserveRatioDailyExpansion).to.not.equal(
currentReserveRatioDailyExpansion
);
expect(newReserveRatioDailyExpansion).to.be.equal(BN.from("1000000000000"));
const encodedCall = goodReserve.interface.encodeFunctionData(
"setReserveRatioDailyExpansion",
[BN.from(currentReserveRatioDailyExpansion).div(1e12), 1e15]
);
const ctrl = await ethers.getContractAt(
"Controller",
controller,
schemeMock
);
await ctrl.genericCall(goodReserve.address, encodedCall, avatar,