diff --git a/contracts/contracts/gelato/conditions/ConditionDestVaultWillBeSafe.sol b/contracts/contracts/gelato/conditions/ConditionDestVaultWillBeSafe.sol index e90b107..07acdbf 100644 --- a/contracts/contracts/gelato/conditions/ConditionDestVaultWillBeSafe.sol +++ b/contracts/contracts/gelato/conditions/ConditionDestVaultWillBeSafe.sol @@ -11,11 +11,15 @@ import { _vaultWillBeSafe, _newVaultWillBeSafe } from "../../../functions/dapps/FMaker.sol"; +import {DAI} from "../../../constants/CInstaDapp.sol"; import { + _getFlashLoanRoute, + _getGasCostMakerToMaker, _getRealisedDebt } from "../../../functions/gelato/FGelatoDebtBridge.sol"; - +import {_getGelatoProviderFees} from "../../../functions/gelato/FGelato.sol"; import {GelatoBytes} from "../../../lib/GelatoBytes.sol"; +import {sub} from "../../../vendor/DSMath.sol"; contract ConditionDestVaultWillBeSafe is GelatoConditionsStandard { using GelatoBytes for bytes; @@ -55,7 +59,14 @@ contract ConditionDestVaultWillBeSafe is GelatoConditionsStandard { ) public view returns (string memory) { uint256 wDaiToBorrow = _getRealisedDebt(_getMakerVaultDebt(_fromVaultId)); - uint256 wColToDeposit = _getMakerVaultCollateralBalance(_fromVaultId); + uint256 route = _getFlashLoanRoute(DAI, wDaiToBorrow); + uint256 gasCost = _getGasCostMakerToMaker(_destVaultId == 0, route); + uint256 gasFeesPaidFromCol = _getGelatoProviderFees(gasCost); + uint256 wColToDeposit = + sub( + _getMakerVaultCollateralBalance(_fromVaultId), + gasFeesPaidFromCol + ); return destVaultWillBeSafeExplicit( diff --git a/contracts/functions/gelato/FGelato.sol b/contracts/functions/gelato/FGelato.sol index 83d4948..bf262a5 100644 --- a/contracts/functions/gelato/FGelato.sol +++ b/contracts/functions/gelato/FGelato.sol @@ -5,7 +5,9 @@ import {GELATO_GAS_PRICE_ORACLE} from "../../constants/CGelato.sol"; import {mul} from "../../vendor/DSMath.sol"; function _getGelatoGasPrice() view returns (uint256) { - return uint256(GELATO_GAS_PRICE_ORACLE.latestAnswer()); + int256 oracleGasPrice = GELATO_GAS_PRICE_ORACLE.latestAnswer(); + if (oracleGasPrice <= 0) revert("_getGelatoGasPrice:0orBelow"); + return uint256(oracleGasPrice); } function _getGelatoProviderFees(uint256 _gas) view returns (uint256) { diff --git a/test/unit/conditions/2_ConditionIsDestVaultWillBeSafe.test.js b/test/unit/conditions/2_ConditionIsDestVaultWillBeSafe.test.js index 4de4997..e233d41 100644 --- a/test/unit/conditions/2_ConditionIsDestVaultWillBeSafe.test.js +++ b/test/unit/conditions/2_ConditionIsDestVaultWillBeSafe.test.js @@ -35,8 +35,11 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { let conditionDestVaultWillBeSafe; let dsa; - let cdpId; - let ilk; + let cdpAId; + let cdpBId; + let ilkA; + let ilkB; + let ethAIlk; let ethBIlk; let amountToBorrow; @@ -94,8 +97,8 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { await instaList.accountAddr(dsaID) ); - // Create/Deposit/Borrow a Vault - const openVault = await hre.run("abi-encode-withselector", { + // Create/Deposit/Borrow a Vault for ETH-A + let openVault = await hre.run("abi-encode-withselector", { abi: ConnectMaker.abi, functionname: "open", inputs: ["ETH-A"], @@ -104,48 +107,82 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress); let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address); - cdpId = String(cdps.ids[0]); + cdpAId = String(cdps.ids[0]); expect(cdps.ids[0].isZero()).to.be.false; + // Create/Deposit/Borrow a Vault for ETH-B + openVault = await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "open", + inputs: ["ETH-B"], + }); + + await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress); + + cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address); + cdpBId = String(cdps.ids[1]); + + expect(cdps.ids[1].isZero()).to.be.false; + + ethAIlk = ethers.utils.formatBytes32String("ETH-A"); + ilkA = await vat.ilks(ethAIlk); + + ethBIlk = ethers.utils.formatBytes32String("ETH-B"); + ilkB = await vat.ilks(ethBIlk); + + amountToBorrow = ethers.utils.parseUnits("100", 18); + }); + + it("#1: ok should return Ok when the gas fees didn't exceed a user define amount with vault creation", async function () { + // Steps : + // 1 - Deposit + // 2 - Borrow + // 3 - Test if vault ETH-B will be safe after debt bridge action + const amountToDeposit = amountToBorrow + .mul(ethers.utils.parseUnits("1", 27)) + .div(ilkA[2]) // ilk[2] represent the liquidation ratio of ilk + .add(ethers.utils.parseUnits("4", 17)); // to be just above the liquidation ratio. + + //#region Deposit + await dsa.cast( [hre.network.config.ConnectMaker], [ await hre.run("abi-encode-withselector", { abi: ConnectMaker.abi, functionname: "deposit", - inputs: [cdpId, ethers.utils.parseEther("10"), 0, 0], + inputs: [cdpAId, amountToDeposit, 0, 0], }), ], userAddress, { - value: ethers.utils.parseEther("10"), + value: amountToDeposit, } ); + + //#endregion Deposit + + //#region Borrow + await dsa.cast( [hre.network.config.ConnectMaker], [ await hre.run("abi-encode-withselector", { abi: ConnectMaker.abi, functionname: "borrow", - inputs: [cdpId, ethers.utils.parseUnits("1000", 18), 0, 0], + inputs: [cdpAId, amountToBorrow, 0, 0], }), ], userAddress ); - expect(await DAI.balanceOf(dsa.address)).to.be.equal( - ethers.utils.parseEther("1000") - ); + expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow); - ethBIlk = ethers.utils.formatBytes32String("ETH-B"); - ilk = await vat.ilks(ethBIlk); - amountToBorrow = ethers.utils.parseUnits("100", 18); - }); + //#endregion Borrow - it("#1: ok should return Ok when the gas fees didn't exceed a user define amount", async function () { const conditionData = await conditionDestVaultWillBeSafe.getConditionData( - cdpId, + cdpAId, 0, "ETH-B" ); @@ -154,14 +191,260 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { ).to.be.equal("OK"); }); - it("#2: New Vault Case : destVaultWillBeSafeExplicit should return false when col is lower than borrow amount / spot", async function () { + it("#2: ok should return DestVaultWillNotBeSafe when the gas fees exceeded a user define amount with vault creation", async function () { + // Steps : + // 1 - Deposit + // 2 - Borrow + // 3 - Test if vault ETH-B will be safe after debt bridge action + const amountToDeposit = amountToBorrow + .mul(ethers.utils.parseUnits("1", 27)) + .div(ilkA[2]) // ilk[2] represent the liquidation ratio of ilk + .add(ethers.utils.parseUnits("1", 16)); // to be just below the liquidation ratio. + + //#region Deposit + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "deposit", + inputs: [cdpAId, amountToDeposit, 0, 0], + }), + ], + userAddress, + { + value: amountToDeposit, + } + ); + + //#endregion Deposit + + //#region Borrow + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "borrow", + inputs: [cdpAId, amountToBorrow, 0, 0], + }), + ], + userAddress + ); + + expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow); + + //#endregion Borrow + + const conditionData = await conditionDestVaultWillBeSafe.getConditionData( + cdpAId, + 0, + "ETH-B" + ); + expect( + await conditionDestVaultWillBeSafe.ok(0, conditionData, 0) + ).to.be.equal("DestVaultWillNotBeSafe"); + }); + + it("#3: ok should return DestVaultWillNotBeSafe when the gas fees exceeded a user define amount", async function () { + // Steps : + // 1 - Deposit + // 2 - Borrow + // 3 - Test if vault ETH-B will be safe after debt bridge action + const amountToDeposit = amountToBorrow + .mul(ethers.utils.parseUnits("1", 27)) + .div(ilkA[2]) // ilk[2] represent the liquidation ratio of ilk + .add(ethers.utils.parseUnits("1", 16)); // to be just below the liquidation ratio. + + //#region Deposit + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "deposit", + inputs: [cdpAId, amountToDeposit, 0, 0], + }), + ], + userAddress, + { + value: amountToDeposit, + } + ); + + //#endregion Deposit + + //#region Borrow + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "borrow", + inputs: [cdpAId, amountToBorrow, 0, 0], + }), + ], + userAddress + ); + + expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow); + + //#endregion Borrow + + const conditionData = await conditionDestVaultWillBeSafe.getConditionData( + cdpAId, + cdpBId, + "ETH-B" + ); + expect( + await conditionDestVaultWillBeSafe.ok(0, conditionData, 0) + ).to.be.equal("DestVaultWillNotBeSafe"); + }); + + it("#4: ok should return Ok when the gas fees didn't exceed a user define amount", async function () { + // Steps : + // 1 - Deposit + // 2 - Borrow + // 3 - Test if vault ETH-B will be safe after debt bridge action + const amountToDeposit = amountToBorrow + .mul(ethers.utils.parseUnits("1", 27)) + .div(ilkA[2]) // ilk[2] represent the liquidation ratio of ilk + .add(ethers.utils.parseUnits("4", 17)); // to be just above the liquidation ratio. + + //#region Deposit + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "deposit", + inputs: [cdpAId, amountToDeposit, 0, 0], + }), + ], + userAddress, + { + value: amountToDeposit, + } + ); + + //#endregion Deposit + + //#region Borrow + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "borrow", + inputs: [cdpAId, amountToBorrow, 0, 0], + }), + ], + userAddress + ); + + expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow); + //#endregion Borrow + + const conditionData = await conditionDestVaultWillBeSafe.getConditionData( + cdpAId, + cdpBId, + "ETH-B" + ); + expect( + await conditionDestVaultWillBeSafe.ok(0, conditionData, 0) + ).to.be.equal("OK"); + }); + + it("#5: ok should return Ok when the gas fees didn't exceed a user define amount with deposit on vaultB", async function () { + // Steps : + // 1 - Deposit in Vault ETH-A + // 2 - Deposit in Vault ETH-B just the minimum needed to make the all position of the user safe after debt bridge + // 3 - Borrow + // 4 - Test if vault ETH-B will be safe after debt bridge action + const amountToDeposit = amountToBorrow + .mul(ethers.utils.parseUnits("1", 27)) + .div(ilkA[2]) // ilk[2] represent the liquidation ratio of ilk + .add(ethers.utils.parseUnits("1", 17)); // to be just above the liquidation ratio. + + //#region Deposit on Vault ETH-A + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "deposit", + inputs: [cdpAId, amountToDeposit, 0, 0], + }), + ], + userAddress, + { + value: amountToDeposit, + } + ); + + //#endregion Deposit + + //#region Deposit on Vault ETH-B + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "deposit", + inputs: [cdpBId, ethers.utils.parseUnits("3", 17), 0, 0], + }), + ], + userAddress, + { + value: amountToDeposit, + } + ); + + //#endregion Deposit on Vault ETH-B + + //#region Borrow + + await dsa.cast( + [hre.network.config.ConnectMaker], + [ + await hre.run("abi-encode-withselector", { + abi: ConnectMaker.abi, + functionname: "borrow", + inputs: [cdpAId, amountToBorrow, 0, 0], + }), + ], + userAddress + ); + + expect(await DAI.balanceOf(dsa.address)).to.be.equal(amountToBorrow); + + //#endregion Borrow + + const conditionData = await conditionDestVaultWillBeSafe.getConditionData( + cdpAId, + cdpBId, + "ETH-B" + ); + expect( + await conditionDestVaultWillBeSafe.ok(0, conditionData, 0) + ).to.be.equal("OK"); + }); + + it("#6: New Vault Case : destVaultWillBeSafeExplicit should return false when col is lower than borrow amount / spot", async function () { let amountOfColToDepo = amountToBorrow - .mul(ilk[1]) + .mul(ilkB[1]) // ilk[1] represent accumulated rates .div(ethers.utils.parseUnits("1", 27)); amountOfColToDepo = amountOfColToDepo .sub(amountOfColToDepo.div(ethers.utils.parseUnits("10", 0))) .mul(ethers.utils.parseUnits("1", 27)) - .div(ilk[2]); + .div(ilkB[2]); // ilk[2] represent the liquidation ratio of ilk expect( await conditionDestVaultWillBeSafe.destVaultWillBeSafeExplicit( @@ -173,14 +456,14 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { ).to.be.false; }); - it("#3: New Vault Case : destVaultWillBeSafeExplicit should return true when col is greater than borrow amount / spot", async function () { + it("#7: New Vault Case : destVaultWillBeSafeExplicit should return true when col is greater than borrow amount / spot", async function () { let amountOfColToDepo = amountToBorrow - .mul(ilk[1]) + .mul(ilkB[1]) // ilk[1] represent accumulated rates .div(ethers.utils.parseUnits("1", 27)); amountOfColToDepo = amountOfColToDepo .add(amountOfColToDepo.div(ethers.utils.parseUnits("10", 0))) .mul(ethers.utils.parseUnits("1", 27)) - .div(ilk[2]); + .div(ilkB[2]); // ilk[2] represent the liquidation ratio of ilk expect( await conditionDestVaultWillBeSafe.destVaultWillBeSafeExplicit( @@ -192,7 +475,7 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { ).to.be.true; }); - it("#4: Old Vault Case : destVaultWillBeSafeExplicit should return false when col is lower than borrow amount / spot", async function () { + it("#8: Old Vault Case : destVaultWillBeSafeExplicit should return false when col is lower than borrow amount / spot", async function () { const openVault = await hre.run("abi-encode-withselector", { abi: ConnectMaker.abi, functionname: "open", @@ -206,12 +489,12 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { ); let amountOfColToDepo = amountToBorrow - .mul(ilk[1]) + .mul(ilkB[1]) // ilk[1] represent accumulated rates .div(ethers.utils.parseUnits("1", 27)); amountOfColToDepo = amountOfColToDepo .sub(amountOfColToDepo.div(ethers.utils.parseUnits("10", 0))) .mul(ethers.utils.parseUnits("1", 27)) - .div(ilk[2]); + .div(ilkB[2]); // ilk[2] represent the liquidation ratio of ilk expect( await conditionDestVaultWillBeSafe.destVaultWillBeSafeExplicit( @@ -223,30 +506,18 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { ).to.be.false; }); - it("#5: Old Vault Case : destVaultWillBeSafeExplicit should return true when col is lower than borrow amount / spot", async function () { - const openVault = await hre.run("abi-encode-withselector", { - abi: ConnectMaker.abi, - functionname: "open", - inputs: ["ETH-B"], - }); - - await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress); - - const cdpIdB = String( - (await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address)).ids[1] - ); - + it("#9: Old Vault Case : destVaultWillBeSafeExplicit should return true when col is lower than borrow amount / spot", async function () { let amountOfColToDepo = amountToBorrow - .mul(ilk[1]) + .mul(ilkB[1]) // ilk[1] represent accumulated rates .div(ethers.utils.parseUnits("1", 27)); amountOfColToDepo = amountOfColToDepo .add(amountOfColToDepo.div(ethers.utils.parseUnits("10", 0))) .mul(ethers.utils.parseUnits("1", 27)) - .div(ilk[2]); + .div(ilkB[2]); // ilk[2] represent the liquidation ratio of ilk expect( await conditionDestVaultWillBeSafe.destVaultWillBeSafeExplicit( - cdpIdB, + cdpBId, amountToBorrow, amountOfColToDepo, "ETH-B" @@ -254,21 +525,9 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { ).to.be.true; }); - it("#6: Old Vault Case with existing deposit : destVaultWillBeSafeExplicit should return true when col is lower than borrow amount / spot due to initial deposit on Vault B", async function () { - const openVault = await hre.run("abi-encode-withselector", { - abi: ConnectMaker.abi, - functionname: "open", - inputs: ["ETH-B"], - }); - - await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress); - - const cdpIdB = String( - (await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address)).ids[1] - ); - + it("#10: Old Vault Case with existing deposit : destVaultWillBeSafeExplicit should return true when col is lower than borrow amount / spot due to initial deposit on Vault B", async function () { let amountOfColToDepo = amountToBorrow - .mul(ilk[1]) + .mul(ilkB[1]) // ilk[1] represent accumulated rates .div(ethers.utils.parseUnits("1", 27)); const tenPercentOfAmountOfColToDepo = amountOfColToDepo.div( ethers.utils.parseUnits("10", 0) @@ -276,7 +535,7 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { amountOfColToDepo = amountOfColToDepo .sub(tenPercentOfAmountOfColToDepo) .mul(ethers.utils.parseUnits("1", 27)) - .div(ilk[2]); + .div(ilkB[2]); // ilk[2] represent the liquidation ratio of ilk // Deposit For ETH-B Vault await dsa.cast( @@ -285,7 +544,7 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { await hre.run("abi-encode-withselector", { abi: ConnectMaker.abi, functionname: "deposit", - inputs: [cdpIdB, tenPercentOfAmountOfColToDepo, 0, 0], + inputs: [cdpBId, tenPercentOfAmountOfColToDepo, 0, 0], }), ], userAddress, @@ -296,7 +555,7 @@ describe("ConditionDestVaultWillBeSafe Unit Test", function () { expect( await conditionDestVaultWillBeSafe.destVaultWillBeSafeExplicit( - cdpIdB, + cdpBId, amountToBorrow, amountOfColToDepo, "ETH-B"