From 40b0c7b8058affb7843b998b08b9075b48ed5ad0 Mon Sep 17 00:00:00 2001 From: PremSOMISH Date: Tue, 7 Jul 2020 18:19:41 +0530 Subject: [PATCH 1/4] Changes in mock files --- contracts/mocks/mDAIMock.sol | 61 ++++++++++++++++++ .../mocks/GoodCompoundStakingMock.sol | 37 +++++++++++ .../mocks/GoodCompoundStakingNoDonation.sol | 37 +++++++++++ .../contracts/mocks/mDAILowWorthMock.sol | 62 +++++++++++++++++++ .../contracts/mocks/mDAINonMintableMock.sol | 54 ++++++++++++++++ 5 files changed, 251 insertions(+) create mode 100644 contracts/mocks/mDAIMock.sol create mode 100644 stakingModel/contracts/mocks/GoodCompoundStakingMock.sol create mode 100644 stakingModel/contracts/mocks/GoodCompoundStakingNoDonation.sol create mode 100644 stakingModel/contracts/mocks/mDAILowWorthMock.sol create mode 100644 stakingModel/contracts/mocks/mDAINonMintableMock.sol diff --git a/contracts/mocks/mDAIMock.sol b/contracts/mocks/mDAIMock.sol new file mode 100644 index 00000000..b460e6e7 --- /dev/null +++ b/contracts/mocks/mDAIMock.sol @@ -0,0 +1,61 @@ +pragma solidity 0.5.4; + +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; + +import "../DSMath.sol"; + +contract mDAIMock is DSMath, ERC20, ERC20Detailed, Ownable { + ERC20 dai; + + uint256 exchangeRate = uint256(100e28).div(99); + + constructor(ERC20 _dai) + public + ERC20() + ERC20Detailed("DMM DAI", "mDAI", 18) + { + dai = _dai; + } + function mint(uint256 daiAmount) public returns (uint256) { + dai.transferFrom(msg.sender, address(this), daiAmount); + + _mint( + msg.sender, + rdiv(daiAmount, getCurrentExchangeRate()).div(1e9) + ); //div to reduce precision from RAY 1e27 to 1e18 precision of mDAI + return 0; + } + + function redeem(uint256 mdaiAmount) public returns (uint256) { + uint256 daiAmount = rmul( + mdaiAmount, + getCurrentExchangeRate().div(10) + ); + _burn(msg.sender, mdaiAmount); + dai.transfer(msg.sender, daiAmount); + return 0; + } + + function redeemUnderlying(uint256 daiAmount) public returns (uint256) { + uint256 mdaiAmount = rdiv(daiAmount, getCurrentExchangeRate()).div( + 1e9 + ); + _burn(msg.sender, mdaiAmount); + dai.transfer(msg.sender, daiAmount); + return 0; + } + + function exchangeRateCurrent() public returns (uint256) { + exchangeRate += uint256(1e28).div(100); + return exchangeRate; + } + + function getCurrentExchangeRate() public view returns (uint256) { + return exchangeRate; + } + +} diff --git a/stakingModel/contracts/mocks/GoodCompoundStakingMock.sol b/stakingModel/contracts/mocks/GoodCompoundStakingMock.sol new file mode 100644 index 00000000..a80c35a3 --- /dev/null +++ b/stakingModel/contracts/mocks/GoodCompoundStakingMock.sol @@ -0,0 +1,37 @@ +pragma solidity 0.5.4; + +import "../GoodCompoundStaking.sol"; + + +/** + * @title A GoodCompoundStaking mock. + * return different donation ratio on collect. + */ +contract GoodCompoundStakingMock is GoodCompoundStaking { + constructor( + address _token, + address _iToken, + address _fundManager, + uint256 _blockInterval, + Avatar _avatar, + Identity _identity + ) + public + GoodCompoundStaking(_token, _iToken, _fundManager, _blockInterval, _avatar, _identity) + {} + + function collectUBIInterest(address recipient) + public + onlyFundManager + returns ( + uint256, + uint256, + uint256, + uint32 + ) + { + (uint256 iTokenGains, uint256 tokenGains, uint256 precisionLossToken, ) = super + .collectUBIInterest(recipient); + return (iTokenGains, tokenGains, precisionLossToken, 8e5); + } +} \ No newline at end of file diff --git a/stakingModel/contracts/mocks/GoodCompoundStakingNoDonation.sol b/stakingModel/contracts/mocks/GoodCompoundStakingNoDonation.sol new file mode 100644 index 00000000..7d112300 --- /dev/null +++ b/stakingModel/contracts/mocks/GoodCompoundStakingNoDonation.sol @@ -0,0 +1,37 @@ +pragma solidity 0.5.4; + +import "../GoodCompoundStaking.sol"; + + +/** + * @title A GoodCompoundStaking mock. + * return 0 donation ratio on collect. + */ +contract GoodCompoundStakingNoDonation is GoodCompoundStaking { + constructor( + address _token, + address _iToken, + address _fundManager, + uint256 _blockInterval, + Avatar _avatar, + Identity _identity + ) + public + GoodCompoundStaking(_token, _iToken, _fundManager, _blockInterval, _avatar, _identity) + {} + + function collectUBIInterest(address recipient) + public + onlyFundManager + returns ( + uint256, + uint256, + uint256, + uint32 + ) + { + (uint256 iTokenGains, uint256 tokenGains, uint256 precisionLossToken, ) = super + .collectUBIInterest(recipient); + return (iTokenGains, tokenGains, precisionLossToken, 0); + } +} \ No newline at end of file diff --git a/stakingModel/contracts/mocks/mDAILowWorthMock.sol b/stakingModel/contracts/mocks/mDAILowWorthMock.sol new file mode 100644 index 00000000..ffaf41b0 --- /dev/null +++ b/stakingModel/contracts/mocks/mDAILowWorthMock.sol @@ -0,0 +1,62 @@ +pragma solidity 0.5.4; + +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; + +import "../../../contracts/DSMath.sol"; + +contract mDAILowWorthMock is DSMath, ERC20, ERC20Detailed, Ownable { + ERC20 dai; + + uint256 exchangeRate = uint256(100e28).div(99); + + constructor(ERC20 _dai) + public + ERC20() + ERC20Detailed("DMM DAI", "mDAI", 18) + { + dai = _dai; + } + function mint(uint256 daiAmount) public returns (uint256) { + dai.transferFrom(msg.sender, address(this), daiAmount); + + _mint( + msg.sender, + rdiv(daiAmount, exchangeRateStored()).div(1e9) + ); + return 0; + } + + function redeem(uint256 mdaiAmount) public returns (uint256) { + uint256 daiAmount = rmul( + mdaiAmount, + exchangeRateStored().div(10) + ); + + _burn(msg.sender, mdaiAmount); + dai.transfer(msg.sender, daiAmount); + return 0; + } + + function redeemUnderlying(uint256 daiAmount) public returns (uint256) { + uint256 mdaiAmount = rdiv(daiAmount, exchangeRateStored().mul(2)).div( + 1e9 + ); + _burn(msg.sender, mdaiAmount); + dai.transfer(msg.sender, daiAmount.div(2)); + return 0; + } + + function exchangeRateCurrent() public returns (uint256) { + exchangeRate += uint256(1e28).div(100); + return exchangeRate; + } + + function exchangeRateStored() public view returns (uint256) { + return exchangeRate.div(2); + } + +} diff --git a/stakingModel/contracts/mocks/mDAINonMintableMock.sol b/stakingModel/contracts/mocks/mDAINonMintableMock.sol new file mode 100644 index 00000000..338067b8 --- /dev/null +++ b/stakingModel/contracts/mocks/mDAINonMintableMock.sol @@ -0,0 +1,54 @@ +pragma solidity 0.5.4; + +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; + +import "../../../contracts/DSMath.sol"; + + +contract mDAINonMintableMock is DSMath, ERC20, ERC20Detailed, Ownable { + ERC20 dai; + + uint256 exchangeRate = uint256(100e28).div(99); + + constructor(ERC20 _dai) public ERC20() ERC20Detailed("DMM DAI", "mDAI", 8) { + dai = _dai; + } + + function mint(uint256 daiAmount) public returns (uint256) { + dai.transferFrom(msg.sender, address(this), daiAmount); + + _mint(msg.sender, rdiv(daiAmount, exchangeRateStored()).div(1e9)); + return 1; + } + + function redeem(uint256 mdaiAmount) public returns (uint256) { + uint256 daiAmount = rmul( + mdaiAmount, + exchangeRateStored().div(10) + ); + + _burn(msg.sender, mdaiAmount); + dai.transfer(msg.sender, daiAmount); + return 0; + } + + function redeemUnderlying(uint256 daiAmount) public returns (uint256) { + uint256 mdaiAmount = rdiv(daiAmount, exchangeRateStored()).div(1e9); + _burn(msg.sender, mdaiAmount); + dai.transfer(msg.sender, daiAmount); + return 0; + } + + function exchangeRateCurrent() public returns (uint256) { + exchangeRate += uint256(1e28).div(100); + return exchangeRate; + } + + function exchangeRateStored() public view returns (uint256) { + return exchangeRate; + } +} From c0d814af1b310026bcf454a704404ada44168943 Mon Sep 17 00:00:00 2001 From: PremSOMISH Date: Thu, 9 Jul 2020 11:05:52 +0530 Subject: [PATCH 2/4] Adding mock file or mDai --- contracts/mocks/mDAIMock.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/mocks/mDAIMock.sol b/contracts/mocks/mDAIMock.sol index b460e6e7..6e601551 100644 --- a/contracts/mocks/mDAIMock.sol +++ b/contracts/mocks/mDAIMock.sol @@ -11,7 +11,7 @@ import "../DSMath.sol"; contract mDAIMock is DSMath, ERC20, ERC20Detailed, Ownable { ERC20 dai; - uint256 exchangeRate = uint256(100e28).div(99); + uint256 exchangeRate = uint256(100e20).div(99); constructor(ERC20 _dai) public @@ -22,12 +22,12 @@ contract mDAIMock is DSMath, ERC20, ERC20Detailed, Ownable { } function mint(uint256 daiAmount) public returns (uint256) { dai.transferFrom(msg.sender, address(this), daiAmount); - + uint mintAmount = rdiv(daiAmount, getCurrentExchangeRate()).div(1e9); //div to reduce precision from RAY 1e27 to 1e18 precision of mDAI _mint( msg.sender, - rdiv(daiAmount, getCurrentExchangeRate()).div(1e9) - ); //div to reduce precision from RAY 1e27 to 1e18 precision of mDAI - return 0; + mintAmount + ); + return mintAmount; } function redeem(uint256 mdaiAmount) public returns (uint256) { @@ -37,7 +37,7 @@ contract mDAIMock is DSMath, ERC20, ERC20Detailed, Ownable { ); _burn(msg.sender, mdaiAmount); dai.transfer(msg.sender, daiAmount); - return 0; + return daiAmount; } function redeemUnderlying(uint256 daiAmount) public returns (uint256) { @@ -46,7 +46,7 @@ contract mDAIMock is DSMath, ERC20, ERC20Detailed, Ownable { ); _burn(msg.sender, mdaiAmount); dai.transfer(msg.sender, daiAmount); - return 0; + return daiAmount; } function exchangeRateCurrent() public returns (uint256) { @@ -58,4 +58,4 @@ contract mDAIMock is DSMath, ERC20, ERC20Detailed, Ownable { return exchangeRate; } -} +} \ No newline at end of file From 6c81e8be7b9691b42bf4322b4d8335aa410f7ab6 Mon Sep 17 00:00:00 2001 From: PremSOMISH Date: Thu, 9 Jul 2020 11:07:14 +0530 Subject: [PATCH 3/4] Adding mock file for mDai Low worth and Non mintable --- stakingModel/contracts/mocks/mDAILowWorthMock.sol | 12 ++++++------ stakingModel/contracts/mocks/mDAINonMintableMock.sol | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stakingModel/contracts/mocks/mDAILowWorthMock.sol b/stakingModel/contracts/mocks/mDAILowWorthMock.sol index ffaf41b0..2e2379cf 100644 --- a/stakingModel/contracts/mocks/mDAILowWorthMock.sol +++ b/stakingModel/contracts/mocks/mDAILowWorthMock.sol @@ -22,12 +22,12 @@ contract mDAILowWorthMock is DSMath, ERC20, ERC20Detailed, Ownable { } function mint(uint256 daiAmount) public returns (uint256) { dai.transferFrom(msg.sender, address(this), daiAmount); - + uint mintAmount = rdiv(daiAmount, exchangeRateStored()).div(1e9); _mint( msg.sender, - rdiv(daiAmount, exchangeRateStored()).div(1e9) + mintAmount ); - return 0; + return mintAmount; } function redeem(uint256 mdaiAmount) public returns (uint256) { @@ -38,7 +38,7 @@ contract mDAILowWorthMock is DSMath, ERC20, ERC20Detailed, Ownable { _burn(msg.sender, mdaiAmount); dai.transfer(msg.sender, daiAmount); - return 0; + return daiAmount; } function redeemUnderlying(uint256 daiAmount) public returns (uint256) { @@ -47,7 +47,7 @@ contract mDAILowWorthMock is DSMath, ERC20, ERC20Detailed, Ownable { ); _burn(msg.sender, mdaiAmount); dai.transfer(msg.sender, daiAmount.div(2)); - return 0; + return daiAmount; } function exchangeRateCurrent() public returns (uint256) { @@ -59,4 +59,4 @@ contract mDAILowWorthMock is DSMath, ERC20, ERC20Detailed, Ownable { return exchangeRate.div(2); } -} +} \ No newline at end of file diff --git a/stakingModel/contracts/mocks/mDAINonMintableMock.sol b/stakingModel/contracts/mocks/mDAINonMintableMock.sol index 338067b8..ab7f04f6 100644 --- a/stakingModel/contracts/mocks/mDAINonMintableMock.sol +++ b/stakingModel/contracts/mocks/mDAINonMintableMock.sol @@ -20,9 +20,9 @@ contract mDAINonMintableMock is DSMath, ERC20, ERC20Detailed, Ownable { function mint(uint256 daiAmount) public returns (uint256) { dai.transferFrom(msg.sender, address(this), daiAmount); - - _mint(msg.sender, rdiv(daiAmount, exchangeRateStored()).div(1e9)); - return 1; + uint mintAmount = rdiv(daiAmount, exchangeRateStored()).div(1e9); + _mint(msg.sender, mintAmount); + return mintAmount; } function redeem(uint256 mdaiAmount) public returns (uint256) { @@ -33,14 +33,14 @@ contract mDAINonMintableMock is DSMath, ERC20, ERC20Detailed, Ownable { _burn(msg.sender, mdaiAmount); dai.transfer(msg.sender, daiAmount); - return 0; + return daiAmount; } function redeemUnderlying(uint256 daiAmount) public returns (uint256) { uint256 mdaiAmount = rdiv(daiAmount, exchangeRateStored()).div(1e9); _burn(msg.sender, mdaiAmount); dai.transfer(msg.sender, daiAmount); - return 0; + return daiAmount; } function exchangeRateCurrent() public returns (uint256) { From b6f38d87ac2cfbfb728b73f04a0db37c0731f48f Mon Sep 17 00:00:00 2001 From: PremSOMISH Date: Thu, 9 Jul 2020 11:08:15 +0530 Subject: [PATCH 4/4] Changes in test file --- stakingModel/test/GoodFundManager.e2e.test.js | 62 +++++++++++++++---- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/stakingModel/test/GoodFundManager.e2e.test.js b/stakingModel/test/GoodFundManager.e2e.test.js index b9b4f03c..b4cf96de 100644 --- a/stakingModel/test/GoodFundManager.e2e.test.js +++ b/stakingModel/test/GoodFundManager.e2e.test.js @@ -1,6 +1,8 @@ -const SimpleDAIStaking = artifacts.require("SimpleDAIStaking"); +const GoodCompoundStaking = artifacts.require("GoodCompoundStaking"); +const GoodDMMStaking = artifacts.require("GoodDMMStaking"); const DAIMock = artifacts.require("DAIMock"); const cDAIMock = artifacts.require("cDAIMock"); +const mDAIMock = artifacts.require("mDAIMock"); const GoodReserve = artifacts.require("GoodReserveCDai"); const MarketMaker = artifacts.require("GoodMarketMaker"); const GoodDollar = artifacts.require("GoodDollar"); @@ -40,7 +42,9 @@ async function proposeAndRegister( contract("GoodFundManager - network e2e tests", ([founder, staker]) => { let dai, cDAI, - simpleStaking, + mDAI, + goodCompoundStaking, + goodDMMStaking, goodReserve, goodFundManager, goodDollar, @@ -63,7 +67,9 @@ contract("GoodFundManager - network e2e tests", ([founder, staker]) => { ubiBridgeRecipient = staking_addresses.UBIScheme; dai = await DAIMock.at(staking_addresses.DAI); cDAI = await cDAIMock.at(staking_addresses.cDAI); - simpleStaking = await SimpleDAIStaking.at(staking_addresses.DAIStaking); + mDAI = await mDAIMock.at(staking_addresses.mDAI); + goodCompoundStaking = await GoodCompoundStaking.at(staking_addresses.DAICompoundStaking); + goodDMMStaking = await GoodDMMStaking.at(staking_addresses.DAIDMMStaking); goodReserve = await GoodReserve.at(staking_addresses.Reserve); goodFundManager = await GoodFundsManager.at(staking_addresses.FundManager); marketMaker = await MarketMaker.at(staking_addresses.MarketMaker); @@ -77,17 +83,26 @@ contract("GoodFundManager - network e2e tests", ([founder, staker]) => { goodReserve.address ); - await dai.mint(staker, web3.utils.toWei("100", "ether")); - await dai.approve(simpleStaking.address, web3.utils.toWei("100", "ether"), { + await dai.mint(staker, web3.utils.toWei("200", "ether")); + await dai.approve(goodCompoundStaking.address, web3.utils.toWei("100", "ether"), { from: staker }); - await simpleStaking - .stakeDAI(web3.utils.toWei("100", "ether"), { + await dai.approve(goodDMMStaking.address, web3.utils.toWei("100", "ether"), { + from: staker + }); + await goodCompoundStaking + .stake(web3.utils.toWei("100", "ether"), { + from: staker + }) + .catch(console.log); + await goodDMMStaking + .stake(web3.utils.toWei("100", "ether"), { from: staker }) .catch(console.log); await cDAI.exchangeRateCurrent(); await cDAI.exchangeRateStored(); + await mDAI.exchangeRateCurrent(); }); it("should be able to set the reserve", async () => { @@ -106,22 +121,45 @@ contract("GoodFundManager - network e2e tests", ([founder, staker]) => { expect(reserve1).to.be.equal(goodReserve.address); }); - it("should not mint UBI if not in the interval", async () => { + it("should not mint UBI if not in the interval(Compound)", async () => { + const error = await goodFundManager + .transferInterest(goodCompoundStaking.address) + .catch(e => e); + expect(error.message).to.have.string("wait for the next interval"); + }); + + it("should not mint UBI if not in the interval(DMM)", async () => { const error = await goodFundManager - .transferInterest(simpleStaking.address) + .transferInterest(goodDMMStaking.address) .catch(e => e); expect(error.message).to.have.string("wait for the next interval"); }); - it("should collect the interest and transfer it to the reserve and the bridge recipient should recieves minted gd", async () => { + it("should collect the interest and transfer it to the reserve and the bridge recipient should recieves minted gd (Compound)", async () => { await next_interval(await goodFundManager.blockInterval()); await cDAI.exchangeRateCurrent(); let recipientBefore = await goodDollar.balanceOf(ubiBridgeRecipient); const gdPriceBefore = await marketMaker.currentPrice(cDAI.address); - await goodFundManager.transferInterest(simpleStaking.address); + await goodFundManager.transferInterest(goodCompoundStaking.address); const gdPriceAfter = await marketMaker.currentPrice(cDAI.address); let recipientAfter = await goodDollar.balanceOf(ubiBridgeRecipient); - let stakingGDBalance = await goodDollar.balanceOf(simpleStaking.address); + let stakingGDBalance = await goodDollar.balanceOf(goodCompoundStaking.address); + expect(stakingGDBalance.toString()).to.be.equal("0"); //100% of interest is donated, so nothing is returned to staking + expect(recipientAfter.sub(recipientBefore).toString()).to.be.equal("1904085"); // total of interest + minted from expansion (received 100%) + expect(Math.floor(gdPriceAfter.toNumber() / 100).toString()).to.be.equal( + Math.floor(gdPriceBefore.toNumber() / 100).toString() + ); + }); + + it("should collect the interest and transfer it to the reserve and the bridge recipient should recieves minted gd (DMM)", async () => { + await next_interval(await goodFundManager.blockInterval()); + await mDAI.exchangeRateCurrent(); + let recipientBefore = await goodDollar.balanceOf(ubiBridgeRecipient); + const gdPriceBefore = await marketMaker.currentPrice(mDAI.address); + await goodFundManager.transferInterest(goodDMMStaking.address); + const gdPriceAfter = await marketMaker.currentPrice(mDAI.address); + let recipientAfter = await goodDollar.balanceOf(ubiBridgeRecipient); + let stakingGDBalance = await goodDollar.balanceOf(goodDMMStaking.address); expect(stakingGDBalance.toString()).to.be.equal("0"); //100% of interest is donated, so nothing is returned to staking expect(recipientAfter.sub(recipientBefore).toString()).to.be.equal("1904085"); // total of interest + minted from expansion (received 100%) expect(Math.floor(gdPriceAfter.toNumber() / 100).toString()).to.be.equal(