mirror of
				https://github.com/Instadapp/Gelato-automations.git
				synced 2024-07-29 22:28:07 +00:00 
			
		
		
		
	Add new Condition, Liq Pool routing and optional vault creation
This commit is contained in:
		
							parent
							
								
									49672c1826
								
							
						
					
					
						commit
						de8ad63dda
					
				
							
								
								
									
										6
									
								
								contracts/constants/CDebtBridge.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contracts/constants/CDebtBridge.sol
									
									
									
									
									
										Normal file
									
								
							|  | @ -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]; | ||||||
|  | } | ||||||
|  | @ -12,3 +12,6 @@ address constant INSTA_POOL_V2 = 0x3150e5A805577366816A1ddc7330c6Ea17070c05; | ||||||
| // Tokens | // Tokens | ||||||
| address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; | address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; | ||||||
| address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; | address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; | ||||||
|  | 
 | ||||||
|  | // Insta Pool | ||||||
|  | address constant INSTA_POOL_RESOLVER = 0xa004a5afBa04b74037E9E52bA1f7eb02b5E61509; | ||||||
|  | @ -3,7 +3,7 @@ pragma solidity 0.7.4; | ||||||
| pragma experimental ABIEncoderV2; | pragma experimental ABIEncoderV2; | ||||||
| 
 | 
 | ||||||
| import {GelatoBytes} from "../../lib/GelatoBytes.sol"; | import {GelatoBytes} from "../../lib/GelatoBytes.sol"; | ||||||
| import {sub} from "../../vendor/DSMath.sol"; | import {sub, wmul} from "../../vendor/DSMath.sol"; | ||||||
| import { | import { | ||||||
|     AccountInterface, |     AccountInterface, | ||||||
|     ConnectorInterface |     ConnectorInterface | ||||||
|  | @ -39,6 +39,11 @@ import { | ||||||
|     _encodeBorrowCompound |     _encodeBorrowCompound | ||||||
| } from "../../functions/InstaDapp/connectors/FConnectCompound.sol"; | } from "../../functions/InstaDapp/connectors/FConnectCompound.sol"; | ||||||
| import {_getGelatoProviderFees} from "../../functions/gelato/FGelato.sol"; | import {_getGelatoProviderFees} from "../../functions/gelato/FGelato.sol"; | ||||||
|  | import { | ||||||
|  |     _getFlashLoanRoute, | ||||||
|  |     _getGasCost, | ||||||
|  |     _getBorrowAmountWithDelta | ||||||
|  | } from "../../functions/gelato/FGelatoDebtBridge.sol"; | ||||||
| 
 | 
 | ||||||
| contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | ||||||
|     using GelatoBytes for bytes; |     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 |     /// @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 |     /// @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 _token  vault's col token address . | ||||||
|     /// @param _colType colType of the new vault. example : ETH-B, ETH-A. |     /// @param _colType colType of the new vault. example : ETH-B, ETH-A. | ||||||
|     function getDataAndCastForFromMakerToMaker( |     function getDataAndCastForFromMakerToMaker( | ||||||
|         uint256 _vaultId, |         uint256 _vaultAId, | ||||||
|  |         uint256 _vaultBId, | ||||||
|         address _token, |         address _token, | ||||||
|         string calldata _colType |         string calldata _colType | ||||||
|     ) external payable { |     ) external payable { | ||||||
|  | @ -79,7 +86,8 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | ||||||
|             address[] memory targets, |             address[] memory targets, | ||||||
|             bytes[] memory datas |             bytes[] memory datas | ||||||
|         ) = _execPayloadForFullRefinanceFromMakerToMaker( |         ) = _execPayloadForFullRefinanceFromMakerToMaker( | ||||||
|             _vaultId, |             _vaultAId, | ||||||
|  |             _vaultBId, | ||||||
|             _token, |             _token, | ||||||
|             _colType |             _colType | ||||||
|         ); |         ); | ||||||
|  | @ -124,48 +132,47 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | ||||||
|     /* solhint-disable function-max-lines */ |     /* solhint-disable function-max-lines */ | ||||||
| 
 | 
 | ||||||
|     function _execPayloadForFullRefinanceFromMakerToMaker( |     function _execPayloadForFullRefinanceFromMakerToMaker( | ||||||
|         uint256 _vaultId, |         uint256 _vaultAId, | ||||||
|  |         uint256 _vaultBId, | ||||||
|         address _token, |         address _token, | ||||||
|         string calldata _colType |         string calldata _colType | ||||||
|     ) internal view returns (address[] memory targets, bytes[] memory datas) { |     ) internal view returns (address[] memory targets, bytes[] memory datas) { | ||||||
|         targets = new address[](1); |         targets = new address[](1); | ||||||
|         targets[0] = INSTA_POOL_V2; |         targets[0] = INSTA_POOL_V2; | ||||||
| 
 | 
 | ||||||
|         uint256 wDaiDebtToMove = _getMakerVaultDebt(_vaultId); |         uint256 wDaiDebtToMove = _getMakerVaultDebt(_vaultAId); | ||||||
|  |         uint256 wDaiToBorrow = _getBorrowAmountWithDelta(wDaiDebtToMove); | ||||||
|         uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance( |         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); |         (address[] memory _targets, bytes[] memory _datas) = _vaultBId == 0 | ||||||
|         _targets[0] = CONNECT_MAKER; // payback |             ? _spellsDebtBridgeWithOpenVaultAction( | ||||||
|         _targets[1] = CONNECT_MAKER; // withdraw |                 _vaultAId, | ||||||
|         _targets[2] = CONNECT_MAKER; // open ETH-B vault |                 _token, | ||||||
|         _targets[3] = CONNECT_MAKER; // deposit |                 _colType, | ||||||
|         _targets[4] = CONNECT_MAKER; // borrow |                 wDaiToBorrow, | ||||||
|         _targets[5] = _connectGelatoProviderPayment; // payProvider |                 wColToWithdrawFromMaker, | ||||||
|         _targets[6] = INSTA_POOL_V2; // flashPayback |                 gasFeesPaidFromCol | ||||||
| 
 |             ) | ||||||
|         bytes[] memory _datas = new bytes[](7); |             : _spellsDebtBridge( | ||||||
|         _datas[0] = _encodePaybackMakerVault(_vaultId, uint256(-1), 0, 0); |                 _vaultAId, | ||||||
|         _datas[1] = _encodedWithdrawMakerVault(_vaultId, uint256(-1), 0, 0); |                 _vaultBId, | ||||||
|         _datas[2] = _encodeOpenMakerVault(_colType); |                 _token, | ||||||
|         _datas[3] = _encodedDepositMakerVault( |                 wDaiToBorrow, | ||||||
|             0, |                 wColToWithdrawFromMaker, | ||||||
|             sub(wColToWithdrawFromMaker, gasFeesPaidFromCol), |                 gasFeesPaidFromCol | ||||||
|             0, |  | ||||||
|             0 |  | ||||||
|             ); |             ); | ||||||
|         _datas[4] = _encodeBorrowDaiMakerVault(0, wDaiDebtToMove, 0, 0); |  | ||||||
|         _datas[5] = _encodePayGelatoProvider(_token, gasFeesPaidFromCol, 0, 0); |  | ||||||
|         _datas[6] = _encodeFlashPayback(DAI, wDaiDebtToMove, 0, 0); |  | ||||||
| 
 | 
 | ||||||
|         datas = new bytes[](1); |         datas = new bytes[](1); | ||||||
|         datas[0] = abi.encodeWithSelector( |         datas[0] = abi.encodeWithSelector( | ||||||
|             IConnectInstaPoolV2.flashBorrowAndCast.selector, |             IConnectInstaPoolV2.flashBorrowAndCast.selector, | ||||||
|             DAI, |             DAI, | ||||||
|             wDaiDebtToMove, |             wDaiToBorrow, | ||||||
|             0, |             route, | ||||||
|             abi.encode(_targets, _datas) |             abi.encode(_targets, _datas) | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | @ -181,7 +188,11 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | ||||||
|         uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance( |         uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance( | ||||||
|             _vaultId |             _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); |         address[] memory _targets = new address[](6); | ||||||
|         _targets[0] = CONNECT_MAKER; // payback |         _targets[0] = CONNECT_MAKER; // payback | ||||||
|  | @ -192,7 +203,7 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | ||||||
|         _targets[5] = INSTA_POOL_V2; // flashPayback |         _targets[5] = INSTA_POOL_V2; // flashPayback | ||||||
| 
 | 
 | ||||||
|         bytes[] memory _datas = new bytes[](6); |         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[1] = _encodedWithdrawMakerVault(_vaultId, uint256(-1), 0, 0); | ||||||
|         _datas[2] = _encodeDepositCompound( |         _datas[2] = _encodeDepositCompound( | ||||||
|             _token, |             _token, | ||||||
|  | @ -200,19 +211,81 @@ contract ConnectGelatoDataForFullRefinance is ConnectorInterface { | ||||||
|             0, |             0, | ||||||
|             0 |             0 | ||||||
|         ); |         ); | ||||||
|         _datas[3] = _encodeBorrowCompound(DAI, wDaiDebtToMove, 0, 0); |         _datas[3] = _encodeBorrowCompound(DAI, 0, 600, 0); | ||||||
|         _datas[4] = _encodePayGelatoProvider(_token, gasFeesPaidFromCol, 0, 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 = new bytes[](1); | ||||||
|         datas[0] = abi.encodeWithSelector( |         datas[0] = abi.encodeWithSelector( | ||||||
|             IConnectInstaPoolV2.flashBorrowAndCast.selector, |             IConnectInstaPoolV2.flashBorrowAndCast.selector, | ||||||
|             DAI, |             DAI, | ||||||
|             wDaiDebtToMove, |             wDaiToBorrow, | ||||||
|             0, |             route, | ||||||
|             abi.encode(_targets, _datas) |             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 */ |     /* solhint-enable function-max-lines */ | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,7 +1,13 @@ | ||||||
| // SPDX-License-Identifier: UNLICENSED | // SPDX-License-Identifier: UNLICENSED | ||||||
| pragma solidity 0.7.4; | pragma solidity 0.7.4; | ||||||
|  | pragma experimental ABIEncoderV2; | ||||||
| 
 | 
 | ||||||
| import {sub, wmul, wdiv} from "../../vendor/DSMath.sol"; | 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( | function _wCalcCollateralToWithdraw( | ||||||
|     uint256 _wMinColRatioA, |     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); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  | } | ||||||
|  | @ -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"]; | ||||||
|  | @ -68,6 +68,7 @@ module.exports = { | ||||||
|       ConnectInstaPool: "0xCeF5f3c402d4fef76A038e89a4357176963e1464", |       ConnectInstaPool: "0xCeF5f3c402d4fef76A038e89a4357176963e1464", | ||||||
|       MakerResolver: "0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5", |       MakerResolver: "0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5", | ||||||
|       CompoundResolver: "0x1f22D77365d8BFE3b901C33C83C01B584F946617", |       CompoundResolver: "0x1f22D77365d8BFE3b901C33C83C01B584F946617", | ||||||
|  |       InstaPoolResolver: "0xa004a5afBa04b74037E9E52bA1f7eb02b5E61509", | ||||||
|       DAI: "0x6b175474e89094c44da98b954eedeac495271d0f", |       DAI: "0x6b175474e89094c44da98b954eedeac495271d0f", | ||||||
|       DAI_UNISWAP: "0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667", |       DAI_UNISWAP: "0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667", | ||||||
|       CDAI: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643", |       CDAI: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643", | ||||||
|  |  | ||||||
|  | @ -5,6 +5,8 @@ const {deployments, ethers} = hre; | ||||||
| const GelatoCoreLib = require("@gelatonetwork/core"); | const GelatoCoreLib = require("@gelatonetwork/core"); | ||||||
| 
 | 
 | ||||||
| const setupFullRefinanceMakerToCompound = require("./helpers/setupFullRefinanceMakerToCompound"); | 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
 | // This test showcases how to submit a task refinancing a Users debt position from
 | ||||||
| // Maker to Compound using Gelato
 | // 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 ======
 |     // ======= GELATO TASK SETUP ======
 | ||||||
|     const refinanceIfVaultUnsafe = new GelatoCoreLib.Task({ |     const refinanceIfVaultUnsafe = new GelatoCoreLib.Task({ | ||||||
|       conditions: [conditionMakerVaultUnsafeObj], |       conditions: [ | ||||||
|  |         conditionMakerVaultUnsafeObj, | ||||||
|  |         conditionDebtBridgeIsAffordableObj, | ||||||
|  |       ], | ||||||
|       actions: gelatoDebtBridgeSpells, |       actions: gelatoDebtBridgeSpells, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | @ -186,7 +199,15 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function () | ||||||
|       vaultId |       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 |       gelatoGasPrice | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  | @ -248,10 +269,22 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function () | ||||||
|       .div(await contracts.cEthToken.totalSupply()); |       .div(await contracts.cEthToken.totalSupply()); | ||||||
| 
 | 
 | ||||||
|     // Estimated amount to borrowed token should be equal to the actual one read on compound contracts
 |     // Estimated amount to borrowed token should be equal to the actual one read on compound contracts
 | ||||||
|  |     if (route === 1) { | ||||||
|  |       expect(debtOnMakerBefore).to.be.lte( | ||||||
|  |         compoundPosition[0].borrowBalanceStoredUser | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|       expect(debtOnMakerBefore).to.be.equal( |       expect(debtOnMakerBefore).to.be.equal( | ||||||
|         compoundPosition[0].borrowBalanceStoredUser |         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
 |     // Estimated amount of collateral should be equal to the actual one read on compound contracts
 | ||||||
|     expect( |     expect( | ||||||
|       pricedCollateral.sub( |       pricedCollateral.sub( | ||||||
|  | @ -259,15 +292,11 @@ describe("Full Debt Bridge refinancing loan from Maker to Compound", function () | ||||||
|       ) |       ) | ||||||
|     ).to.be.lt(ethers.utils.parseUnits("1", 12)); |     ).to.be.lt(ethers.utils.parseUnits("1", 12)); | ||||||
| 
 | 
 | ||||||
|     const debtOnMakerAfter = await contracts.makerResolver.getMakerVaultDebt( |  | ||||||
|       vaultId |  | ||||||
|     ); |  | ||||||
|     const collateralOnMakerAfter = await contracts.makerResolver.getMakerVaultCollateralBalance( |     const collateralOnMakerAfter = await contracts.makerResolver.getMakerVaultCollateralBalance( | ||||||
|       vaultId |       vaultId | ||||||
|     ); // in Ether.
 |     ); // in Ether.
 | ||||||
| 
 | 
 | ||||||
|     // We should not have borrowed DAI on maker or deposited ether on it.
 |     // We should not have deposited ether on it.
 | ||||||
|     expect(debtOnMakerAfter).to.be.equal(ethers.constants.Zero); |  | ||||||
|     expect(collateralOnMakerAfter).to.be.equal(ethers.constants.Zero); |     expect(collateralOnMakerAfter).to.be.equal(ethers.constants.Zero); | ||||||
| 
 | 
 | ||||||
|     // DSA contain 1000 DAI
 |     // DSA contain 1000 DAI
 | ||||||
|  |  | ||||||
|  | @ -4,10 +4,12 @@ const {deployments, ethers} = hre; | ||||||
| const GelatoCoreLib = require("@gelatonetwork/core"); | const GelatoCoreLib = require("@gelatonetwork/core"); | ||||||
| 
 | 
 | ||||||
| const setupFullRefinanceMakerToMaker = require("./helpers/setupFullRefinanceMakerToMaker"); | 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
 | // This test showcases how to submit a task refinancing a Users debt position from
 | ||||||
| // Maker to Compound using Gelato
 | // 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); |   this.timeout(0); | ||||||
|   if (hre.network.name !== "hardhat") { |   if (hre.network.name !== "hardhat") { | ||||||
|     console.error("Test Suite is meant to be run on hardhat only"); |     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 ======
 |     // ======= GELATO TASK SETUP ======
 | ||||||
|     const refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({ |     const refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({ | ||||||
|       conditions: [conditionMakerVaultUnsafeObj], |       conditions: [ | ||||||
|  |         conditionMakerVaultUnsafeObj, | ||||||
|  |         conditionDebtBridgeIsAffordableObj, | ||||||
|  |       ], | ||||||
|       actions: gelatoDebtBridgeSpells, |       actions: gelatoDebtBridgeSpells, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | @ -188,7 +201,15 @@ describe("Full Debt Bridge refinancing loan from ETH-A to ETH-B", function () { | ||||||
|       vaultAId |       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 |       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
 |     // 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); |       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
 |     // Estimated amount of collateral should be equal to the actual one read on compound contracts
 | ||||||
|     expect(pricedCollateral).to.be.equal(pricedCollateralOnVaultB); |     expect(pricedCollateral).to.be.equal(pricedCollateralOnVaultB); | ||||||
| 
 | 
 | ||||||
|     const debtOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultDebt( |  | ||||||
|       vaultAId |  | ||||||
|     ); |  | ||||||
|     const collateralOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultCollateralBalance( |     const collateralOnMakerOnVaultAAfter = await contracts.makerResolver.getMakerVaultCollateralBalance( | ||||||
|       vaultAId |       vaultAId | ||||||
|     ); // in Ether.
 |     ); // in Ether.
 | ||||||
| 
 | 
 | ||||||
|     // We should not have borrowed DAI on maker or deposited ether on it.
 |     // We should not have deposited ether on it.
 | ||||||
|     expect(debtOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero); |  | ||||||
|     expect(collateralOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero); |     expect(collateralOnMakerOnVaultAAfter).to.be.equal(ethers.constants.Zero); | ||||||
| 
 | 
 | ||||||
|     // DSA has maximum 2 wei DAI in it due to maths inaccuracies
 |     // DSA has maximum 2 wei DAI in it due to maths inaccuracies
 | ||||||
							
								
								
									
										303
									
								
								test/4_Full-Debt-Bridge-ETHA-ETHB.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								test/4_Full-Debt-Bridge-ETHA-ETHB.test.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										35
									
								
								test/helpers/services/createVaultForETHB.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								test/helpers/services/createVaultForETHB.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||||
|  | @ -2,7 +2,7 @@ const hre = require("hardhat"); | ||||||
| const {ethers} = hre; | const {ethers} = hre; | ||||||
| 
 | 
 | ||||||
| const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; | const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; | ||||||
| const GAS_LIMIT = "4000000"; | const GAS_LIMIT = "5000000"; | ||||||
| const GAS_PRICE_CEIL = ethers.utils.parseUnits("1000", "gwei"); | const GAS_PRICE_CEIL = ethers.utils.parseUnits("1000", "gwei"); | ||||||
| 
 | 
 | ||||||
| const MIN_COL_RATIO_MAKER = ethers.utils.parseUnits("3", 18); | 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_ETH = ethers.utils.parseEther("10"); | ||||||
| const MAKER_INITIAL_DEBT = ethers.utils.parseUnits("1000", 18); | const MAKER_INITIAL_DEBT = ethers.utils.parseUnits("1000", 18); | ||||||
| 
 | 
 | ||||||
|  | const MAX_FEES_IN_PERCENT = ethers.utils.parseUnits("1", 17); | ||||||
|  | 
 | ||||||
| async function getConstants() { | async function getConstants() { | ||||||
|   return { |   return { | ||||||
|     ETH: ETH, |     ETH: ETH, | ||||||
|  | @ -19,6 +21,7 @@ async function getConstants() { | ||||||
|     GAS_LIMIT: GAS_LIMIT, |     GAS_LIMIT: GAS_LIMIT, | ||||||
|     MAKER_INITIAL_DEBT: MAKER_INITIAL_DEBT, |     MAKER_INITIAL_DEBT: MAKER_INITIAL_DEBT, | ||||||
|     MAKER_INITIAL_ETH: MAKER_INITIAL_ETH, |     MAKER_INITIAL_ETH: MAKER_INITIAL_ETH, | ||||||
|  |     MAX_FEES_IN_PERCENT: MAX_FEES_IN_PERCENT, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ const IERC20 = require("../../../pre-compiles/IERC20.json"); | ||||||
| const CTokenInterface = require("../../../pre-compiles/CTokenInterface.json"); | const CTokenInterface = require("../../../pre-compiles/CTokenInterface.json"); | ||||||
| const CompoundResolver = require("../../../pre-compiles/InstaCompoundResolver.json"); | const CompoundResolver = require("../../../pre-compiles/InstaCompoundResolver.json"); | ||||||
| const DsaProviderModuleABI = require("../../../pre-compiles/ProviderModuleDsa_ABI.json"); | const DsaProviderModuleABI = require("../../../pre-compiles/ProviderModuleDsa_ABI.json"); | ||||||
|  | const InstaPoolResolver = require("../../../artifacts/contracts/interfaces/InstaDapp/resolvers/IInstaPoolResolver.sol/IInstaPoolResolver.json"); | ||||||
| 
 | 
 | ||||||
| async function getContracts() { | async function getContracts() { | ||||||
|   const instaMaster = await ethers.provider.getSigner( |   const instaMaster = await ethers.provider.getSigner( | ||||||
|  | @ -85,12 +86,19 @@ async function getContracts() { | ||||||
|     DsaProviderModuleABI, |     DsaProviderModuleABI, | ||||||
|     hre.network.config.ProviderModuleDsa |     hre.network.config.ProviderModuleDsa | ||||||
|   ); |   ); | ||||||
|  |   const instaPoolResolver = await ethers.getContractAt( | ||||||
|  |     InstaPoolResolver.abi, | ||||||
|  |     hre.network.config.InstaPoolResolver | ||||||
|  |   ); | ||||||
| 
 | 
 | ||||||
|   // ===== Get deployed contracts ==================
 |   // ===== Get deployed contracts ==================
 | ||||||
|   const priceOracleResolver = await ethers.getContract("PriceOracleResolver"); |   const priceOracleResolver = await ethers.getContract("PriceOracleResolver"); | ||||||
|   const conditionMakerVaultUnsafe = await ethers.getContract( |   const conditionMakerVaultUnsafe = await ethers.getContract( | ||||||
|     "ConditionMakerVaultUnsafe" |     "ConditionMakerVaultUnsafe" | ||||||
|   ); |   ); | ||||||
|  |   const conditionDebtBridgeIsAffordable = await ethers.getContract( | ||||||
|  |     "ConditionDebtBridgeIsAffordable" | ||||||
|  |   ); | ||||||
|   const connectGelatoProviderPayment = await ethers.getContract( |   const connectGelatoProviderPayment = await ethers.getContract( | ||||||
|     "ConnectGelatoProviderPayment" |     "ConnectGelatoProviderPayment" | ||||||
|   ); |   ); | ||||||
|  | @ -122,7 +130,9 @@ async function getContracts() { | ||||||
|     priceOracleResolver, |     priceOracleResolver, | ||||||
|     dsa: ethers.constants.AddressZero, |     dsa: ethers.constants.AddressZero, | ||||||
|     makerResolver, |     makerResolver, | ||||||
|  |     instaPoolResolver, | ||||||
|     dsaProviderModule, |     dsaProviderModule, | ||||||
|  |     conditionDebtBridgeIsAffordable, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								test/helpers/services/getGasCostForFullRefinance.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								test/helpers/services/getGasCostForFullRefinance.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||||
							
								
								
									
										10
									
								
								test/helpers/services/getRoute.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								test/helpers/services/getRoute.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||||
|  | @ -25,7 +25,7 @@ async function providerWhiteListTaskForMakerETHAToMakerETHB( | ||||||
|       abi: (await deployments.getArtifact("ConnectGelatoDataForFullRefinance")) |       abi: (await deployments.getArtifact("ConnectGelatoDataForFullRefinance")) | ||||||
|         .abi, |         .abi, | ||||||
|       functionname: "getDataAndCastForFromMakerToMaker", |       functionname: "getDataAndCastForFromMakerToMaker", | ||||||
|       inputs: [vaultId, constants.ETH, "ETH-B"], |       inputs: [vaultId, 0, constants.ETH, "ETH-B"], | ||||||
|     }), |     }), | ||||||
|     operation: GelatoCoreLib.Operation.Delegatecall, |     operation: GelatoCoreLib.Operation.Delegatecall, | ||||||
|   }); |   }); | ||||||
|  | @ -36,7 +36,10 @@ async function providerWhiteListTaskForMakerETHAToMakerETHB( | ||||||
| 
 | 
 | ||||||
|   const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec( |   const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec( | ||||||
|     { |     { | ||||||
|       conditions: [contracts.conditionMakerVaultUnsafe.address], |       conditions: [ | ||||||
|  |         contracts.conditionMakerVaultUnsafe.address, | ||||||
|  |         contracts.conditionDebtBridgeIsAffordable.address, | ||||||
|  |       ], | ||||||
|       actions: spells, |       actions: spells, | ||||||
|       gasPriceCeil, |       gasPriceCeil, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | @ -36,7 +36,10 @@ async function providerWhiteListTaskForMakerToCompound( | ||||||
| 
 | 
 | ||||||
|   const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec( |   const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec( | ||||||
|     { |     { | ||||||
|       conditions: [contracts.conditionMakerVaultUnsafe.address], |       conditions: [ | ||||||
|  |         contracts.conditionMakerVaultUnsafe.address, | ||||||
|  |         contracts.conditionDebtBridgeIsAffordable.address, | ||||||
|  |       ], | ||||||
|       actions: spells, |       actions: spells, | ||||||
|       gasPriceCeil, |       gasPriceCeil, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
							
								
								
									
										157
									
								
								test/unit_tests/4_ConditionDebtBridgeIsAffordable.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								test/unit_tests/4_ConditionDebtBridgeIsAffordable.test.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -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"); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Shivva
						Shivva