From 89ccc493896cea1976cdec5481765a5f052a7c65 Mon Sep 17 00:00:00 2001 From: thomga Date: Tue, 28 Nov 2023 20:06:11 -0300 Subject: [PATCH] chore: refactor liquidate function and finish tests --- contracts/LiquidatePool.sol | 33 +- contracts/interfaces/ILiquidatePool.sol | 2 +- contracts/mocks/MockChainlink.sol | 2 +- contracts/mocks/MockPriceFeed.sol | 23 - contracts/rBRLLPool.sol | 43 +- test/common/allFixture.js | 6 +- test/rBRLLPool.js | 1105 ++++++++--------------- 7 files changed, 430 insertions(+), 784 deletions(-) delete mode 100644 contracts/mocks/MockPriceFeed.sol diff --git a/contracts/LiquidatePool.sol b/contracts/LiquidatePool.sol index 5c200db..6d7b2aa 100644 --- a/contracts/LiquidatePool.sol +++ b/contracts/LiquidatePool.sol @@ -97,37 +97,38 @@ contract LiquidatePool is AccessControl { /** * @dev liquidate a give amout of tselic on UniswapV3 - * @param tselicAmount the amout of tselic - * @param minReturn the minimum amount of return + * @param drexAmount the amout of drex to be liquidated + * @param amountInMaximum the maximum amount of tselic to be liquidated * @param receiver used to receive token */ function flashLiquidateTSELIC( - uint256 tselicAmount, - uint256 minReturn, + uint256 drexAmount, + uint256 amountInMaximum, address receiver - ) external { + ) external returns (uint256 amountLiquidated) { require(msg.sender == rbrllpool, "unauthorized"); - tselic.approve(address(swapRouter), tselicAmount); + tselic.approve(address(swapRouter), amountInMaximum); - // We set the sqrtPriceLimitx96 to be 0 to ensure we swap our exact input amount. - ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ + // We set the sqrtPriceLimitx96 to be 0 to ensure we swap our exact output amount. + ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({ tokenIn: address(tselic), tokenOut: address(drex), fee: 3000, - recipient: msg.sender, + recipient: address(this), deadline: block.timestamp, - amountIn: tselicAmount, - amountOutMinimum: minReturn, + amountOut: drexAmount, + amountInMaximum: amountInMaximum, sqrtPriceLimitX96: 0 }); - // The call to `exactInputSingle` executes the swap. - uint256 amountOut = swapRouter.exactInputSingle(params); - - uint256 feeAmount = amountOut.mul(liquidateFeeRate).div(FEE_COEFFICIENT); - uint256 amountAfterFee = amountOut.sub(feeAmount); + // The call to `exactOutputSingle` executes the swap. + amountLiquidated = swapRouter.exactOutputSingle(params); + uint256 leftover = amountInMaximum.sub(amountLiquidated); + uint256 feeAmount = drexAmount.mul(liquidateFeeRate).div(FEE_COEFFICIENT); + uint256 amountAfterFee = drexAmount.sub(feeAmount); drex.safeTransfer(receiver, amountAfterFee); drex.safeTransfer(feeCollector, feeAmount); + tselic.safeTransfer(address(rbrllpool), leftover); } } diff --git a/contracts/interfaces/ILiquidatePool.sol b/contracts/interfaces/ILiquidatePool.sol index fdd2911..45e2838 100644 --- a/contracts/interfaces/ILiquidatePool.sol +++ b/contracts/interfaces/ILiquidatePool.sol @@ -6,5 +6,5 @@ interface ILiquidatePool { uint256 tselicAmount, uint256 minReturn, address receiver - ) external; + ) external returns (uint256); } diff --git a/contracts/mocks/MockChainlink.sol b/contracts/mocks/MockChainlink.sol index c83c632..c9144a0 100644 --- a/contracts/mocks/MockChainlink.sol +++ b/contracts/mocks/MockChainlink.sol @@ -8,7 +8,7 @@ contract MockChainlink { constructor() { selicRate = 12250000; - unitValue = 14009949999999999803392; + unitValue = 14000000000000000000000; maturityTime = 1867014000; } diff --git a/contracts/mocks/MockPriceFeed.sol b/contracts/mocks/MockPriceFeed.sol deleted file mode 100644 index fd693f8..0000000 --- a/contracts/mocks/MockPriceFeed.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.18; - -contract MockPriceFeed { - // mock usdc to $1 - function latestAnswer() public pure returns (int256) { - return 100000000; - } - - function latestRoundData() - public - view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ) - { - return (1, 100000000, block.timestamp, block.timestamp, 1); - } -} diff --git a/contracts/rBRLLPool.sol b/contracts/rBRLLPool.sol index 35ebac7..4a8f32e 100644 --- a/contracts/rBRLLPool.sol +++ b/contracts/rBRLLPool.sol @@ -307,7 +307,7 @@ contract rBRLLPool is rBRLL, AccessControl, Pausable { * @param _amount the amount of DREX. */ function borrowDREX(uint256 _amount) external whenNotPaused realizeInterest { - require(_amount > 0, "Borrow DREX should be more thea 0."); + require(_amount > 0, "Borrow DREX should be more than 0."); // convert to rBRLL. uint256 convertTorBRLL = _amount.mul(10 ** 12); @@ -364,40 +364,39 @@ contract rBRLLPool is rBRLL, AccessControl, Pausable { * Emits a `LiquidationRecord` event. * * @param borrower The borrower be liquidated - * @param repayAmount The amount of the rBRLL to repay - * @param minReturn the minimum amount of return + * @param repayAmount The amount of the Drex to repay + * @param tselicInMaximum the maximum amount of tselic to be liquidated */ function flashLiquidateBorrow( address borrower, uint256 repayAmount, - uint256 minReturn + uint256 tselicInMaximum ) external whenNotPaused realizeInterest { require(liquidateProvider[borrower], "borrower is not a provider."); _liquidateProcedure(borrower, repayAmount); - liquidatePool.flashLiquidateTSELIC(repayAmount, minReturn, msg.sender); + tselic.transfer(address(liquidatePool), tselicInMaximum); + uint256 tselicAmount = liquidatePool.flashLiquidateTSELIC( + repayAmount, + tselicInMaximum, + msg.sender + ); + require( + depositedTSELIC[borrower] >= tselicAmount, + "repayAmount should be less than borrower's deposit." + ); + totalDepositedTSELIC -= tselicAmount; + depositedTSELIC[borrower] -= tselicAmount; emit LiquidationRecord(msg.sender, borrower, repayAmount, block.timestamp); } function _liquidateProcedure(address borrower, uint256 repayAmount) internal { require(msg.sender != borrower, "don't liquidate self."); - uint256 borrowedBRL = getBorrowrBRLLAmountByShares(borrowedShares[borrower]); - require(borrowedBRL >= repayAmount, "repayAmount should be less than borrower's debt."); - _burnrBRLL(msg.sender, repayAmount); - - _burnrBRLLDebt(borrower, repayAmount); - - // TODO verify the decimal places. - uint256 liquidateTSELIC = repayAmount.div(_tselicPrice()); - - require( - depositedTSELIC[borrower] >= liquidateTSELIC, - "repayAmount should be less than borrower's deposit." - ); - totalDepositedTSELIC -= liquidateTSELIC; - depositedTSELIC[borrower] -= liquidateTSELIC; - - tselic.transfer(address(liquidatePool), repayAmount); + uint256 borrowedrBRLL = getBorrowrBRLLAmountByShares(borrowedShares[borrower]); + uint256 convertToDREX = borrowedrBRLL.div(1e12) + 1; + require(convertToDREX >= repayAmount, "repayAmount should be less than borrower's debt."); + _burnrBRLL(msg.sender, borrowedrBRLL); + _burnrBRLLDebt(borrower, borrowedrBRLL); } /** diff --git a/test/common/allFixture.js b/test/common/allFixture.js index 1222543..fa629c3 100644 --- a/test/common/allFixture.js +++ b/test/common/allFixture.js @@ -79,11 +79,11 @@ async function deployTokensFixture(deployer, investor, investor2) { await drexToken.deployed() await tselicToken.deployed() - await drexToken.connect(deployer).transfer(investor.address, ethers.utils.parseUnits("1000", 6)) + await drexToken.connect(deployer).transfer(investor.address, ethers.utils.parseUnits("400000", 6)) await drexToken .connect(deployer) - .transfer(investor2.address, ethers.utils.parseUnits("1000", 6)) + .transfer(investor2.address, ethers.utils.parseUnits("400000", 6)) await tselicToken .connect(deployer) @@ -237,7 +237,7 @@ async function deployUniPoolFixture(deployer, tselicToken, drexToken) { amount0Min: 0, amount1Min: 0, recipient: deployer.address, - deadline: Math.floor(Date.now() / 1000) + 60 * 10, + deadline: 32532350693, } const tx = await nonfungiblePositionManager .connect(deployer) diff --git a/test/rBRLLPool.js b/test/rBRLLPool.js index 3b2228a..5a10319 100644 --- a/test/rBRLLPool.js +++ b/test/rBRLLPool.js @@ -1,5 +1,4 @@ -const { time, loadFixture } = require("@nomicfoundation/hardhat-network-helpers") -// const { ethers } = require("hardhat") +const { ethers } = require("hardhat") const { expect } = require("chai") const { BigNumber } = ethers @@ -45,20 +44,20 @@ describe("rBRLLPool", function () { // const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545") // ethers.provider = provider ;[admin, deployer, drexInvestor, tselicInvestor, feeCollector] = await ethers.getSigners() - // deploy tokens - ;({ drexToken, tselicToken } = await deployTokensFixture( - deployer, - drexInvestor, - tselicInvestor - )) + // deploy tokens + ; ({ drexToken, tselicToken } = await deployTokensFixture( + deployer, + drexInvestor, + tselicInvestor + )) swapRouter = await deployUniPoolFixture(deployer, tselicToken, drexToken) if (TEST_CHAINLINK) { - ;({ functionsAddresses, autoConsumerContract } = await deployLocalChainlinkFunctions( + ; ({ functionsAddresses, autoConsumerContract } = await deployLocalChainlinkFunctions( admin, deployer )) } else { - ;({ autoConsumerContract } = await deployMockPriceFeedFixture(deployer)) + ; ({ autoConsumerContract } = await deployMockPriceFeedFixture(deployer)) } rbrllpool = await deployrBRLLPoolFixture(admin, deployer, tselicToken, drexToken) @@ -82,9 +81,9 @@ describe("rBRLLPool", function () { await liquidatePool.connect(admin).setFeeCollector(feeCollector.address) now = (await ethers.provider.getBlock("latest")).timestamp }) - const amountToSupplyDrex = ethers.utils.parseUnits("100", 6) // 100 DREX + const amountToSupplyDrex = ethers.utils.parseUnits("14000", 6) // 100 DREX const amountToSupplyTSelic = ethers.utils.parseUnits("1", 18) // 1 TSELIC - const amountToBorrowDrex = ethers.utils.parseUnits("98", 6) // 98 DREX + const amountToBorrowDrex = ethers.utils.parseUnits("13000", 6) // 98 DREX describe("Chainlink Automation", function () { it("Check automation run", async () => { @@ -104,7 +103,7 @@ describe("rBRLLPool", function () { await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex) expect(await rbrllpool.balanceOf(drexInvestor.address)).to.be.equal( - ethers.utils.parseUnits("100", 18) + ethers.utils.parseUnits("14000", 18) ) }) @@ -135,709 +134,379 @@ describe("rBRLLPool", function () { }) }) - // describe("Withdraw", function () { - // beforeEach(async () => { - // now = now + ONE_HOUR - // await mineBlockWithTimestamp(ethers.provider, now) - // await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) - // await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex) - // await tselicToken.connect(tselicInvestor).approve(rbrllpool.address, amountToSupplyTSelic) - // await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic) - // }) - // describe("Withdraw DREX", function () { - // it("Should be able to withdraw", async function () { - // const drexAmountBefore = await drexToken.balanceOf(drexInvestor.address) - - // const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) - // await rbrllpool.connect(drexInvestor).withdrawDREX(amountToSupplyDrex) - - // const drexAmountAfter = await drexToken.balanceOf(drexInvestor.address) - - // expect(await rbrllpool.balanceOf(drexInvestor.address)).to.be.equal(0) - // expect(drexAmountAfter).to.be.equal(rbrllAmount.div(1e12).add(drexAmountBefore)) - // }) - - // it("Should be able to withdraw all drex", async function () { - // await rbrllpool.connect(tselicInvestor).borrowUSDC(amountToBorrowDrex) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rbrllpool.connect(admin).setReserveFactor(0) - - // // add interest - // await drexToken - // .connect(deployer) - // .transfer(rbrllpool.address, amountToBorrowDrex.mul(2)) - - // const drexAmountBefore = await drexToken.balanceOf(drexInvestor.address) - - // const rustpAmount = await rbrllpool.balanceOf(drexInvestor.address) - // await rbrllpool.connect(drexInvestor).withdrawAllDREX() - - // const drexAmountAfter = await drexToken.balanceOf(drexInvestor.address) - - // expect(await rbrllpool.balanceOf(drexInvestor.address)).to.be.equal(0) - // expect(drexAmountAfter).to.be.equal(rustpAmount.div(1e12).add(drexAmountBefore)) - // }) - - // it("Should fail if withdraw zero DREX", async function () { - // await expect(rbrllpool.connect(drexInvestor).withdrawDREX(0)).to.be.revertedWith( - // "Withdraw DREX should more then 0." - // ) - // }) - - // it("Should fail if withdraw more than supply", async function () { - // await expect( - // rbrllpool.connect(drexInvestor).withdrawDREX(amountToSupplyDrex + 1) - // ).to.be.revertedWith("BALANCE_EXCEEDED") - // }) - // }) - // describe("Withdraw TSELIC", function () { - // it("Should be able to withdraw", async function () { - // const tselicAmountBefore = await tselicToken.balanceOf(tselicInvestor.address) - // await rbrllpool.connect(tselicInvestor).withdrawTSELIC(amountToSupplyTSELIC) - - // const tselicAmountAfter = await tselicToken.balanceOf(tselicInvestor.address) - - // expect(await rbrllpool.depositedTSELIC(tselicInvestor.address)).to.be.equal(0) - // expect(tselicAmountAfter).to.be.equal(amountToSupplyTSELIC.add(tselicAmountBefore)) - // }) - - // it("Should be able to withdraw all tselic", async function () { - // const tselicAmountBefore = await tselicToken.balanceOf(tselicInvestor.address) - // await rbrllpool.connect(tselicInvestor).withdrawAllTSELIC() - - // const tselicAmountAfter = await tselicToken.balanceOf(tselicInvestor.address) - - // expect(await rbrllpool.depositedSharesTSELIC(tselicInvestor.address)).to.be.equal(0) - // expect(tselicAmountAfter).to.be.equal(amountToSupplyTSELIC.add(tselicAmountBefore)) - // }) - - // it("Should fail if supply zero TSELIC", async function () { - // await expect(rbrllpool.connect(tselicInvestor).withdrawTSELIC(0)).to.be.revertedWith( - // "Withdraw TSELIC should more then 0." - // ) - // }) - - // it("Should fail if withdraw more than supply", async function () { - // await expect(rbrllpool.connect(tselicInvestor).withdrawTSELIC(amountToSupplyTSELIC + 1)).to - // .be.reverted - // }) - // }) - // }) - // describe("Borrow", function () { - // beforeEach(async () => { - // now = now + ONE_HOUR - // await mineBlockWithTimestamp(ethers.provider, now) - // await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) - // await rbrllpool.connect(drexInvestor).supplyUSDC(amountToSupplyDrex) - // await tselicToken.connect(tselicInvestor).approve(rbrllpool.address, amountToSupplyTSelic) - // await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic) - // }) - // describe("Borrow Drex", function () { - // it("Should be able to borrow", async function () { - // const drexAmountBefore = await drexToken.balanceOf(tselicInvestor.address) - - // const borrowShares = await rbrllpool.getSharesByrBRLLAmount( - // amountToBorrowDrex.mul(1e12) - // ) - // await rbrllpool.connect(tselicInvestor).borrowDREX(amountToBorrowDrex) - - // const drexAmountAfter = await drexToken.balanceOf(tselicInvestor.address) - - // expect(await rbrllpool.getBorrowedSharesOf(tselicInvestor.address)).to.be.equal( - // borrowShares - // ) - // expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowShares) - // expect(drexAmountAfter).to.be.equal(amountToBorrowDrex.add(drexAmountBefore)) - // }) - - // it("Should be able to borrow more when TSELIC distribute", async function () { - // const drexAmountBefore = await drexToken.balanceOf(tselicInvestor.address) - - // const doubleBorrow = amountToBorrowDrex.mul(2) - // await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) - // await rbrllpool.connect(drexInvestor).supplyUSDC(amountToSupplyDrex) - // const totalSupplyTSELIC = await tselicToken.totalSupply() - // await tselicToken.connect(deployer).distributeInterests(totalSupplyTSELIC, now, now + 1) - - // const borrowShares = await rbrllpool.getSharesByrBRLLAmount(doubleBorrow.mul(1e12)) - // await rbrllpool.connect(tselicInvestor).borrowDREX(doubleBorrow) - - // const drexAmountAfter = await drexToken.balanceOf(tselicInvestor.address) - - // expect(await rbrllpool.getBorrowedSharesOf(tselicInvestor.address)).to.be.equal( - // borrowShares - // ) - // expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowShares) - // expect(drexAmountAfter).to.be.equal(doubleBorrow.add(drexAmountBefore)) - // }) - - // it("Should fail if borrow zero DREX", async function () { - // await expect(rbrllpool.connect(tselicInvestor).borrowDREX(0)).to.be.revertedWith( - // "Borrow DREX should more then 0." - // ) - // }) - - // it("Should fail if borrow more than collateral", async function () { - // await expect( - // rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) - // ).to.be.revertedWith("Cannot be lower than the safeCollateralRate.") - // }) - // }) - // }) - - // describe("Repay", function () { - // beforeEach(async () => { - // now = now + ONE_HOUR - // await mineBlockWithTimestamp(ethers.provider, now) - // // await interestRateModel.connect(deployer).setAPR(0) - // await usdcToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) - // await rbrllpool.connect(drexInvestor).supplyUSDC(amountToSupplyDrex) - // await stbtToken.connect(tselicInvestor).approve(rbrllpool.address, amountToSupplyTSelic) - // await rbrllpool.connect(tselicInvestor).supplySTBT(amountToSupplyTSelic) - - // await rbrllpool.connect(tselicInvestor).borrowUSDC(amountToBorrowUSDC) - // await usdcToken.connect(tselicInvestor).approve(rbrllpool.address, BIGNUMBER) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - // // to realize interest - // await rbrllpool.connect(admin).setReserveFactor(0) - // await interestRateModel.connect(deployer).setAPR(0) - // }) - // describe("Repay USDC", function () { - // it("Should be able to repay 50%", async function () { - // const usdcAmountBefore = await usdcToken.balanceOf(tselicInvestor.address) - - // const borrowSharesBefore = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) - // const borrowiUSDP = (await rbrllpool.getBorrowedAmount(tselicInvestor.address)).div(2) - - // const borrowUSDC = borrowiUSDP.div(1e12) - - // const repayShares = await rbrllpool.getBorrowSharesByrUSTPAmount(borrowiUSDP) - - // await rbrllpool.connect(tselicInvestor).repayUSDC(borrowUSDC) - - // const usdcAmountAfter = await usdcToken.balanceOf(tselicInvestor.address) - // const borrowSharesAfter = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) - - // expect(borrowSharesAfter).to.be.within( - // borrowSharesBefore.sub(repayShares), - // borrowSharesBefore.sub(repayShares).add(1e12) - // ) - // expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowSharesAfter) - // expect(usdcAmountBefore).to.be.equal(usdcAmountAfter.add(borrowUSDC)) - // }) - // it("Should be able to repay 100%", async function () { - // const usdcAmountBefore = await usdcToken.balanceOf(tselicInvestor.address) - - // const borrowSharesBefore = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) - // const borrowiUSDP = await rbrllpool.getBorrowedAmount(tselicInvestor.address) - - // const borrowUSDC = borrowiUSDP.div(1e12) - - // const repayShares = await rbrllpool.getBorrowSharesByrUSTPAmount(borrowiUSDP) - - // await rbrllpool.connect(tselicInvestor).repayUSDC(borrowUSDC) - - // const usdcAmountAfter = await usdcToken.balanceOf(tselicInvestor.address) - // const borrowSharesAfter = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) - - // expect(borrowSharesAfter).to.be.within( - // borrowSharesBefore.sub(repayShares), - // borrowSharesBefore.sub(repayShares).add(1e12) - // ) - // expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowSharesAfter) - // expect(usdcAmountBefore).to.be.equal(usdcAmountAfter.add(borrowUSDC)) - // }) - - // it("Should be able to repay 100% and user could with all usdc", async function () { - // const usdcAmountBefore = await usdcToken.balanceOf(tselicInvestor.address) - // const borrowiUSDP = await rbrllpool.getBorrowedAmount(tselicInvestor.address) - // const borrowUSDC = borrowiUSDP.div(1e12) - // await rbrllpool.connect(tselicInvestor).repayAll() - // const usdcAmountAfter = await usdcToken.balanceOf(tselicInvestor.address) - // // at repayAll function, the repay usdc should be add 1. - // expect(usdcAmountBefore).to.be.equal(usdcAmountAfter.add(borrowUSDC).add(1)) - // const interestUSDC = borrowUSDC.sub(amountToBorrowUSDC) - - // const balanceOfUserBefore = await usdcToken.balanceOf(drexInvestor.address) - // await rbrllpool.connect(drexInvestor).withdrawAllUSDC() - - // const balanceOfUserAfter = await usdcToken.balanceOf(drexInvestor.address) - // expect(balanceOfUserAfter).to.be.equal( - // balanceOfUserBefore.add(amountToSupplyUSDC).add(interestUSDC) - // ) - // }) - // it("Should fail if repay zero USDC", async function () { - // await expect(rbrllpool.connect(tselicInvestor).repayUSDC(0)).to.be.revertedWith( - // "Repay USDC should more then 0." - // ) - // }) - // }) - // }) - - // describe("Interest", function () { - // beforeEach(async () => { - // now = now + ONE_HOUR - // await mineBlockWithTimestamp(ethers.provider, now) - // await usdcToken.connect(usdcInvestor).approve(rustpool.address, amountToSupplyUSDC) - // await rustpool.connect(usdcInvestor).supplyUSDC(amountToSupplyUSDC) - // await stbtToken - // .connect(stbtInvestor) - // .approve(rustpool.address, amountToSupplySTBT.mul(2)) - // await rustpool.connect(stbtInvestor).supplySTBT(amountToSupplySTBT.mul(2)) - // }) - // describe("Gain interest", function () { - // it("Should be able to full interest when 100% utilization rate", async function () { - // // borrow all usdc - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - - // const rustpAmount = await rustpool.balanceOf(usdcInvestor.address) - - // // ~= 5.2% apr - // expect(rustpAmount.div(1e12)).to.be.within( - // amountToSupplyUSDC.mul(10510).div(10000), - // amountToSupplyUSDC.mul(10530).div(10000) - // ) - // }) - // it("Should be able to half interest when 50% utilization rate", async function () { - // // borrow all usdc - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC.div(2)) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - - // const rustpAmount = await rustpool.balanceOf(usdcInvestor.address) - - // // ~= 2.1% apr - // expect(rustpAmount.div(1e12)).to.be.within( - // amountToSupplyUSDC.mul(10255).div(10000), - // amountToSupplyUSDC.mul(10265).div(10000) - // ) - // }) - - // it("Should be able to withdraw interest income", async function () { - // // borrow all usdc - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - - // await usdcToken - // .connect(stbtInvestor) - // .approve(rustpool.address, amountToSupplyUSDC.mul(2)) - // await rustpool.connect(stbtInvestor).supplyUSDC(amountToSupplyUSDC.mul(2)) - - // const usdcAmountBefore = await usdcToken.balanceOf(usdcInvestor.address) - - // const rustpAmount = await rustpool.balanceOf(usdcInvestor.address) - // await rustpool.connect(usdcInvestor).withdrawUSDC(rustpAmount.div(1e12)) - - // const usdcAmountAfter = await usdcToken.balanceOf(usdcInvestor.address) - - // expect(usdcAmountAfter).to.be.equal(rustpAmount.div(1e12).add(usdcAmountBefore)) - // }) - // it("Should be able to get reserve fee", async function () { - // // set reserve 10% - // await rustpool.connect(admin).setReserveFactor(1000000) - // // borrow all usdc - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - - // await rustpool.connect(admin).claimReservesFee(feeCollector.address) - // const feeBalance = await rustpool.balanceOf(feeCollector.address) - // const rustpAmount = await rustpool.balanceOf(usdcInvestor.address) - // // ~= 5.2% apr - // expect(rustpAmount.add(feeBalance).div(1e12)).to.be.within( - // amountToSupplyUSDC.mul(10510).div(10000), - // amountToSupplyUSDC.mul(10530).div(10000) - // ) - // }) - // it("Should be able the same debt and ustp supply when 100% utilization rate", async function () { - // const oldTotalSupplyrUSTP = await rustpool.totalSupplyrUSTP() - // // borrow all usdc - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - // const newTotalSupplyrUSTP = await rustpool.totalSupplyrUSTP() - // const totalBorrowrUSTP = await rustpool.totalBorrowrUSTP() - - // const rustpAmount = await rustpool.balanceOf(usdcInvestor.address) - - // // ~= 5.2% apr - // expect(rustpAmount.div(1e12)).to.be.within( - // amountToSupplyUSDC.mul(10510).div(10000), - // amountToSupplyUSDC.mul(10530).div(10000) - // ) - - // expect(totalBorrowrUSTP.sub(amountToSupplyUSDC.mul(1e12))).to.be.equal( - // newTotalSupplyrUSTP.sub(oldTotalSupplyrUSTP) - // ) - // }) - - // it("Should be able the same debt and ustp supply when 50% utilization rate", async function () { - // const oldTotalSupplyrUSTP = await rustpool.totalSupplyrUSTP() - // // borrow 50% usdc - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC.div(2)) - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - // const newTotalSupplyrUSTP = await rustpool.totalSupplyrUSTP() - // const totalBorrowrUSTP = await rustpool.totalBorrowrUSTP() - - // const rustpAmount = await rustpool.balanceOf(usdcInvestor.address) - - // // ~= 2.1% apr - // expect(rustpAmount.div(1e12)).to.be.within( - // amountToSupplyUSDC.mul(10255).div(10000), - // amountToSupplyUSDC.mul(10265).div(10000) - // ) - - // expect(totalBorrowrUSTP.sub(amountToSupplyUSDC.div(2).mul(1e12))).to.be.equal( - // newTotalSupplyrUSTP.sub(oldTotalSupplyrUSTP) - // ) - // }) - // }) - // }) - - // describe("Liquidate", function () { - // beforeEach(async () => { - // now = now + ONE_HOUR - // await mineBlockWithTimestamp(ethers.provider, now) - // await usdcToken - // .connect(usdcInvestor) - // .approve(rustpool.address, amountToSupplyUSDC.mul(10)) - // await rustpool.connect(usdcInvestor).supplyUSDC(amountToSupplyUSDC.mul(10)) - // await stbtToken - // .connect(stbtInvestor) - // .approve(rustpool.address, amountToSupplySTBT.mul(2)) - // await rustpool.connect(stbtInvestor).supplySTBT(amountToSupplySTBT.mul(2)) - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC) - // await rustpool.connect(admin).setLiquidateProvider(stbtInvestor.address, true) - // await liquidatePool.connect(admin).setRedemptionOption(true) - // }) - - // it(`Should be able to liquidate for with zero fee`, async () => { - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - // const beforeUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const afterUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // // There are some err in interest. - // expect(beforeUSDPAmount.sub(afterUSDPAmount)).to.be.within( - // liquidateSTBT.mul(99999).div(100000), - // liquidateSTBT.mul(100001).div(100000) - // ) - - // const mxpBalance = await stbtToken.balanceOf(mxpRedeemPool.address) - // expect(mxpBalance).to.be.equal(liquidateSTBT) - - // const liquidationIndex = await liquidatePool.liquidationIndex() - // await usdcToken.connect(deployer).transfer(liquidatePool.address, amountToSupplyUSDC) - // const beforeUSDCAmount = await usdcToken.balanceOf(usdcInvestor.address) - // await liquidatePool.connect(usdcInvestor).finalizeLiquidationById(liquidationIndex) - // const afterUSDCAmount = await usdcToken.balanceOf(usdcInvestor.address) - // expect(afterUSDCAmount.sub(beforeUSDCAmount)).to.be.equal(amountToSupplyUSDC) - // }) - - // it(`Should be able to liquidate for with fee`, async () => { - // await liquidatePool.connect(admin).setLiquidateFeeRate(1000000) - - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - // const beforeUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const afterUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // // There are some err in interest. - // expect(beforeUSDPAmount.sub(afterUSDPAmount)).to.be.within( - // liquidateSTBT.mul(99999).div(100000), - // liquidateSTBT.mul(100001).div(100000) - // ) - - // const mxpBalance = await stbtToken.balanceOf(mxpRedeemPool.address) - // expect(mxpBalance).to.be.equal(liquidateSTBT) - - // const fee = amountToSupplyUSDC.mul(1000000).div(100000000) - - // const liquidationIndex = await liquidatePool.liquidationIndex() - // await usdcToken.connect(deployer).transfer(liquidatePool.address, amountToSupplyUSDC) - // const beforeUSDCAmount = await usdcToken.balanceOf(usdcInvestor.address) - // await liquidatePool.connect(usdcInvestor).finalizeLiquidationById(liquidationIndex) - // const afterUSDCAmount = await usdcToken.balanceOf(usdcInvestor.address) - // expect(afterUSDCAmount.sub(beforeUSDCAmount)).to.be.equal(amountToSupplyUSDC.sub(fee)) - // const feeCollectorBalance = await usdcToken.balanceOf(feeCollector.address) - // expect(feeCollectorBalance).to.be.equal(fee) - // }) - - // it(`Should be able to liquidate for with interest`, async () => { - // now = now + ONE_YEAR - // await mineBlockWithTimestamp(ethers.provider, now) - - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - - // const liquidateSTBT = await rustpool.getBorrowedAmount(stbtInvestor.address) - - // // ~= 5.2% apr - // expect(liquidateSTBT.div(1e12)).to.be.within( - // amountToSupplyUSDC.mul(10510).div(10000), - // amountToSupplyUSDC.mul(10530).div(10000) - // ) - - // const beforeUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const afterUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // // There are some err in interest. - // expect(beforeUSDPAmount.sub(afterUSDPAmount)).to.be.within( - // liquidateSTBT.mul(99999).div(100000), - // liquidateSTBT.mul(100001).div(100000) - // ) - - // const mxpBalance = await stbtToken.balanceOf(mxpRedeemPool.address) - // expect(mxpBalance).to.be.equal(liquidateSTBT) - - // const liquidationIndex = await liquidatePool.liquidationIndex() - // await usdcToken - // .connect(deployer) - // .transfer(liquidatePool.address, liquidateSTBT.div(1e12)) - // const beforeUSDCAmount = await usdcToken.balanceOf(usdcInvestor.address) - // await liquidatePool.connect(usdcInvestor).finalizeLiquidationById(liquidationIndex) - // const afterUSDCAmount = await usdcToken.balanceOf(usdcInvestor.address) - // expect(afterUSDCAmount.sub(beforeUSDCAmount)).to.be.equal(liquidateSTBT.div(1e12)) - // }) - - // it(`Should be able to finalizeLiquidationById for twice`, async () => { - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const liquidationIndex = await liquidatePool.liquidationIndex() - // await usdcToken.connect(deployer).transfer(liquidatePool.address, amountToSupplyUSDC) - // await liquidatePool.connect(usdcInvestor).finalizeLiquidationById(liquidationIndex) - // await expect( - // liquidatePool.connect(usdcInvestor).finalizeLiquidationById(liquidationIndex) - // ).to.be.revertedWith("Withdrawn") - // }) - - // it(`Should be able to finalizeLiquidationById from others`, async () => { - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const liquidationIndex = await liquidatePool.liquidationIndex() - // await usdcToken.connect(deployer).transfer(liquidatePool.address, amountToSupplyUSDC) - // await expect( - // liquidatePool.connect(stbtInvestor).finalizeLiquidationById(liquidationIndex) - // ).to.be.revertedWith("Not yours.") - // }) - - // it(`Should be able to liquidate without otc`, async () => { - // await liquidatePool.connect(admin).setRedemptionOption(false) - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - // const beforeUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const afterUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - - // // There are some err in interest. - // expect(beforeUSDPAmount.sub(afterUSDPAmount)).to.be.within( - // liquidateSTBT.mul(99999).div(100000), - // liquidateSTBT.mul(100001).div(100000) - // ) - - // const mxpBalance = await stbtToken.balanceOf(mxpRedeemPool.address) - // expect(mxpBalance).to.be.equal(liquidateSTBT) - // }) - - // it(`Should be not able to finalizeLiquidationById when the proccess not done yet.`, async () => { - // await liquidatePool.connect(admin).setProcessPeriod(ONE_WEEK) - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - // await rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT) - // const liquidationIndex = await liquidatePool.liquidationIndex() - // await usdcToken.connect(deployer).transfer(liquidatePool.address, amountToSupplyUSDC) - // await expect( - // liquidatePool.connect(usdcInvestor).finalizeLiquidationById(liquidationIndex) - // ).to.be.revertedWith("Not done yet.") - // }) - - // it("Should be not able to more than user owns.", async () => { - // const liquidateSTBT = await rustpool.balanceOf(admin.address) - // await expect( - // rustpool - // .connect(admin) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT.add(100)) - // ).to.be.revertedWith("BALANCE_EXCEEDED") - // }) - - // it("Should be not able to liquidate self", async () => { - // const liquidateSTBT = await rustpool.balanceOf(stbtInvestor.address) - // await expect( - // rustpool - // .connect(stbtInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT.add(100)) - // ).to.be.revertedWith("don't liquidate self.") - // }) - - // it("Should be not able to more than borrower's debt.", async () => { - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - // const liquidateSTBT = await rustpool.getBorrowedAmount(stbtInvestor.address) - // await expect( - // rustpool - // .connect(usdcInvestor) - // .liquidateBorrow(stbtInvestor.address, liquidateSTBT.mul(2)) - // ).to.be.revertedWith("repayAmount should be less than borrower's debt.") - // }) - // }) - // describe("Set process period", function () { - // it("Should be not able to set _processPeriod over 7 days", async () => { - // await expect( - // liquidatePool.connect(admin).setProcessPeriod(ONE_WEEK + 1) - // ).to.be.revertedWith("should be less than 7 days") - // }) - // }) - - // describe("Flash liquidate", function () { - // let testList = [ - // { - // tokenName: "DAI", - // tokenIndex: 1, - // }, - // { - // tokenName: "USDC", - // tokenIndex: 2, - // }, - // { - // tokenName: "USDT", - // tokenIndex: 3, - // }, - // ] - // beforeEach(async () => { - // now = now + ONE_HOUR - // await mineBlockWithTimestamp(ethers.provider, now) - // await usdcToken - // .connect(usdcInvestor) - // .approve(rustpool.address, amountToSupplyUSDC.mul(10)) - // await rustpool.connect(usdcInvestor).supplyUSDC(amountToSupplyUSDC.mul(10)) - // await stbtToken - // .connect(stbtInvestor) - // .approve(rustpool.address, amountToSupplySTBT.mul(2)) - // await rustpool.connect(stbtInvestor).supplySTBT(amountToSupplySTBT.mul(2)) - // await rustpool.connect(stbtInvestor).borrowUSDC(amountToSupplyUSDC) - - // await rustpool.connect(stbtInvestor).applyFlashLiquidateProvider() - // await rustpool.connect(admin).acceptFlashLiquidateProvider(stbtInvestor.address) - // }) - - // testList.forEach(({ tokenName, tokenIndex }, i) => { - // it(`Should be able to flash liquidate for ${tokenName} with zero fee`, async () => { - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - - // const beforeUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // const liquidateOut = await liquidatePool.getFlashLiquidateAmountOutFromCurve( - // liquidateSTBT, - // tokenIndex - // ) - - // const beforeBalance = await tokens[i].balanceOf(usdcInvestor.address) - // await rustpool - // .connect(usdcInvestor) - // .flashLiquidateBorrow(stbtInvestor.address, liquidateSTBT, tokenIndex, 0) - // const afterBalance = await tokens[i].balanceOf(usdcInvestor.address) - // const afterUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // expect(afterBalance.sub(beforeBalance)).to.be.equal(liquidateOut) - // // There are some err in interest. - // expect(beforeUSDPAmount.sub(afterUSDPAmount)).to.be.within( - // liquidateSTBT.mul(99999).div(100000), - // liquidateSTBT.mul(100001).div(100000) - // ) - // }) - - // it(`Should be able to flash liquidate for ${tokenName} with fee`, async () => { - // await liquidatePool.connect(admin).setLiquidateFeeRate(1000000) - // const liquidateSTBT = amountToSupplyUSDC.mul(1e12) - - // const beforeUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - - // const liquidateOut = await liquidatePool.getFlashLiquidateAmountOutFromCurve( - // liquidateSTBT, - // tokenIndex - // ) - // const fee = liquidateOut.mul(1000000).div(100000000) - // const amountAfterFee = liquidateOut.sub(fee) - - // const beforeBalance = await tokens[i].balanceOf(usdcInvestor.address) - // await rustpool - // .connect(usdcInvestor) - // .flashLiquidateBorrow(stbtInvestor.address, liquidateSTBT, tokenIndex, 0) - // const afterBalance = await tokens[i].balanceOf(usdcInvestor.address) - // expect(afterBalance.sub(beforeBalance)).to.be.equal(amountAfterFee) - - // const feeCollectorBalance = await tokens[i].balanceOf(feeCollector.address) - // expect(feeCollectorBalance).to.be.equal(fee) - // const afterUSDPAmount = await rustpool.balanceOf(usdcInvestor.address) - // // There are some err in interest. - // expect(beforeUSDPAmount.sub(afterUSDPAmount)).to.be.within( - // liquidateSTBT.mul(99999).div(100000), - // liquidateSTBT.mul(100001).div(100000) - // ) - // }) - // }) - - // it("Should be not able to more than user owns.", async () => { - // const liquidateSTBT = await rustpool.balanceOf(admin.address) - // await expect( - // rustpool - // .connect(admin) - // .flashLiquidateBorrow(stbtInvestor.address, liquidateSTBT.add(100), 1, 0) - // ).to.be.revertedWith("BALANCE_EXCEEDED") - // }) - - // it("Should be not able to liquidate self", async () => { - // const liquidateSTBT = await rustpool.balanceOf(stbtInvestor.address) - // await expect( - // rustpool - // .connect(stbtInvestor) - // .flashLiquidateBorrow(stbtInvestor.address, liquidateSTBT.add(100), 1, 0) - // ).to.be.revertedWith("don't liquidate self.") - // }) - - // it("Should be not able to more than borrower's debt.", async () => { - // // to realize interest - // await rustpool.connect(admin).setReserveFactor(0) - // const liquidateSTBT = await rustpool.getBorrowedAmount(stbtInvestor.address) - // await expect( - // rustpool - // .connect(usdcInvestor) - // .flashLiquidateBorrow(stbtInvestor.address, liquidateSTBT.mul(2), 1, 0) - // ).to.be.revertedWith("repayAmount should be less than borrower's debt.") - // }) - // }) + describe("Withdraw", function () { + beforeEach(async () => { + now = now + ONE_HOUR + await mineBlockWithTimestamp(ethers.provider, now) + await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) + await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex) + await tselicToken.connect(tselicInvestor).approve(rbrllpool.address, amountToSupplyTSelic) + await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic) + }) + describe("Withdraw DREX", function () { + it("Should be able to withdraw", async function () { + const drexAmountBefore = await drexToken.balanceOf(drexInvestor.address) + + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + await rbrllpool.connect(drexInvestor).withdrawDREX(amountToSupplyDrex) + + const drexAmountAfter = await drexToken.balanceOf(drexInvestor.address) + + expect(await rbrllpool.balanceOf(drexInvestor.address)).to.be.equal(0) + expect(drexAmountAfter).to.be.equal(rbrllAmount.div(1e12).add(drexAmountBefore)) + }) + + it("Should fail if withdraw zero DREX", async function () { + await expect(rbrllpool.connect(drexInvestor).withdrawDREX(0)).to.be.revertedWith( + "Withdraw DREX should be more than 0." + ) + }) + + it("Should fail if withdraw more than supply", async function () { + await expect( + rbrllpool.connect(drexInvestor).withdrawDREX(amountToSupplyDrex + 1) + ).to.be.revertedWith("BALANCE_EXCEEDED") + }) + }) + describe("Withdraw TSELIC", function () { + it("Should be able to withdraw", async function () { + const tselicAmountBefore = await tselicToken.balanceOf(tselicInvestor.address) + await rbrllpool.connect(tselicInvestor).withdrawTSELIC(amountToSupplyTSelic) + + const tselicAmountAfter = await tselicToken.balanceOf(tselicInvestor.address) + + expect(await rbrllpool.depositedTSELIC(tselicInvestor.address)).to.be.equal(0) + expect(tselicAmountAfter).to.be.equal(amountToSupplyTSelic.add(tselicAmountBefore)) + }) + + it("Should be able to withdraw all tselic", async function () { + const tselicAmountBefore = await tselicToken.balanceOf(tselicInvestor.address) + await rbrllpool.connect(tselicInvestor).withdrawAllTSELIC() + + const tselicAmountAfter = await tselicToken.balanceOf(tselicInvestor.address) + + expect(await rbrllpool.depositedTSELIC(tselicInvestor.address)).to.be.equal(0) + expect(tselicAmountAfter).to.be.equal(amountToSupplyTSelic.add(tselicAmountBefore)) + }) + + it("Should fail if supply zero TSELIC", async function () { + await expect(rbrllpool.connect(tselicInvestor).withdrawTSELIC(0)).to.be.revertedWith( + "Withdraw TSELIC should be more than 0." + ) + }) + + it("Should fail if withdraw more than supply", async function () { + await expect(rbrllpool.connect(tselicInvestor).withdrawTSELIC(amountToSupplyTSelic + 1)).to + .be.reverted + }) + }) + }) + describe("Borrow", function () { + beforeEach(async () => { + now = now + ONE_HOUR + await mineBlockWithTimestamp(ethers.provider, now) + await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) + await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex) + await tselicToken.connect(tselicInvestor).approve(rbrllpool.address, amountToSupplyTSelic) + await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic) + }) + describe("Borrow Drex", function () { + it("Should be able to borrow", async function () { + const drexAmountBefore = await drexToken.balanceOf(tselicInvestor.address) + + const borrowShares = await rbrllpool.getSharesByrBRLLAmount( + amountToBorrowDrex.mul(1e12) + ) + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToBorrowDrex) + + const drexAmountAfter = await drexToken.balanceOf(tselicInvestor.address) + + expect(await rbrllpool.getBorrowedSharesOf(tselicInvestor.address)).to.be.equal( + borrowShares + ) + expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowShares) + expect(drexAmountAfter).to.be.equal(amountToBorrowDrex.add(drexAmountBefore)) + }) + + it("Should fail if borrow zero DREX", async function () { + await expect(rbrllpool.connect(tselicInvestor).borrowDREX(0)).to.be.revertedWith( + "Borrow DREX should be more than 0." + ) + }) + + it("Should fail if borrow more than collateral", async function () { + await expect( + rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) + ).to.be.revertedWith("Cannot be lower than the safeCollateralRate.") + }) + }) + }) + + describe("Repay", function () { + beforeEach(async () => { + now = now + ONE_HOUR + await mineBlockWithTimestamp(ethers.provider, now) + // await interestRateModel.connect(deployer).setAPR(0) + await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) + await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex) + await tselicToken.connect(tselicInvestor).approve(rbrllpool.address, amountToSupplyTSelic) + await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic) + + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToBorrowDrex) + await drexToken.connect(tselicInvestor).approve(rbrllpool.address, BIGNUMBER) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + }) + describe("Repay DREX", function () { + it("Should be able to repay 50%", async function () { + const drexAmountBefore = await drexToken.balanceOf(tselicInvestor.address) + + const borrowSharesBefore = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) + const borrowrBRLL = (await rbrllpool.getBorrowedAmount(tselicInvestor.address)).div(2) + + const borrowDREX = borrowrBRLL.div(1e12) + + await rbrllpool.connect(tselicInvestor).repayDREX(borrowDREX) + + const drexAmountAfter = await drexToken.balanceOf(tselicInvestor.address) + const borrowSharesAfter = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) + + + expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowSharesAfter) + expect(drexAmountBefore).to.be.equal(drexAmountAfter.add(borrowDREX)) + }) + it("Should be able to repay 100%", async function () { + const drexAmountBefore = await drexToken.balanceOf(tselicInvestor.address) + + const borrowSharesBefore = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) + const borrowrBRLL = await rbrllpool.getBorrowedAmount(tselicInvestor.address) + + const borrowDREX = borrowrBRLL.div(1e12) + + await rbrllpool.connect(tselicInvestor).repayDREX(borrowDREX) + + const drexAmountAfter = await drexToken.balanceOf(tselicInvestor.address) + const borrowSharesAfter = await rbrllpool.getBorrowedSharesOf(tselicInvestor.address) + + expect(await rbrllpool.totalBorrowShares()).to.be.equal(borrowSharesAfter) + expect(drexAmountBefore).to.be.equal(drexAmountAfter.add(borrowDREX)) + }) + + it("Should fail if repay zero DREX", async function () { + await expect(rbrllpool.connect(tselicInvestor).repayDREX(0)).to.be.revertedWith( + "Repay DREX should be more than 0." + ) + }) + }) + }) + + describe("Interest", function () { + beforeEach(async () => { + now = now + ONE_HOUR + await mineBlockWithTimestamp(ethers.provider, now) + await drexToken.connect(drexInvestor).approve(rbrllpool.address, amountToSupplyDrex) + await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex) + await tselicToken + .connect(tselicInvestor) + .approve(rbrllpool.address, amountToSupplyTSelic.mul(2)) + await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic.mul(2)) + }) + describe("Gain interest", function () { + it("Should be able to full interest when 100% utilization rate", async function () { + // borrow all drex + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + + // ~= 12.25% apr + expect(rbrllAmount.div(1e12)).to.be.within( + amountToSupplyDrex.mul(11215).div(10000), + amountToSupplyDrex.mul(11235).div(10000) + ) + }) + it("Should be able to half interest when 50% utilization rate", async function () { + // borrow all drex + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex.div(2)) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + + // ~= 6.125% apr + expect(rbrllAmount.div(1e12)).to.be.within( + amountToSupplyDrex.mul(10602).div(10000), + amountToSupplyDrex.mul(10625).div(10000) + ) + }) + + it("Should be able to withdraw interest income", async function () { + // borrow all drex + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + + await drexToken + .connect(tselicInvestor) + .approve(rbrllpool.address, amountToSupplyDrex.mul(2)) + await rbrllpool.connect(tselicInvestor).supplyDREX(amountToSupplyDrex.mul(2)) + + const drexAmountBefore = await drexToken.balanceOf(drexInvestor.address) + + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + await rbrllpool.connect(drexInvestor).withdrawDREX(rbrllAmount.div(1e12)) + + const drexAmountAfter = await drexToken.balanceOf(drexInvestor.address) + + expect(drexAmountAfter).to.be.equal(rbrllAmount.div(1e12).add(drexAmountBefore)) + }) + it("Should be able to get reserve fee", async function () { + // set reserve 10% + await rbrllpool.connect(admin).setReserveFactor(1000000) + // borrow all drex + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + + await rbrllpool.connect(admin).claimReservesFee(feeCollector.address) + const feeBalance = await rbrllpool.balanceOf(feeCollector.address) + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + // ~= 12.25% apr + expect(rbrllAmount.add(feeBalance).div(1e12)).to.be.within( + amountToSupplyDrex.mul(11215).div(10000), + amountToSupplyDrex.mul(11235).div(10000) + ) + }) + it("Should be able the same debt and brll supply when 100% utilization rate", async function () { + const oldTotalSupplyrBRLL = await rbrllpool.totalSupplyrBRLL() + // borrow all drex + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + const newTotalSupplyrBRLL = await rbrllpool.totalSupplyrBRLL() + const totalBorrowrBRLL = await rbrllpool.totalBorrowrBRLL() + + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + + // ~= 12.25% apr + expect(rbrllAmount.div(1e12)).to.be.within( + amountToSupplyDrex.mul(11215).div(10000), + amountToSupplyDrex.mul(11235).div(10000) + ) + + expect(totalBorrowrBRLL.sub(amountToSupplyDrex.mul(1e12))).to.be.equal( + newTotalSupplyrBRLL.sub(oldTotalSupplyrBRLL) + ) + }) + + it("Should be able the same debt and brll supply when 50% utilization rate", async function () { + const oldTotalSupplyrBRLL = await rbrllpool.totalSupplyrBRLL() + // borrow 50% drex + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex.div(2)) + now = now + ONE_YEAR + await mineBlockWithTimestamp(ethers.provider, now) + + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + const newTotalSupplyrBRLL = await rbrllpool.totalSupplyrBRLL() + const totalBorrowrBRLL = await rbrllpool.totalBorrowrBRLL() + + const rbrllAmount = await rbrllpool.balanceOf(drexInvestor.address) + + // // ~= 6.25% apr + // expect(rbrllAmount.div(1e12)).to.be.within( + // amountToSupplyDrex.mul(10602).div(10000), + // amountToSupplyDrex.mul(10625).div(10000) + // ) + + expect(totalBorrowrBRLL.sub(amountToSupplyDrex.div(2).mul(1e12))).to.be.equal( + newTotalSupplyrBRLL.sub(oldTotalSupplyrBRLL) + ) + }) + }) + }) + + describe("Flash liquidate", function () { + + beforeEach(async () => { + now = now + ONE_HOUR + await mineBlockWithTimestamp(ethers.provider, now) + await drexToken + .connect(drexInvestor) + .approve(rbrllpool.address, amountToSupplyDrex.mul(10)) + await rbrllpool.connect(drexInvestor).supplyDREX(amountToSupplyDrex.mul(10)) + await tselicToken + .connect(tselicInvestor) + .approve(rbrllpool.address, amountToSupplyTSelic.mul(2)) + await rbrllpool.connect(tselicInvestor).supplyTSELIC(amountToSupplyTSelic.mul(2)) + await rbrllpool.connect(tselicInvestor).borrowDREX(amountToSupplyDrex) + + await rbrllpool.connect(admin).setLiquidateProvider(tselicInvestor.address, true) + }) + + it(`Should be able to liquidate `, async () => { + const liquidateDREX = amountToSupplyDrex.div(10) + const beforeBRLLAmount = await rbrllpool.balanceOf(drexInvestor.address) + + const beforeBalance = await drexToken.balanceOf(drexInvestor.address) + await rbrllpool + .connect(drexInvestor) + .flashLiquidateBorrow(tselicInvestor.address, liquidateDREX, amountToSupplyTSelic) + const afterBalance = await drexToken.balanceOf(drexInvestor.address) + const afterBRLLAmount = await rbrllpool.balanceOf(drexInvestor.address) + expect(afterBalance.sub(beforeBalance)).to.be.equal(liquidateDREX) + // There are some err in interest. + expect(beforeBRLLAmount.sub(afterBRLLAmount).div(10 ** 13)).to.be.within( + liquidateDREX.mul(99999).div(100000), + liquidateDREX.mul(100001).div(100000) + ) + }) + + it("Should be not able to more than user owns.", async () => { + const liquidateTSELIC = await rbrllpool.balanceOf(admin.address) + await expect( + rbrllpool + .connect(admin) + .flashLiquidateBorrow(tselicInvestor.address, liquidateTSELIC.add(100), 0) + ).to.be.revertedWith("BALANCE_EXCEEDED") + }) + + it("Should be not able to liquidate self", async () => { + const liquidateTSELIC = await rbrllpool.balanceOf(tselicInvestor.address) + await expect( + rbrllpool + .connect(tselicInvestor) + .flashLiquidateBorrow(tselicInvestor.address, liquidateTSELIC.add(100), 0) + ).to.be.revertedWith("don't liquidate self.") + }) + + it("Should be not able to more than borrower's debt.", async () => { + // to realize interest + await rbrllpool.connect(admin).setReserveFactor(0) + const liquidateTSELIC = await rbrllpool.getBorrowedAmount(tselicInvestor.address) + await expect( + rbrllpool + .connect(drexInvestor) + .flashLiquidateBorrow(tselicInvestor.address, liquidateTSELIC.mul(2), 0) + ).to.be.revertedWith("repayAmount should be less than borrower's debt.") + }) + }) })