From de8ad63dda53a0cce903ebfecf1d1c31d95b69f6 Mon Sep 17 00:00:00 2001
From: Shivva <matthieumariejoseph@gmail.com>
Date: Tue, 10 Nov 2020 11:43:04 +0100
Subject: [PATCH] Add new Condition, Liq Pool routing and optional vault
 creation

---
 contracts/constants/CDebtBridge.sol           |   6 +
 contracts/constants/CInstaDapp.sol            |   3 +
 .../ConnectGelatoDataForFullRefinance.sol     | 149 ++++++---
 .../ConditionDebtBridgeIsAffordable.sol       |  82 +++++
 .../functions/gelato/FGelatoDebtBridge.sol    |  32 ++
 .../resolvers/IInstaPoolResolver.sol          |  17 +
 .../ConditionDebtBridgeIsAffordable.deploy.js |  24 ++
 hardhat.config.js                             |   1 +
 .../2_Full-Debt-Bridge-Maker-Compound.test.js |  49 ++-
 ...dge-ETHA-ETHB-With-Vault-Creation.test.js} |  45 ++-
 test/4_Full-Debt-Bridge-ETHA-ETHB.test.js     | 303 ++++++++++++++++++
 test/helpers/services/createVaultForETHB.js   |  35 ++
 test/helpers/services/getConstants.js         |   5 +-
 test/helpers/services/getContracts.js         |  10 +
 .../services/getGasCostForFullRefinance.js    |  16 +
 test/helpers/services/getRoute.js             |  10 +
 ...derWhiteListTaskForMakerETHAToMakerETHB.js |   7 +-
 ...stTaskForMakerETHAToMakerETHBWithVaultB.js |  80 +++++
 ...providerWhiteListTaskForMakerToCompound.js |   5 +-
 ...RefinanceMakerToMakerWithVaultBCreation.js |  85 +++++
 .../4_ConditionDebtBridgeIsAffordable.test.js | 157 +++++++++
 21 files changed, 1060 insertions(+), 61 deletions(-)
 create mode 100644 contracts/constants/CDebtBridge.sol
 create mode 100644 contracts/contracts/gelato/conditions/ConditionDebtBridgeIsAffordable.sol
 create mode 100644 contracts/interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol
 create mode 100644 deploy/gelato/conditions/ConditionDebtBridgeIsAffordable.deploy.js
 rename test/{3_Full-Debt-Bridge-ETHA-ETHB.test.js => 3_Full-Debt-Bridge-ETHA-ETHB-With-Vault-Creation.test.js} (86%)
 create mode 100644 test/4_Full-Debt-Bridge-ETHA-ETHB.test.js
 create mode 100644 test/helpers/services/createVaultForETHB.js
 create mode 100644 test/helpers/services/getGasCostForFullRefinance.js
 create mode 100644 test/helpers/services/getRoute.js
 create mode 100644 test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB.js
 create mode 100644 test/helpers/setupFullRefinanceMakerToMakerWithVaultBCreation.js
 create mode 100644 test/unit_tests/4_ConditionDebtBridgeIsAffordable.test.js

diff --git a/contracts/constants/CDebtBridge.sol b/contracts/constants/CDebtBridge.sol
new file mode 100644
index 0000000..2e4994b
--- /dev/null
+++ b/contracts/constants/CDebtBridge.sol
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.7.4;
+
+function GAS_COSTS_FOR_FULL_REFINANCE() pure returns(uint256[4] memory) {
+    return [uint256(2519000), 3140500, 3971000, 4345000];
+}
\ No newline at end of file
diff --git a/contracts/constants/CInstaDapp.sol b/contracts/constants/CInstaDapp.sol
index b86fab7..f57eaba 100644
--- a/contracts/constants/CInstaDapp.sol
+++ b/contracts/constants/CInstaDapp.sol
@@ -12,3 +12,6 @@ address constant INSTA_POOL_V2 = 0x3150e5A805577366816A1ddc7330c6Ea17070c05;
 // Tokens
 address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
 address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
+
+// Insta Pool
+address constant INSTA_POOL_RESOLVER = 0xa004a5afBa04b74037E9E52bA1f7eb02b5E61509;
\ No newline at end of file
diff --git a/contracts/contracts/connectors/ConnectGelatoDataForFullRefinance.sol b/contracts/contracts/connectors/ConnectGelatoDataForFullRefinance.sol
index 475e5fa..d8514e7 100644
--- a/contracts/contracts/connectors/ConnectGelatoDataForFullRefinance.sol
+++ b/contracts/contracts/connectors/ConnectGelatoDataForFullRefinance.sol
@@ -3,7 +3,7 @@ pragma solidity 0.7.4;
 pragma experimental ABIEncoderV2;
 
 import {GelatoBytes} from "../../lib/GelatoBytes.sol";
-import {sub} from "../../vendor/DSMath.sol";
+import {sub, wmul} from "../../vendor/DSMath.sol";
 import {
     AccountInterface,
     ConnectorInterface
@@ -39,6 +39,11 @@ import {
     _encodeBorrowCompound
 } from "../../functions/InstaDapp/connectors/FConnectCompound.sol";
 import {_getGelatoProviderFees} from "../../functions/gelato/FGelato.sol";
+import {
+    _getFlashLoanRoute,
+    _getGasCost,
+    _getBorrowAmountWithDelta
+} from "../../functions/gelato/FGelatoDebtBridge.sol";
 
 contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
     using GelatoBytes for bytes;
@@ -67,11 +72,13 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
 
     /// @notice Entry Point for DSA.cast DebtBridge from e.g ETH-A to ETH-B
     /// @dev payable to be compatible in conjunction with DSA.cast payable target
-    /// @param _vaultId Id of the unsafe vault of the client.
+    /// @param _vaultAId Id of the unsafe vault of the client of Vault A Collateral.
+    /// @param _vaultBId Id of the vault B Collateral of the client.
     /// @param _token  vault's col token address .
     /// @param _colType colType of the new vault. example : ETH-B, ETH-A.
     function getDataAndCastForFromMakerToMaker(
-        uint256 _vaultId,
+        uint256 _vaultAId,
+        uint256 _vaultBId,
         address _token,
         string calldata _colType
     ) external payable {
@@ -79,7 +86,8 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
             address[] memory targets,
             bytes[] memory datas
         ) = _execPayloadForFullRefinanceFromMakerToMaker(
-            _vaultId,
+            _vaultAId,
+            _vaultBId,
             _token,
             _colType
         );
@@ -124,48 +132,47 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
     /* solhint-disable function-max-lines */
 
     function _execPayloadForFullRefinanceFromMakerToMaker(
-        uint256 _vaultId,
+        uint256 _vaultAId,
+        uint256 _vaultBId,
         address _token,
         string calldata _colType
     ) internal view returns (address[] memory targets, bytes[] memory datas) {
         targets = new address[](1);
         targets[0] = INSTA_POOL_V2;
 
-        uint256 wDaiDebtToMove = _getMakerVaultDebt(_vaultId);
+        uint256 wDaiDebtToMove = _getMakerVaultDebt(_vaultAId);
+        uint256 wDaiToBorrow = _getBorrowAmountWithDelta(wDaiDebtToMove);
         uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance(
-            _vaultId
+            _vaultAId
         );
-        uint256 gasFeesPaidFromCol = _getGelatoProviderFees(GAS_COST);
+        uint256 route = _getFlashLoanRoute(DAI, wDaiDebtToMove);
+        uint256 gasCost = _getGasCost(route);
+        uint256 gasFeesPaidFromCol = _getGelatoProviderFees(gasCost);
 
-        address[] memory _targets = new address[](7);
-        _targets[0] = CONNECT_MAKER; // payback
-        _targets[1] = CONNECT_MAKER; // withdraw
-        _targets[2] = CONNECT_MAKER; // open ETH-B vault
-        _targets[3] = CONNECT_MAKER; // deposit
-        _targets[4] = CONNECT_MAKER; // borrow
-        _targets[5] = _connectGelatoProviderPayment; // payProvider
-        _targets[6] = INSTA_POOL_V2; // flashPayback
-
-        bytes[] memory _datas = new bytes[](7);
-        _datas[0] = _encodePaybackMakerVault(_vaultId, uint256(-1), 0, 0);
-        _datas[1] = _encodedWithdrawMakerVault(_vaultId, uint256(-1), 0, 0);
-        _datas[2] = _encodeOpenMakerVault(_colType);
-        _datas[3] = _encodedDepositMakerVault(
-            0,
-            sub(wColToWithdrawFromMaker, gasFeesPaidFromCol),
-            0,
-            0
-        );
-        _datas[4] = _encodeBorrowDaiMakerVault(0, wDaiDebtToMove, 0, 0);
-        _datas[5] = _encodePayGelatoProvider(_token, gasFeesPaidFromCol, 0, 0);
-        _datas[6] = _encodeFlashPayback(DAI, wDaiDebtToMove, 0, 0);
+        (address[] memory _targets, bytes[] memory _datas) = _vaultBId == 0
+            ? _spellsDebtBridgeWithOpenVaultAction(
+                _vaultAId,
+                _token,
+                _colType,
+                wDaiToBorrow,
+                wColToWithdrawFromMaker,
+                gasFeesPaidFromCol
+            )
+            : _spellsDebtBridge(
+                _vaultAId,
+                _vaultBId,
+                _token,
+                wDaiToBorrow,
+                wColToWithdrawFromMaker,
+                gasFeesPaidFromCol
+            );
 
         datas = new bytes[](1);
         datas[0] = abi.encodeWithSelector(
             IConnectInstaPoolV2.flashBorrowAndCast.selector,
             DAI,
-            wDaiDebtToMove,
-            0,
+            wDaiToBorrow,
+            route,
             abi.encode(_targets, _datas)
         );
     }
@@ -181,7 +188,11 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
         uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance(
             _vaultId
         );
-        uint256 gasFeesPaidFromCol = _getGelatoProviderFees(GAS_COST);
+        uint256 route = _getFlashLoanRoute(DAI, wDaiDebtToMove);
+        uint256 gasCost = _getGasCost(route);
+        uint256 gasFeesPaidFromCol = _getGelatoProviderFees(gasCost);
+
+        uint256 wDaiToBorrow = _getBorrowAmountWithDelta(wDaiDebtToMove);
 
         address[] memory _targets = new address[](6);
         _targets[0] = CONNECT_MAKER; // payback
@@ -192,7 +203,7 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
         _targets[5] = INSTA_POOL_V2; // flashPayback
 
         bytes[] memory _datas = new bytes[](6);
-        _datas[0] = _encodePaybackMakerVault(_vaultId, uint256(-1), 0, 0);
+        _datas[0] = _encodePaybackMakerVault(_vaultId, uint256(-1), 0, 600);
         _datas[1] = _encodedWithdrawMakerVault(_vaultId, uint256(-1), 0, 0);
         _datas[2] = _encodeDepositCompound(
             _token,
@@ -200,19 +211,81 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface {
             0,
             0
         );
-        _datas[3] = _encodeBorrowCompound(DAI, wDaiDebtToMove, 0, 0);
+        _datas[3] = _encodeBorrowCompound(DAI, 0, 600, 0);
         _datas[4] = _encodePayGelatoProvider(_token, gasFeesPaidFromCol, 0, 0);
-        _datas[5] = _encodeFlashPayback(DAI, wDaiDebtToMove, 0, 0);
+        _datas[5] = _encodeFlashPayback(DAI, wDaiToBorrow, 0, 0);
 
         datas = new bytes[](1);
         datas[0] = abi.encodeWithSelector(
             IConnectInstaPoolV2.flashBorrowAndCast.selector,
             DAI,
-            wDaiDebtToMove,
-            0,
+            wDaiToBorrow,
+            route,
             abi.encode(_targets, _datas)
         );
     }
 
+    function _spellsDebtBridgeWithOpenVaultAction(
+        uint256 _vaultAId,
+        address _token,
+        string calldata _colType,
+        uint256 _wDaiToBorrow,
+        uint256 _wColToWithdrawFromMaker,
+        uint256 _gasFeesPaidFromCol
+    ) internal view returns (address[] memory targets, bytes[] memory datas) {
+        targets = new address[](7);
+        targets[0] = CONNECT_MAKER; // payback
+        targets[1] = CONNECT_MAKER; // withdraw
+        targets[2] = CONNECT_MAKER; // open ETH-B vault
+        targets[3] = CONNECT_MAKER; // deposit
+        targets[4] = CONNECT_MAKER; // borrow
+        targets[5] = _connectGelatoProviderPayment; // payProvider
+        targets[6] = INSTA_POOL_V2; // flashPayback
+
+        datas = new bytes[](7);
+        datas[0] = _encodePaybackMakerVault(_vaultAId, uint256(-1), 0, 600);
+        datas[1] = _encodedWithdrawMakerVault(_vaultAId, uint256(-1), 0, 0);
+        datas[2] = _encodeOpenMakerVault(_colType);
+        datas[3] = _encodedDepositMakerVault(
+            0,
+            sub(_wColToWithdrawFromMaker, _gasFeesPaidFromCol),
+            0,
+            0
+        );
+        datas[4] = _encodeBorrowDaiMakerVault(0, 0, 600, 0);
+        datas[5] = _encodePayGelatoProvider(_token, _gasFeesPaidFromCol, 0, 0);
+        datas[6] = _encodeFlashPayback(DAI, _wDaiToBorrow, 0, 0);
+    }
+
+    function _spellsDebtBridge(
+        uint256 _vaultAId,
+        uint256 _vaultBId,
+        address _token,
+        uint256 _wDaiToBorrow,
+        uint256 _wColToWithdrawFromMaker,
+        uint256 _gasFeesPaidFromCol
+    ) internal view returns (address[] memory targets, bytes[] memory datas) {
+        targets = new address[](6);
+        targets[0] = CONNECT_MAKER; // payback
+        targets[1] = CONNECT_MAKER; // withdraw
+        targets[2] = CONNECT_MAKER; // deposit
+        targets[3] = CONNECT_MAKER; // borrow
+        targets[4] = _connectGelatoProviderPayment; // payProvider
+        targets[5] = INSTA_POOL_V2; // flashPayback
+
+        datas = new bytes[](6);
+        datas[0] = _encodePaybackMakerVault(_vaultAId, uint256(-1), 0, 600);
+        datas[1] = _encodedWithdrawMakerVault(_vaultAId, uint256(-1), 0, 0);
+        datas[2] = _encodedDepositMakerVault(
+            _vaultBId,
+            sub(_wColToWithdrawFromMaker, _gasFeesPaidFromCol),
+            0,
+            0
+        );
+        datas[3] = _encodeBorrowDaiMakerVault(_vaultBId, 0, 600, 0);
+        datas[4] = _encodePayGelatoProvider(_token, _gasFeesPaidFromCol, 0, 0);
+        datas[5] = _encodeFlashPayback(DAI, _wDaiToBorrow, 0, 0);
+    }
+
     /* solhint-enable function-max-lines */
 }
diff --git a/contracts/contracts/gelato/conditions/ConditionDebtBridgeIsAffordable.sol b/contracts/contracts/gelato/conditions/ConditionDebtBridgeIsAffordable.sol
new file mode 100644
index 0000000..4274bb7
--- /dev/null
+++ b/contracts/contracts/gelato/conditions/ConditionDebtBridgeIsAffordable.sol
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.7.4;
+pragma experimental ABIEncoderV2;
+
+import {
+    GelatoConditionsStandard
+} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
+import {
+    IGelatoCore
+} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";
+import {GelatoBytes} from "../../../lib/GelatoBytes.sol";
+import {
+    _getMakerVaultDebt,
+    _getMakerVaultCollateralBalance
+} from "../../../functions/dapps/FMaker.sol";
+import {
+    _getFlashLoanRoute,
+    _getGasCost
+} from "../../../functions/gelato/FGelatoDebtBridge.sol";
+import {_getGelatoProviderFees} from "../../../functions/gelato/FGelato.sol";
+import {DAI} from "../../../constants/CInstaDapp.sol";
+import {wdiv} from "../../../vendor/DSMath.sol";
+
+/// @title ConditionDebtBridgeIsAffordable
+/// @notice Condition checking if Debt Refinance is affordable.
+/// @author Gelato Team
+contract ConditionDebtBridgeIsAffordable is GelatoConditionsStandard {
+    using GelatoBytes for bytes;
+
+    /// @notice Convenience function for off-chain _conditionData encoding
+    /// @dev Use the return for your Task's Condition.data field off-chain.
+    /// @dev WARNING _ratioLimit should be in wad standard.
+    /// @return The encoded payload for your Task's Condition.data field.
+    function getConditionData(uint256 _vaultId, uint256 _ratioLimit)
+        public
+        pure
+        virtual
+        returns (bytes memory)
+    {
+        return abi.encode(_vaultId, _ratioLimit);
+    }
+
+    /// @notice Standard GelatoCore system function
+    /// @dev A standard interface for GelatoCore to read Conditions
+    /// @param _conditionData The data you get from `getConditionData()`
+    /// @return OK if the Condition is there, else some error message.
+    function ok(
+        uint256,
+        bytes calldata _conditionData,
+        uint256
+    ) public view virtual override returns (string memory) {
+        (uint256 _vaultID, uint256 _ratioLimit) = abi.decode(
+            _conditionData,
+            (uint256, uint256)
+        );
+
+        return isAffordable(_vaultID, _ratioLimit);
+    }
+
+    /// @notice Specific implementation of this Condition's ok function
+    /// @dev Check if the debt refinancing action is affordable.
+    /// @dev WARNING _ratioLimit should be in wad standard.
+    /// @param _vaultId The id of the Maker vault
+    /// @param _ratioLimit the maximum limit define by the user up on which
+    /// the debt is too expensive for him
+    /// @return OK if the Debt Bridge is affordable, otherwise some error message.
+    function isAffordable(uint256 _vaultId, uint256 _ratioLimit)
+        public
+        view
+        returns (string memory)
+    {
+        uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance(
+            _vaultId
+        );
+        uint256 gasFeesPaidFromCol = _getGelatoProviderFees(
+            _getGasCost(_getFlashLoanRoute(DAI, _getMakerVaultDebt(_vaultId)))
+        );
+        if (wdiv(gasFeesPaidFromCol, wColToWithdrawFromMaker) >= _ratioLimit)
+            return "DebtRefinanceTooExpensive";
+        return OK;
+    }
+}
diff --git a/contracts/functions/gelato/FGelatoDebtBridge.sol b/contracts/functions/gelato/FGelatoDebtBridge.sol
index 1b297c1..5b9c5c4 100644
--- a/contracts/functions/gelato/FGelatoDebtBridge.sol
+++ b/contracts/functions/gelato/FGelatoDebtBridge.sol
@@ -1,7 +1,13 @@
 // SPDX-License-Identifier: UNLICENSED
 pragma solidity 0.7.4;
+pragma experimental ABIEncoderV2;
 
 import {sub, wmul, wdiv} from "../../vendor/DSMath.sol";
+import {INSTA_POOL_RESOLVER} from "../../constants/CInstaDapp.sol";
+import {GAS_COSTS_FOR_FULL_REFINANCE} from "../../constants/CDebtBridge.sol";
+import {
+    IInstaPoolResolver
+} from "../../interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol";
 
 function _wCalcCollateralToWithdraw(
     uint256 _wMinColRatioA,
@@ -47,3 +53,29 @@ function _wCalcDebtToRepay(
             )
         );
 }
+
+function _getFlashLoanRoute(address _tokenA, uint256 _wTokenADebtToMove)
+    view
+    returns (uint256)
+{
+    IInstaPoolResolver.RouteData memory rData = IInstaPoolResolver(
+        INSTA_POOL_RESOLVER
+    )
+        .getTokenLimit(_tokenA);
+
+    if (rData.dydx > _wTokenADebtToMove) return 0;
+    if (rData.maker > _wTokenADebtToMove) return 1;
+    if (rData.compound > _wTokenADebtToMove) return 2;
+    if (rData.aave > _wTokenADebtToMove) return 3;
+    revert(
+        "GelateDebtBridge._getRoute: All route have insufficient liquidties."
+    );
+}
+
+function _getGasCost(uint256 _route) pure returns (uint256) {
+    return GAS_COSTS_FOR_FULL_REFINANCE()[_route];
+}
+
+function _getBorrowAmountWithDelta(uint256 _DebtToMove) pure returns (uint256) {
+    return wmul(_DebtToMove, 1005e15);
+}
diff --git a/contracts/interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol b/contracts/interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol
new file mode 100644
index 0000000..2f786dc
--- /dev/null
+++ b/contracts/interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol
@@ -0,0 +1,17 @@
+// "SPDX-License-Identifier: UNLICENSED"
+pragma solidity 0.7.4;
+pragma experimental ABIEncoderV2;
+
+interface IInstaPoolResolver {
+    struct RouteData {
+        uint256 dydx;
+        uint256 maker;
+        uint256 compound;
+        uint256 aave;
+    }
+
+    function getTokenLimit(address token)
+        external
+        view
+        returns (RouteData memory);
+}
diff --git a/deploy/gelato/conditions/ConditionDebtBridgeIsAffordable.deploy.js b/deploy/gelato/conditions/ConditionDebtBridgeIsAffordable.deploy.js
new file mode 100644
index 0000000..713193e
--- /dev/null
+++ b/deploy/gelato/conditions/ConditionDebtBridgeIsAffordable.deploy.js
@@ -0,0 +1,24 @@
+const {sleep} = require("@gelatonetwork/core");
+
+module.exports = async (hre) => {
+  if (hre.network.name === "mainnet") {
+    console.log(
+      "Deploying ConditionDebtBridgeIsAffordable to mainnet. Hit ctrl + c to abort"
+    );
+    await sleep(10000);
+  }
+
+  const {deployments} = hre;
+  const {deploy} = deployments;
+  const {deployer} = await hre.getNamedAccounts();
+
+  // the following will only deploy "ConditionMakerVaultUnsafe"
+  // if the contract was never deployed or if the code changed since last deployment
+  await deploy("ConditionDebtBridgeIsAffordable", {
+    from: deployer,
+    gasPrice: hre.network.config.gasPrice,
+    log: hre.network.name === "mainnet" ? true : false,
+  });
+};
+
+module.exports.tags = ["ConditionDebtBridgeIsAffordable"];
diff --git a/hardhat.config.js b/hardhat.config.js
index 9c385aa..e52d762 100644
--- a/hardhat.config.js
+++ b/hardhat.config.js
@@ -68,6 +68,7 @@ module.exports = {
       ConnectInstaPool: "0xCeF5f3c402d4fef76A038e89a4357176963e1464",
       MakerResolver: "0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5",
       CompoundResolver: "0x1f22D77365d8BFE3b901C33C83C01B584F946617",
+      InstaPoolResolver: "0xa004a5afBa04b74037E9E52bA1f7eb02b5E61509",
       DAI: "0x6b175474e89094c44da98b954eedeac495271d0f",
       DAI_UNISWAP: "0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667",
       CDAI: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643",
diff --git a/test/2_Full-Debt-Bridge-Maker-Compound.test.js b/test/2_Full-Debt-Bridge-Maker-Compound.test.js
index d50651e..a1642a4 100644
--- a/test/2_Full-Debt-Bridge-Maker-Compound.test.js
+++ b/test/2_Full-Debt-Bridge-Maker-Compound.test.js
@@ -5,6 +5,8 @@ const {deployments, ethers} = hre;
 const GelatoCoreLib = require("@gelatonetwork/core");
 
 const setupFullRefinanceMakerToCompound = require("./helpers/setupFullRefinanceMakerToCompound");
+const getRoute = require("./helpers/services/getRoute");
+const getGasCostForFullRefinance = require("./helpers/services/getGasCostForFullRefinance");
 
 // This test showcases how to submit a task refinancing a Users debt position from
 // Maker to Compound using Gelato
@@ -90,9 +92,20 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function ()
       ),
     });
 
+    const conditionDebtBridgeIsAffordableObj = new GelatoCoreLib.Condition({
+      inst: contracts.conditionDebtBridgeIsAffordable.address,
+      data: await contracts.conditionDebtBridgeIsAffordable.getConditionData(
+        vaultId,
+        constants.MAX_FEES_IN_PERCENT
+      ),
+    });
+
     // ======= GELATO TASK SETUP ======
     const refinanceIfVaultUnsafe = new GelatoCoreLib.Task({
-      conditions: [conditionMakerVaultUnsafeObj],
+      conditions: [
+        conditionMakerVaultUnsafeObj,
+        conditionDebtBridgeIsAffordableObj,
+      ],
       actions: gelatoDebtBridgeSpells,
     });
 
@@ -186,7 +199,15 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function ()
       vaultId
     );
 
-    const gasFeesPaidFromCol = ethers.BigNumber.from(1850000).mul(
+    const route = await getRoute(
+      contracts.DAI.address,
+      debtOnMakerBefore,
+      contracts.instaPoolResolver
+    );
+
+    const gasCost = await getGasCostForFullRefinance(route);
+
+    const gasFeesPaidFromCol = ethers.BigNumber.from(gasCost).mul(
       gelatoGasPrice
     );
 
@@ -248,9 +269,21 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function ()
       .div(await contracts.cEthToken.totalSupply());
 
     // Estimated amount to borrowed token should be equal to the actual one read on compound contracts
-    expect(debtOnMakerBefore).to.be.equal(
-      compoundPosition[0].borrowBalanceStoredUser
-    );
+    if (route === 1) {
+      expect(debtOnMakerBefore).to.be.lte(
+        compoundPosition[0].borrowBalanceStoredUser
+      );
+    } else {
+      expect(debtOnMakerBefore).to.be.equal(
+        compoundPosition[0].borrowBalanceStoredUser
+      );
+
+      // We should not have borrowed DAI on maker
+      const debtOnMakerAfter = await contracts.makerResolver.getMakerVaultDebt(
+        vaultId
+      );
+      expect(debtOnMakerAfter).to.be.equal(ethers.constants.Zero);
+    }
 
     // Estimated amount of collateral should be equal to the actual one read on compound contracts
     expect(
@@ -259,15 +292,11 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function ()
       )
     ).to.be.lt(ethers.utils.parseUnits("1", 12));
 
-    const debtOnMakerAfter = await contracts.makerResolver.getMakerVaultDebt(
-      vaultId
-    );
     const collateralOnMakerAfter = await contracts.makerResolver.getMakerVaultCollateralBalance(
       vaultId
     ); // in Ether.
 
-    // We should not have borrowed DAI on maker or deposited ether on it.
-    expect(debtOnMakerAfter).to.be.equal(ethers.constants.Zero);
+    // We should not have deposited ether on it.
     expect(collateralOnMakerAfter).to.be.equal(ethers.constants.Zero);
 
     // DSA contain 1000 DAI
diff --git a/test/3_Full-Debt-Bridge-ETHA-ETHB.test.js b/test/3_Full-Debt-Bridge-ETHA-ETHB-With-Vault-Creation.test.js
similarity index 86%
rename from test/3_Full-Debt-Bridge-ETHA-ETHB.test.js
rename to test/3_Full-Debt-Bridge-ETHA-ETHB-With-Vault-Creation.test.js
index 4204190..fbd2bb0 100644
--- a/test/3_Full-Debt-Bridge-ETHA-ETHB.test.js
+++ b/test/3_Full-Debt-Bridge-ETHA-ETHB-With-Vault-Creation.test.js
@@ -4,10 +4,12 @@ const {deployments, ethers} = hre;
 const GelatoCoreLib = require("@gelatonetwork/core");
 
 const setupFullRefinanceMakerToMaker = require("./helpers/setupFullRefinanceMakerToMaker");
+const getRoute = require("./helpers/services/getRoute");
+const getGasCostForFullRefinance = require("./helpers/services/getGasCostForFullRefinance");
 
 // This test showcases how to submit a task refinancing a Users debt position from
 // Maker to Compound using Gelato
-describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () {
+describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B with vault creation for ETH-B ", function () {
   this.timeout(0);
   if (hre.network.name !== "hardhat") {
     console.error("Test Suite is meant to be run on hardhat only");
@@ -92,9 +94,20 @@ describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () {
       ),
     });
 
+    const conditionDebtBridgeIsAffordableObj = new GelatoCoreLib.Condition({
+      inst: contracts.conditionDebtBridgeIsAffordable.address,
+      data: await contracts.conditionDebtBridgeIsAffordable.getConditionData(
+        vaultAId,
+        constants.MAX_FEES_IN_PERCENT
+      ),
+    });
+
     // ======= GELATO TASK SETUP ======
     const refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({
-      conditions: [conditionMakerVaultUnsafeObj],
+      conditions: [
+        conditionMakerVaultUnsafeObj,
+        conditionDebtBridgeIsAffordableObj,
+      ],
       actions: gelatoDebtBridgeSpells,
     });
 
@@ -188,7 +201,15 @@ describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () {
       vaultAId
     );
 
-    const gasFeesPaidFromCol = ethers.BigNumber.from(1850000).mul(
+    const route = await getRoute(
+      contracts.DAI.address,
+      debtOnMakerBefore,
+      contracts.instaPoolResolver
+    );
+
+    const gasCost = await getGasCostForFullRefinance(route);
+
+    const gasFeesPaidFromCol = ethers.BigNumber.from(gasCost).mul(
       gelatoGasPrice
     );
 
@@ -250,20 +271,26 @@ describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () {
     );
 
     // Estimated amount to borrowed token should be equal to the actual one read on compound contracts
-    expect(debtOnMakerBefore).to.be.equal(debtOnMakerVaultB);
+    if (route === 1) {
+      expect(debtOnMakerBefore).to.be.lte(debtOnMakerVaultB);
+    } else {
+      expect(debtOnMakerBefore).to.be.equal(debtOnMakerVaultB);
+
+      // We should not have borrowed DAI on maker
+      const debtOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultDebt(
+        vaultAId
+      );
+      expect(debtOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero);
+    }
 
     // Estimated amount of collateral should be equal to the actual one read on compound contracts
     expect(pricedCollateral).to.be.equal(pricedCollateralOnVaultB);
 
-    const debtOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultDebt(
-      vaultAId
-    );
     const collateralOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultCollateralBalance(
       vaultAId
     ); // in Ether.
 
-    // We should not have borrowed DAI on maker or deposited ether on it.
-    expect(debtOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero);
+    // We should not have deposited ether on it.
     expect(collateralOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero);
 
     // DSA has maximum 2 wei DAI in it due to maths inaccuracies
diff --git a/test/4_Full-Debt-Bridge-ETHA-ETHB.test.js b/test/4_Full-Debt-Bridge-ETHA-ETHB.test.js
new file mode 100644
index 0000000..ee6c493
--- /dev/null
+++ b/test/4_Full-Debt-Bridge-ETHA-ETHB.test.js
@@ -0,0 +1,303 @@
+const {expect} = require("chai");
+const hre = require("hardhat");
+const {deployments, ethers} = hre;
+const GelatoCoreLib = require("@gelatonetwork/core");
+
+const setupFullRefinanceMakerToMakerWithVaultBCreation = require("./helpers/setupFullRefinanceMakerToMakerWithVaultBCreation");
+const getRoute = require("./helpers/services/getRoute");
+const getGasCostForFullRefinance = require("./helpers/services/getGasCostForFullRefinance");
+
+// This test showcases how to submit a task refinancing a Users debt position from
+// Maker to Compound using Gelato
+describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () {
+  this.timeout(0);
+  if (hre.network.name !== "hardhat") {
+    console.error("Test Suite is meant to be run on hardhat only");
+    process.exit(1);
+  }
+
+  let contracts;
+  let wallets;
+  let constants;
+  let ABI;
+
+  // Payload Params for ConnectGelatoFullDebtBridgeFromMaker and ConditionMakerVaultUnsafe
+  let vaultAId;
+
+  // For TaskSpec and for Task
+  let gelatoDebtBridgeSpells = [];
+
+  // Cross test var
+  let taskReceipt;
+
+  before(async function () {
+    // Reset back to a fresh forked state during runtime
+    await deployments.fixture();
+
+    const result = await setupFullRefinanceMakerToMakerWithVaultBCreation();
+
+    wallets = result.wallets;
+    contracts = result.contracts;
+    vaultAId = result.vaultAId;
+    gelatoDebtBridgeSpells = result.spells;
+
+    ABI = result.ABI;
+    constants = result.constants;
+  });
+
+  it("#1: DSA authorizes Gelato to cast spells.", async function () {
+    //#region User give authorization to gelato to use his DSA on his behalf.
+
+    // Instadapp DSA contract give the possibility to the user to delegate
+    // action by giving authorization.
+    // In this case user give authorization to gelato to execute
+    // task for him if needed.
+
+    await contracts.dsa.cast(
+      [hre.network.config.ConnectAuth],
+      [
+        await hre.run("abi-encode-withselector", {
+          abi: ABI.ConnectAuthABI,
+          functionname: "add",
+          inputs: [contracts.gelatoCore.address],
+        }),
+      ],
+      wallets.userAddress
+    );
+
+    expect(await contracts.dsa.isAuth(contracts.gelatoCore.address)).to.be.true;
+
+    //#endregion
+  });
+
+  it("#2: User submits automated Debt Bridge task to Gelato via DSA", async function () {
+    //#region User submit a Debt Refinancing task if market move against him
+
+    // User submit the refinancing task if market move against him.
+    // So in this case if the maker vault go to the unsafe area
+    // the refinancing task will be executed and the position
+    // will be split on two position on maker and compound.
+    // It will be done through a algorithm that will optimize the
+    // total borrow rate.
+
+    const conditionMakerVaultUnsafeObj = new GelatoCoreLib.Condition({
+      inst: contracts.conditionMakerVaultUnsafe.address,
+      data: await contracts.conditionMakerVaultUnsafe.getConditionData(
+        vaultAId,
+        contracts.priceOracleResolver.address,
+        await hre.run("abi-encode-withselector", {
+          abi: (await deployments.getArtifact("PriceOracleResolver")).abi,
+          functionname: "getMockPrice",
+          inputs: [wallets.userAddress],
+        }),
+        constants.MIN_COL_RATIO_MAKER
+      ),
+    });
+
+    const conditionDebtBridgeIsAffordableObj = new GelatoCoreLib.Condition({
+      inst: contracts.conditionDebtBridgeIsAffordable.address,
+      data: await contracts.conditionDebtBridgeIsAffordable.getConditionData(
+        vaultAId,
+        constants.MAX_FEES_IN_PERCENT
+      ),
+    });
+
+    // ======= GELATO TASK SETUP ======
+    const refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({
+      conditions: [
+        conditionMakerVaultUnsafeObj,
+        conditionDebtBridgeIsAffordableObj,
+      ],
+      actions: gelatoDebtBridgeSpells,
+    });
+
+    const gelatoExternalProvider = new GelatoCoreLib.GelatoProvider({
+      addr: wallets.gelatoProviderAddress, // Gelato Provider Address
+      module: contracts.dsaProviderModule.address, // Gelato DSA module
+    });
+
+    const expiryDate = 0;
+
+    await expect(
+      contracts.dsa.cast(
+        [contracts.connectGelato.address], // targets
+        [
+          await hre.run("abi-encode-withselector", {
+            abi: ABI.ConnectGelatoABI,
+            functionname: "submitTask",
+            inputs: [
+              gelatoExternalProvider,
+              refinanceFromEthAToBIfVaultUnsafe,
+              expiryDate,
+            ],
+          }),
+        ], // datas
+        wallets.userAddress, // origin
+        {
+          gasLimit: 5000000,
+        }
+      )
+    ).to.emit(contracts.gelatoCore, "LogTaskSubmitted");
+
+    taskReceipt = new GelatoCoreLib.TaskReceipt({
+      id: await contracts.gelatoCore.currentTaskReceiptId(),
+      userProxy: contracts.dsa.address,
+      provider: gelatoExternalProvider,
+      tasks: [refinanceFromEthAToBIfVaultUnsafe],
+      expiryDate,
+    });
+
+    //#endregion
+  });
+
+  // This test showcases the part which is automatically done by the Gelato Executor Network on mainnet
+  // Bots constatly check whether the submitted task is executable (by calling canExec)
+  // If the task becomes executable (returns "OK"), the "exec" function will be called
+  // which will execute the debt refinancing on behalf of the user
+  it("#3: Auto-refinance from ETH-A to ETH-B, if the Maker vault became unsafe.", async function () {
+    // Steps
+    // Step 1: Market Move against the user (Mock)
+    // Step 2: Executor execute the user's task
+
+    //#region Step 1 Market Move against the user (Mock)
+
+    // Ether market price went from the current price to 250$
+
+    const gelatoGasPrice = await hre.run("fetchGelatoGasPrice");
+    expect(gelatoGasPrice).to.be.lte(constants.GAS_PRICE_CEIL);
+
+    // TO DO: base mock price off of real price data
+    await contracts.priceOracleResolver.setMockPrice(
+      ethers.utils.parseUnits("400", 18)
+    );
+
+    expect(
+      await contracts.gelatoCore
+        .connect(wallets.gelatoExecutorWallet)
+        .canExec(taskReceipt, constants.GAS_LIMIT, gelatoGasPrice)
+    ).to.be.equal("ConditionNotOk:MakerVaultNotUnsafe");
+
+    // TO DO: base mock price off of real price data
+    await contracts.priceOracleResolver.setMockPrice(
+      ethers.utils.parseUnits("250", 18)
+    );
+
+    expect(
+      await contracts.gelatoCore
+        .connect(wallets.gelatoExecutorWallet)
+        .canExec(taskReceipt, constants.GAS_LIMIT, gelatoGasPrice)
+    ).to.be.equal("OK");
+
+    //#endregion
+
+    //#region Step 2 Executor execute the user's task
+
+    // The market move make the vault unsafe, so the executor
+    // will execute the user's task to make the user position safe
+    // by a debt refinancing in compound.
+
+    //#region EXPECTED OUTCOME
+    const debtOnMakerBefore = await contracts.makerResolver.getMakerVaultDebt(
+      vaultAId
+    );
+
+    const route = await getRoute(
+      contracts.DAI.address,
+      debtOnMakerBefore,
+      contracts.instaPoolResolver
+    );
+
+    const gasCost = await getGasCostForFullRefinance(route);
+
+    const gasFeesPaidFromCol = ethers.BigNumber.from(gasCost).mul(
+      gelatoGasPrice
+    );
+
+    const pricedCollateral = (
+      await contracts.makerResolver.getMakerVaultCollateralBalance(vaultAId)
+    ).sub(gasFeesPaidFromCol);
+
+    //#endregion
+    const providerBalanceBeforeExecution = await contracts.gelatoCore.providerFunds(
+      wallets.gelatoProviderAddress
+    );
+
+    await expect(
+      contracts.gelatoCore
+        .connect(wallets.gelatoExecutorWallet)
+        .exec(taskReceipt, {
+          gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei)
+          gasLimit: constants.GAS_LIMIT,
+        })
+    ).to.emit(contracts.gelatoCore, "LogExecSuccess");
+
+    // 🚧 For Debugging:
+    // const txResponse2 = await contracts.gelatoCore
+    //   .connect(wallets.gelatoExecutorWallet)
+    //   .exec(taskReceipt, {
+    //     gasPrice: gelatoGasPrice,
+    //     gasLimit: constants.GAS_LIMIT,
+    //   });
+    // const {blockHash} = await txResponse2.wait();
+    // const logs = await ethers.provider.getLogs({blockHash});
+    // const iFace = new ethers.utils.Interface(GelatoCoreLib.GelatoCore.abi);
+    // for (const log of logs) {
+    //   console.log(iFace.parseLog(log).args.reason);
+    // }
+    // await GelatoCoreLib.sleep(10000);
+
+    const cdps = await contracts.getCdps.getCdpsAsc(
+      contracts.dssCdpManager.address,
+      contracts.dsa.address
+    );
+    let vaultBId = String(cdps.ids[1]);
+    expect(cdps.ids[1].isZero()).to.be.false;
+
+    const debtOnMakerVaultB = await contracts.makerResolver.getMakerVaultDebt(
+      vaultBId
+    );
+    const pricedCollateralOnVaultB = await contracts.makerResolver.getMakerVaultCollateralBalance(
+      vaultBId
+    );
+
+    expect(
+      await contracts.gelatoCore.providerFunds(wallets.gelatoProviderAddress)
+    ).to.be.gt(
+      providerBalanceBeforeExecution.sub(
+        gasFeesPaidFromCol
+          .mul(await contracts.gelatoCore.totalSuccessShare())
+          .div(100)
+      )
+    );
+
+    // Estimated amount to borrowed token should be equal to the actual one read on compound contracts
+    if (route === 1) {
+      expect(debtOnMakerBefore).to.be.lte(debtOnMakerVaultB);
+    } else {
+      expect(debtOnMakerBefore).to.be.equal(debtOnMakerVaultB);
+
+      // We should not have borrowed DAI on maker
+      const debtOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultDebt(
+        vaultAId
+      );
+      expect(debtOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero);
+    }
+
+    // Estimated amount of collateral should be equal to the actual one read on compound contracts
+    expect(pricedCollateral).to.be.equal(pricedCollateralOnVaultB);
+
+    const collateralOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultCollateralBalance(
+      vaultAId
+    ); // in Ether.
+
+    // We should not have deposited ether on it.
+    expect(collateralOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero);
+
+    // DSA has maximum 2 wei DAI in it due to maths inaccuracies
+    expect(await contracts.DAI.balanceOf(contracts.dsa.address)).to.be.equal(
+      constants.MAKER_INITIAL_DEBT
+    );
+
+    //#endregion
+  });
+});
diff --git a/test/helpers/services/createVaultForETHB.js b/test/helpers/services/createVaultForETHB.js
new file mode 100644
index 0000000..9d54af1
--- /dev/null
+++ b/test/helpers/services/createVaultForETHB.js
@@ -0,0 +1,35 @@
+const {expect} = require("chai");
+const hre = require("hardhat");
+
+const ConnectMaker = require("../../../pre-compiles/ConnectMaker.json");
+
+async function createVaultForETHB(
+  userAddress,
+  DAI,
+  dsa,
+  getCdps,
+  dssCdpManager
+) {
+  //#region Step 8 User open a Vault, put some ether on it and borrow some dai
+
+  // User open a maker vault
+  // He deposit 10 Eth on it
+  // He borrow a 1000 DAI
+  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 cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
+  let vaultId = String(cdps.ids[1]);
+  expect(cdps.ids[1].isZero()).to.be.false;
+
+  //#endregion
+
+  return vaultId;
+}
+
+module.exports = createVaultForETHB;
diff --git a/test/helpers/services/getConstants.js b/test/helpers/services/getConstants.js
index c14bc8a..035557c 100644
--- a/test/helpers/services/getConstants.js
+++ b/test/helpers/services/getConstants.js
@@ -2,7 +2,7 @@ const hre = require("hardhat");
 const {ethers} = hre;
 
 const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
-const GAS_LIMIT = "4000000";
+const GAS_LIMIT = "5000000";
 const GAS_PRICE_CEIL = ethers.utils.parseUnits("1000", "gwei");
 
 const MIN_COL_RATIO_MAKER = ethers.utils.parseUnits("3", 18);
@@ -11,6 +11,8 @@ const MIN_COL_RATIO_MAKER = ethers.utils.parseUnits("3", 18);
 const MAKER_INITIAL_ETH = ethers.utils.parseEther("10");
 const MAKER_INITIAL_DEBT = ethers.utils.parseUnits("1000", 18);
 
+const MAX_FEES_IN_PERCENT = ethers.utils.parseUnits("1", 17);
+
 async function getConstants() {
   return {
     ETH: ETH,
@@ -19,6 +21,7 @@ async function getConstants() {
     GAS_LIMIT: GAS_LIMIT,
     MAKER_INITIAL_DEBT: MAKER_INITIAL_DEBT,
     MAKER_INITIAL_ETH: MAKER_INITIAL_ETH,
+    MAX_FEES_IN_PERCENT: MAX_FEES_IN_PERCENT,
   };
 }
 
diff --git a/test/helpers/services/getContracts.js b/test/helpers/services/getContracts.js
index 3ff71d9..8797fe3 100644
--- a/test/helpers/services/getContracts.js
+++ b/test/helpers/services/getContracts.js
@@ -17,6 +17,7 @@ const IERC20 = require("../../../pre-compiles/IERC20.json");
 const CTokenInterface = require("../../../pre-compiles/CTokenInterface.json");
 const CompoundResolver = require("../../../pre-compiles/InstaCompoundResolver.json");
 const DsaProviderModuleABI = require("../../../pre-compiles/ProviderModuleDsa_ABI.json");
+const InstaPoolResolver = require("../../../artifacts/contracts/interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol/IInstaPoolResolver.json");
 
 async function getContracts() {
   const instaMaster = await ethers.provider.getSigner(
@@ -85,12 +86,19 @@ async function getContracts() {
     DsaProviderModuleABI,
     hre.network.config.ProviderModuleDsa
   );
+  const instaPoolResolver = await ethers.getContractAt(
+    InstaPoolResolver.abi,
+    hre.network.config.InstaPoolResolver
+  );
 
   // ===== Get deployed contracts ==================
   const priceOracleResolver = await ethers.getContract("PriceOracleResolver");
   const conditionMakerVaultUnsafe = await ethers.getContract(
     "ConditionMakerVaultUnsafe"
   );
+  const conditionDebtBridgeIsAffordable = await ethers.getContract(
+    "ConditionDebtBridgeIsAffordable"
+  );
   const connectGelatoProviderPayment = await ethers.getContract(
     "ConnectGelatoProviderPayment"
   );
@@ -122,7 +130,9 @@ async function getContracts() {
     priceOracleResolver,
     dsa: ethers.constants.AddressZero,
     makerResolver,
+    instaPoolResolver,
     dsaProviderModule,
+    conditionDebtBridgeIsAffordable,
   };
 }
 
diff --git a/test/helpers/services/getGasCostForFullRefinance.js b/test/helpers/services/getGasCostForFullRefinance.js
new file mode 100644
index 0000000..1b245c0
--- /dev/null
+++ b/test/helpers/services/getGasCostForFullRefinance.js
@@ -0,0 +1,16 @@
+async function getGasCostForFullRefinance(route) {
+  switch (route) {
+    case 0:
+      return 2519000; // 2290000 * 1,1
+    case 1:
+      return 3140500; // 2855000 * 1,1
+    case 2:
+      return 3971000; // 3610000 * 1,1
+    case 3:
+      return 4345000; // 3950000 * 1,1
+    default:
+      break;
+  }
+}
+
+module.exports = getGasCostForFullRefinance;
diff --git a/test/helpers/services/getRoute.js b/test/helpers/services/getRoute.js
new file mode 100644
index 0000000..69fa728
--- /dev/null
+++ b/test/helpers/services/getRoute.js
@@ -0,0 +1,10 @@
+async function getRoute(token, tokenDebtToMove, instaPoolResolver) {
+  const rData = await instaPoolResolver.getTokenLimit(token);
+
+  if (rData.dydx > tokenDebtToMove) return 0;
+  if (rData.maker > tokenDebtToMove) return 1;
+  if (rData.compound > tokenDebtToMove) return 2;
+  if (rData.aave > tokenDebtToMove) return 3;
+}
+
+module.exports = getRoute;
diff --git a/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHB.js b/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHB.js
index 88db46f..135cb13 100644
--- a/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHB.js
+++ b/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHB.js
@@ -25,7 +25,7 @@ async function providerWhiteListTaskForMakerETHAToMakerETHB(
       abi: (await deployments.getArtifact("ConnectGelatoDataForFullRefinance"))
         .abi,
       functionname: "getDataAndCastForFromMakerToMaker",
-      inputs: [vaultId, constants.ETH, "ETH-B"],
+      inputs: [vaultId, 0, constants.ETH, "ETH-B"],
     }),
     operation: GelatoCoreLib.Operation.Delegatecall,
   });
@@ -36,7 +36,10 @@ async function providerWhiteListTaskForMakerETHAToMakerETHB(
 
   const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec(
     {
-      conditions: [contracts.conditionMakerVaultUnsafe.address],
+      conditions: [
+        contracts.conditionMakerVaultUnsafe.address,
+        contracts.conditionDebtBridgeIsAffordable.address,
+      ],
       actions: spells,
       gasPriceCeil,
     }
diff --git a/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB.js b/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB.js
new file mode 100644
index 0000000..835b463
--- /dev/null
+++ b/test/helpers/services/providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB.js
@@ -0,0 +1,80 @@
+const {expect} = require("chai");
+const hre = require("hardhat");
+const {deployments, ethers} = hre;
+const GelatoCoreLib = require("@gelatonetwork/core");
+
+// Instadapp UI should do the same implementation for submitting debt bridge task
+async function providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB(
+  wallets,
+  contracts,
+  constants,
+  vaultAId,
+  vaultBId
+) {
+  //#region Step 9 Provider should whitelist task
+
+  // By WhiteList task, the provider can constrain the type
+  // of task the user can submitting.
+
+  //#region Actions
+
+  const spells = [];
+
+  const debtBridgeCalculationForFullRefinance = new GelatoCoreLib.Action({
+    addr: contracts.connectGelatoDataForFullRefinance.address,
+    data: await hre.run("abi-encode-withselector", {
+      abi: (await deployments.getArtifact("ConnectGelatoDataForFullRefinance"))
+        .abi,
+      functionname: "getDataAndCastForFromMakerToMaker",
+      inputs: [vaultAId, vaultBId, constants.ETH, "ETH-B"],
+    }),
+    operation: GelatoCoreLib.Operation.Delegatecall,
+  });
+
+  spells.push(debtBridgeCalculationForFullRefinance);
+
+  const gasPriceCeil = ethers.constants.MaxUint256;
+
+  const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec(
+    {
+      conditions: [
+        contracts.conditionMakerVaultUnsafe.address,
+        contracts.conditionDebtBridgeIsAffordable.address,
+      ],
+      actions: spells,
+      gasPriceCeil,
+    }
+  );
+
+  await expect(
+    contracts.gelatoCore
+      .connect(wallets.gelatoProviderWallet)
+      .provideTaskSpecs([connectGelatoFullDebtBridgeFromMakerTaskSpec])
+  ).to.emit(contracts.gelatoCore, "LogTaskSpecProvided");
+
+  expect(
+    await contracts.gelatoCore
+      .connect(wallets.gelatoProviderWallet)
+      .isTaskSpecProvided(
+        wallets.gelatoProviderAddress,
+        connectGelatoFullDebtBridgeFromMakerTaskSpec
+      )
+  ).to.be.equal("OK");
+
+  expect(
+    await contracts.gelatoCore
+      .connect(wallets.gelatoProviderWallet)
+      .taskSpecGasPriceCeil(
+        wallets.gelatoProviderAddress,
+        await contracts.gelatoCore
+          .connect(wallets.gelatoProviderWallet)
+          .hashTaskSpec(connectGelatoFullDebtBridgeFromMakerTaskSpec)
+      )
+  ).to.be.equal(gasPriceCeil);
+
+  //#endregion
+
+  return spells;
+}
+
+module.exports = providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB;
diff --git a/test/helpers/services/providerWhiteListTaskForMakerToCompound.js b/test/helpers/services/providerWhiteListTaskForMakerToCompound.js
index 409368d..38b64b5 100644
--- a/test/helpers/services/providerWhiteListTaskForMakerToCompound.js
+++ b/test/helpers/services/providerWhiteListTaskForMakerToCompound.js
@@ -36,7 +36,10 @@ async function providerWhiteListTaskForMakerToCompound(
 
   const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec(
     {
-      conditions: [contracts.conditionMakerVaultUnsafe.address],
+      conditions: [
+        contracts.conditionMakerVaultUnsafe.address,
+        contracts.conditionDebtBridgeIsAffordable.address,
+      ],
       actions: spells,
       gasPriceCeil,
     }
diff --git a/test/helpers/setupFullRefinanceMakerToMakerWithVaultBCreation.js b/test/helpers/setupFullRefinanceMakerToMakerWithVaultBCreation.js
new file mode 100644
index 0000000..80d5be6
--- /dev/null
+++ b/test/helpers/setupFullRefinanceMakerToMakerWithVaultBCreation.js
@@ -0,0 +1,85 @@
+const getWallets = require("./services/getWallets");
+const getContracts = require("./services/getContracts");
+const getConstants = require("./services/getConstants");
+const stakeExecutor = require("./services/stakeExecutor");
+const provideFunds = require("./services/provideFunds");
+const providerAssignsExecutor = require("./services/providerAssignsExecutor");
+const addProviderModuleDSA = require("./services/addProviderModuleDSA");
+const createDSA = require("./services/createDSA");
+const addETHBGemJoinMapping = require("./services/addETHBGemJoinMapping");
+const initializeMakerCdp = require("./services/initializeMakerCdp");
+const createVaultForETHB = require("./services/createVaultForETHB");
+const providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB = require("./services/providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB");
+const getABI = require("./services/getABI");
+
+async function setupFullRefinanceMakerToMakerWithVaultBCreation() {
+  const wallets = await getWallets();
+  const contracts = await getContracts();
+  const constants = await getConstants();
+
+  // Gelato Testing environment setup.
+  await stakeExecutor(wallets.gelatoExecutorWallet, contracts.gelatoCore);
+  await provideFunds(
+    wallets.gelatoProviderWallet,
+    contracts.gelatoCore,
+    constants.GAS_LIMIT,
+    constants.GAS_PRICE_CEIL
+  );
+  await providerAssignsExecutor(
+    wallets.gelatoProviderWallet,
+    wallets.gelatoExecutorAddress,
+    contracts.gelatoCore
+  );
+  await addProviderModuleDSA(
+    wallets.gelatoProviderWallet,
+    contracts.gelatoCore,
+    contracts.dsaProviderModule.address
+  );
+  contracts.dsa = await createDSA(
+    wallets.userAddress,
+    contracts.instaIndex,
+    contracts.instaList
+  );
+  await addETHBGemJoinMapping(
+    wallets.userWallet,
+    contracts.instaMapping,
+    contracts.instaMaster
+  );
+  const vaultAId = await initializeMakerCdp(
+    wallets.userAddress,
+    contracts.DAI,
+    contracts.dsa,
+    contracts.getCdps,
+    contracts.dssCdpManager,
+    constants.MAKER_INITIAL_ETH,
+    constants.MAKER_INITIAL_DEBT
+  );
+  const vaultBId = await createVaultForETHB(
+    wallets.userAddress,
+    contracts.DAI,
+    contracts.dsa,
+    contracts.getCdps,
+    contracts.dssCdpManager
+  );
+  const spells = await providerWhiteListTaskForMakerETHAToMakerETHBWithVaultB(
+    wallets,
+    contracts,
+    constants,
+    vaultAId,
+    vaultBId
+  );
+
+  const ABI = getABI();
+
+  return {
+    wallets,
+    contracts,
+    constants,
+    vaultAId,
+    vaultBId,
+    spells,
+    ABI,
+  };
+}
+
+module.exports = setupFullRefinanceMakerToMakerWithVaultBCreation;
diff --git a/test/unit_tests/4_ConditionDebtBridgeIsAffordable.test.js b/test/unit_tests/4_ConditionDebtBridgeIsAffordable.test.js
new file mode 100644
index 0000000..5a4c91f
--- /dev/null
+++ b/test/unit_tests/4_ConditionDebtBridgeIsAffordable.test.js
@@ -0,0 +1,157 @@
+const {expect} = require("chai");
+const hre = require("hardhat");
+const {deployments, ethers} = hre;
+
+// #region Contracts ABI
+
+const ConnectMaker = require("../../pre-compiles/ConnectMaker.json");
+const GetCdps = require("../../pre-compiles/GetCdps.json");
+const DssCdpManager = require("../../pre-compiles/DssCdpManager.json");
+const InstaList = require("../../pre-compiles/InstaList.json");
+const InstaAccount = require("../../pre-compiles/InstaAccount.json");
+const InstaIndex = require("../../pre-compiles/InstaIndex.json");
+const IERC20 = require("../../pre-compiles/IERC20.json");
+
+// #endregion
+
+describe("ConditionDebtBridgeIsAffordable Unit Test", function () {
+  this.timeout(0);
+  if (hre.network.name !== "hardhat") {
+    console.error("Test Suite is meant to be run on hardhat only");
+    process.exit(1);
+  }
+
+  let userWallet;
+  let userAddress;
+
+  let getCdps;
+  let dssCdpManager;
+  let instaList;
+  let instaIndex;
+  let DAI;
+
+  let conditionDebtBridgeIsAffordable;
+
+  let dsa;
+  let cdpId;
+
+  beforeEach(async function () {
+    // Deploy contract dependencies
+    await deployments.fixture();
+
+    // Get Test Wallet for local testnet
+    [userWallet] = await ethers.getSigners();
+    userAddress = await userWallet.getAddress();
+
+    // Hardhat default accounts prefilled with 100 ETH
+    expect(await userWallet.getBalance()).to.be.gt(
+      ethers.utils.parseEther("10")
+    );
+
+    instaIndex = await ethers.getContractAt(
+      InstaIndex.abi,
+      hre.network.config.InstaIndex
+    );
+    instaList = await ethers.getContractAt(
+      InstaList.abi,
+      hre.network.config.InstaList
+    );
+    getCdps = await ethers.getContractAt(
+      GetCdps.abi,
+      hre.network.config.GetCdps
+    );
+    dssCdpManager = await ethers.getContractAt(
+      DssCdpManager.abi,
+      hre.network.config.DssCdpManager
+    );
+    DAI = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
+
+    // ========== Test Setup ============
+    conditionDebtBridgeIsAffordable = await ethers.getContract(
+      "ConditionDebtBridgeIsAffordable"
+    );
+
+    // Create DeFi Smart Account
+
+    const dsaAccountCount = await instaList.accounts();
+
+    await expect(instaIndex.build(userAddress, 1, userAddress)).to.emit(
+      instaIndex,
+      "LogAccountCreated"
+    );
+    const dsaID = dsaAccountCount.add(1);
+    await expect(await instaList.accounts()).to.be.equal(dsaID);
+
+    // Instantiate the DSA
+    dsa = await ethers.getContractAt(
+      InstaAccount.abi,
+      await instaList.accountAddr(dsaID)
+    );
+
+    // Create/Deposit/Borrow a Vault
+    const openVault = await hre.run("abi-encode-withselector", {
+      abi: ConnectMaker.abi,
+      functionname: "open",
+      inputs: ["ETH-A"],
+    });
+
+    await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress);
+
+    let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
+    cdpId = String(cdps.ids[0]);
+
+    expect(cdps.ids[0].isZero()).to.be.false;
+
+    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],
+        }),
+      ],
+      userAddress,
+      {
+        value: ethers.utils.parseEther("10"),
+      }
+    );
+    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],
+        }),
+      ],
+      userAddress
+    );
+
+    expect(await DAI.balanceOf(dsa.address)).to.be.equal(
+      ethers.utils.parseEther("1000")
+    );
+  });
+
+  it("#1: ok should return DebtRefinanceTooExpensive when the gas fees exceed a define amount", async function () {
+    const conditionData = await conditionDebtBridgeIsAffordable.getConditionData(
+      cdpId,
+      ethers.utils.parseUnits("5", 15)
+    );
+
+    expect(
+      await conditionDebtBridgeIsAffordable.ok(0, conditionData, 0)
+    ).to.be.equal("DebtRefinanceTooExpensive");
+  });
+
+  it("#2: ok should return OK when the gas fees not exceed a define amount", async function () {
+    const conditionData = await conditionDebtBridgeIsAffordable.getConditionData(
+      cdpId,
+      ethers.utils.parseUnits("10", 15)
+    );
+
+    expect(
+      await conditionDebtBridgeIsAffordable.ok(0, conditionData, 0)
+    ).to.be.equal("OK");
+  });
+});