mirror of
https://github.com/Instadapp/Gelato-automations.git
synced 2024-07-29 22:28:07 +00:00
resolve conflits, refactor integration test
This commit is contained in:
parent
ff551cf52b
commit
57a48812a5
|
@ -1,531 +0,0 @@
|
||||||
// "SPDX-License-Identifier: UNLICENSED"
|
|
||||||
pragma solidity 0.6.12;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "./IMemoryInterface.sol";
|
|
||||||
import "./DSMath.sol";
|
|
||||||
|
|
||||||
interface ConnectorInterface {
|
|
||||||
function connectorID() external view returns (uint256 _type, uint256 _id);
|
|
||||||
|
|
||||||
function name() external view returns (string memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OracleAggregator {
|
|
||||||
function getMakerTokenPrice(string memory _pair)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GelatoGasPriceOracle {
|
|
||||||
function latestAnswer() external view returns (int256);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IMakerResolver {
|
|
||||||
struct VaultData {
|
|
||||||
uint256 id;
|
|
||||||
address owner;
|
|
||||||
string colType;
|
|
||||||
uint256 collateral;
|
|
||||||
uint256 art;
|
|
||||||
uint256 debt;
|
|
||||||
uint256 liquidatedCol;
|
|
||||||
uint256 borrowRate;
|
|
||||||
uint256 colPrice;
|
|
||||||
uint256 liquidationRatio;
|
|
||||||
address vaultAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVaultById(uint256 id) external view returns (VaultData memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ICompoundResolver {
|
|
||||||
struct CompData {
|
|
||||||
uint256 tokenPriceInEth;
|
|
||||||
uint256 tokenPriceInUsd;
|
|
||||||
uint256 exchangeRateStored;
|
|
||||||
uint256 balanceOfUser;
|
|
||||||
uint256 borrowBalanceStoredUser;
|
|
||||||
uint256 supplyRatePerBlock;
|
|
||||||
uint256 borrowRatePerBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCompoundData(address owner, address[] memory cAddress)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (CompData[] memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IAaveResolver {
|
|
||||||
struct AaveTokenData {
|
|
||||||
uint256 ltv;
|
|
||||||
uint256 threshold;
|
|
||||||
bool usageAsCollEnabled;
|
|
||||||
bool borrowEnabled;
|
|
||||||
bool stableBorrowEnabled;
|
|
||||||
bool isActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AaveUserTokenData {
|
|
||||||
uint256 tokenPriceInEth;
|
|
||||||
uint256 tokenPriceInUsd;
|
|
||||||
uint256 supplyBalance;
|
|
||||||
uint256 borrowBalance;
|
|
||||||
uint256 borrowFee;
|
|
||||||
uint256 supplyRate;
|
|
||||||
uint256 borrowRate;
|
|
||||||
uint256 borrowModal;
|
|
||||||
AaveTokenData aaveTokenData;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AaveUserData {
|
|
||||||
uint256 totalSupplyETH;
|
|
||||||
uint256 totalCollateralETH;
|
|
||||||
uint256 totalBorrowsETH;
|
|
||||||
uint256 totalFeesETH;
|
|
||||||
uint256 availableBorrowsETH;
|
|
||||||
uint256 currentLiquidationThreshold;
|
|
||||||
uint256 ltv;
|
|
||||||
uint256 healthFactor;
|
|
||||||
uint256 ethPriceInUsd;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPosition(address user, address[] memory tokens)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (AaveUserTokenData[] memory, AaveUserData memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract contract Helpers is ConnectorInterface, DSMath {
|
|
||||||
uint256 internal _id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Return ethereum address
|
|
||||||
*/
|
|
||||||
function _getAddressETH() internal pure returns (address) {
|
|
||||||
return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH Address
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Return Memory Variable Address
|
|
||||||
*/
|
|
||||||
function _getMemoryAddr() internal pure returns (address) {
|
|
||||||
return 0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F; // InstaMemory Address
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Set Uint value in InstaMemory Contract.
|
|
||||||
*/
|
|
||||||
function _setUint(uint256 setId, uint256 val) internal {
|
|
||||||
if (setId != 0) IMemoryInterface(_getMemoryAddr()).setUint(setId, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Get Uint value from InstaMemory Contract.
|
|
||||||
*/
|
|
||||||
function _getUint(uint256 getId, uint256 val)
|
|
||||||
internal
|
|
||||||
returns (uint256 returnVal)
|
|
||||||
{
|
|
||||||
returnVal = getId == 0
|
|
||||||
? val
|
|
||||||
: IMemoryInterface(_getMemoryAddr()).getUint(getId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Connector Details
|
|
||||||
*/
|
|
||||||
function connectorID()
|
|
||||||
public
|
|
||||||
view
|
|
||||||
override
|
|
||||||
returns (uint256 _type, uint256 _iD)
|
|
||||||
{
|
|
||||||
(_type, _iD) = (1, _id); // Should put specific value.
|
|
||||||
}
|
|
||||||
|
|
||||||
function _stringToBytes32(string memory str)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (bytes32 result)
|
|
||||||
{
|
|
||||||
require(bytes(str).length != 0, "String-Empty");
|
|
||||||
// solium-disable-next-line security/no-inline-assembly
|
|
||||||
assembly {
|
|
||||||
result := mload(add(str, 32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract contract ConnectGelatoDebtBridgeHelpers is Helpers {
|
|
||||||
function _getMakerResolver() internal pure returns (address) {
|
|
||||||
return 0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getCompoundResolver() internal pure returns (address) {
|
|
||||||
return 0x1f22D77365d8BFE3b901C33C83C01B584F946617;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getAaveResolver() internal pure returns (address) {
|
|
||||||
return 0xe04Cd009fF68628BC663058dDAA7E5Bf7979BEaF;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getGelatoGasPriceOracle() internal pure returns (address) {
|
|
||||||
return 0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getGasPrice() internal view returns (uint256) {
|
|
||||||
return
|
|
||||||
uint256(
|
|
||||||
GelatoGasPriceOracle(_getGelatoGasPriceOracle()).latestAnswer()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract contract ConnectGelatoDebtBridgeResolver is
|
|
||||||
ConnectGelatoDebtBridgeHelpers
|
|
||||||
{
|
|
||||||
mapping(address => address) internal _priceFeeds;
|
|
||||||
|
|
||||||
function getMakerVault(uint256 _vaultID)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (IMakerResolver.VaultData memory)
|
|
||||||
{
|
|
||||||
// call maker resolver.
|
|
||||||
return IMakerResolver(_getMakerResolver()).getVaultById(_vaultID);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMakerVaultDebt(uint256 _vaultID) public view returns (uint256) {
|
|
||||||
return getMakerVault(_vaultID).debt;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMakerVaultCollateralBalance(uint256 _vaultID)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return getMakerVault(_vaultID).collateral;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMakerVaultCollateralType(uint256 _vaultID)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (string memory)
|
|
||||||
{
|
|
||||||
return getMakerVault(_vaultID).colType;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCompoundData(address _owner, address _cAddress)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (ICompoundResolver.CompData memory)
|
|
||||||
{
|
|
||||||
address[] memory cAddressArray;
|
|
||||||
cAddressArray[0] = _cAddress;
|
|
||||||
return
|
|
||||||
ICompoundResolver(_getCompoundResolver()).getCompoundData(
|
|
||||||
_owner,
|
|
||||||
cAddressArray
|
|
||||||
)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCompoundDebt(address _owner, address _cAddress)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return getCompoundData(_owner, _cAddress).borrowBalanceStoredUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCompoundCollateralBalance(address _owner, address _cAddress)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return getCompoundData(_owner, _cAddress).balanceOfUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAaveTokenData(address _owner, address _atoken)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
IAaveResolver.AaveUserTokenData memory,
|
|
||||||
IAaveResolver.AaveUserData memory
|
|
||||||
)
|
|
||||||
{
|
|
||||||
address[] memory aTokenArray;
|
|
||||||
aTokenArray[0] = _atoken;
|
|
||||||
(
|
|
||||||
IAaveResolver.AaveUserTokenData[] memory tokensData,
|
|
||||||
IAaveResolver.AaveUserData memory etherUserData
|
|
||||||
) = IAaveResolver(_getAaveResolver()).getPosition(_owner, aTokenArray);
|
|
||||||
return (tokensData[0], etherUserData);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAaveTokenDebt(address _owner, address _atoken)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
(IAaveResolver.AaveUserTokenData memory tokenData, ) = getAaveTokenData(
|
|
||||||
_owner,
|
|
||||||
_atoken
|
|
||||||
);
|
|
||||||
return tokenData.supplyBalance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAaveTokenCollateralBalance(address _owner, address _atoken)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
(IAaveResolver.AaveUserTokenData memory tokenData, ) = getAaveTokenData(
|
|
||||||
_owner,
|
|
||||||
_atoken
|
|
||||||
);
|
|
||||||
return tokenData.borrowBalance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contract ConnectGelatoDebtBridge is ConnectGelatoDebtBridgeResolver {
|
|
||||||
// Constant name must be in capitalized SNAKE_CASE
|
|
||||||
// solhint-disable-next-line
|
|
||||||
string public constant override name = "GelatoDebtBridge-v1.0";
|
|
||||||
uint256 public constant GASLIMIT = 1933090 + (19331 * 2); // 1933080 + ~2% (Estimated Value)
|
|
||||||
address public immutable oracleAggregator;
|
|
||||||
|
|
||||||
constructor(uint256 _iD, address _oracleAggregator) public {
|
|
||||||
_id = _iD;
|
|
||||||
oracleAggregator = _oracleAggregator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Write in instaMemory the needed values for doing full refinancing between makerDAO and Compound.
|
|
||||||
/// @param _vaultID is the id of the makerDAO vault.
|
|
||||||
/// @param _getID Id for writting in instaMemory.
|
|
||||||
/// @param _setID Id for loading from instaMemory.
|
|
||||||
function fullRefinanceMakerToCompound(
|
|
||||||
uint256 _vaultID,
|
|
||||||
uint256 _getID,
|
|
||||||
uint256 _setID
|
|
||||||
) external payable {
|
|
||||||
uint256 fees = _getFees(); // get Fees
|
|
||||||
uint256 paybackAmount = getMakerVaultDebt(_vaultID);
|
|
||||||
uint256 collateralToWithdraw = getMakerVaultCollateralBalance(_vaultID);
|
|
||||||
|
|
||||||
_setUint(100, paybackAmount);
|
|
||||||
_setUint(101, paybackAmount); // payback maker
|
|
||||||
_setUint(102, collateralToWithdraw); // withdraw maker
|
|
||||||
_setUint(103, _sub(collateralToWithdraw, fees)); // deposit compound
|
|
||||||
_setUint(104, paybackAmount); // borrow compound
|
|
||||||
_setUint(105, fees); // pay the provider
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Write in instaMemory the needed values for doing partial refinancing between makerDAO and Compound.
|
|
||||||
/// @param _vaultID is the id of the makerDAO vault.
|
|
||||||
/// @param _vaultCollateralizationRatio is the collateralization ratio wanted by the client.
|
|
||||||
/// @param _compPosCollateralizationRatio is the collateralization ratio wanted by the client.
|
|
||||||
/// @param _pair crypto currency pair used (collateral token/ borrowed token).
|
|
||||||
/// @param _getID Id for writting in instaMemory.
|
|
||||||
/// @param _setID Id for loading from instaMemory.
|
|
||||||
function partialRefinanceMakerToCompound(
|
|
||||||
uint256 _vaultID,
|
|
||||||
uint256 _vaultCollateralizationRatio, // should be in ray because maker use ray standard
|
|
||||||
uint256 _compPosCollateralizationRatio, // should be in wad because compound use wad standard
|
|
||||||
string memory _pair,
|
|
||||||
uint256 _getID,
|
|
||||||
uint256 _setID
|
|
||||||
) external payable {
|
|
||||||
(
|
|
||||||
uint256 paybackAmount,
|
|
||||||
uint256 collateralToWithdraw,
|
|
||||||
uint256 fees
|
|
||||||
) = debtBridgeCompute(
|
|
||||||
_vaultID,
|
|
||||||
_vaultCollateralizationRatio,
|
|
||||||
_compPosCollateralizationRatio,
|
|
||||||
_pair
|
|
||||||
);
|
|
||||||
|
|
||||||
_setUint(100, paybackAmount);
|
|
||||||
_setUint(101, paybackAmount); // payback maker
|
|
||||||
_setUint(102, _add(collateralToWithdraw, fees)); // withdraw maker
|
|
||||||
_setUint(103, collateralToWithdraw); // deposit compound
|
|
||||||
_setUint(104, paybackAmount); // borrow compound
|
|
||||||
_setUint(105, fees); // pay the provider
|
|
||||||
}
|
|
||||||
|
|
||||||
// Price Oracle
|
|
||||||
|
|
||||||
function debtBridgeCompute(
|
|
||||||
uint256 _vaultID,
|
|
||||||
uint256 _vaultLiquidationRatio, // should be in ray because maker use ray standard
|
|
||||||
uint256 _compPosLiquidationRatio, // should be in wad because compound use wad standard
|
|
||||||
string memory _pair
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256 paybackAmount,
|
|
||||||
uint256 collateralToWithdraw,
|
|
||||||
uint256 fees
|
|
||||||
)
|
|
||||||
{
|
|
||||||
uint256 latestPrice = _getLatestPrice(_pair);
|
|
||||||
// uint256 fees = mul(GASLIMIT, wmul(_getGasPrice(), latestPrice));
|
|
||||||
fees = _getFees();
|
|
||||||
|
|
||||||
uint256 debt = getMakerVaultDebt(_vaultID);
|
|
||||||
uint256 collateral = _wmul(
|
|
||||||
_sub(getMakerVaultCollateralBalance(_vaultID), fees),
|
|
||||||
latestPrice
|
|
||||||
);
|
|
||||||
|
|
||||||
collateralToWithdraw = wcollateralToWithdraw(
|
|
||||||
_vaultLiquidationRatio,
|
|
||||||
_compPosLiquidationRatio,
|
|
||||||
collateral,
|
|
||||||
debt,
|
|
||||||
latestPrice
|
|
||||||
);
|
|
||||||
paybackAmount = wborrowedTokenToPayback(
|
|
||||||
_vaultLiquidationRatio,
|
|
||||||
_compPosLiquidationRatio,
|
|
||||||
collateral,
|
|
||||||
debt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getFees() internal view returns (uint256 gasCost) {
|
|
||||||
gasCost = _mul(GASLIMIT, _getGasPrice());
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getLatestPrice(string memory _pair)
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
return OracleAggregator(oracleAggregator).getMakerTokenPrice(_pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computation in ray
|
|
||||||
/// @notice return the amount of collateral we need to withdraw during the debt refinancing in ray standard.
|
|
||||||
/// @param _p1LiqRatio the liquidation ratio of protocol 1.
|
|
||||||
/// @param _p2LiqRatio the liquidation ratio of protocol 2.
|
|
||||||
/// @param _col token1 collateral to put on protocol 1.
|
|
||||||
/// @param _bor amount of borrowed token2 on protocol 1.
|
|
||||||
/// @param _colPrice price of the collateral.
|
|
||||||
/// @return collateral to withdraw in ray standard
|
|
||||||
function _rcollateralToWithdraw(
|
|
||||||
uint256 _p1LiqRatio,
|
|
||||||
uint256 _p2LiqRatio,
|
|
||||||
uint256 _col,
|
|
||||||
uint256 _bor,
|
|
||||||
uint256 _colPrice
|
|
||||||
) internal pure returns (uint256) {
|
|
||||||
return
|
|
||||||
_rdiv(
|
|
||||||
_sub(
|
|
||||||
_col,
|
|
||||||
_rdiv(
|
|
||||||
_sub(
|
|
||||||
_rmul(_p1LiqRatio, _col),
|
|
||||||
_rmul(_p1LiqRatio, _rmul(_p2LiqRatio, _bor))
|
|
||||||
),
|
|
||||||
_sub(_p1LiqRatio, _p2LiqRatio)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_colPrice
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computation in ray
|
|
||||||
/// @notice return the amount of borrowed token we need to payback during the debt refinancing in ray standard.
|
|
||||||
/// @param _p1LiqRatio the liquidation ratio of protocol 1.
|
|
||||||
/// @param _p2LiqRatio the liquidation ratio of protocol 2.
|
|
||||||
/// @param _col token1 collateral to put on protocol 1.
|
|
||||||
/// @param _bor amount of borrowed token2 on protocol 1.
|
|
||||||
/// @return amount of borrowed token to pay back in ray standard
|
|
||||||
function _rborrowedTokenToPayback(
|
|
||||||
uint256 _p1LiqRatio,
|
|
||||||
uint256 _p2LiqRatio,
|
|
||||||
uint256 _col,
|
|
||||||
uint256 _bor
|
|
||||||
) internal pure returns (uint256) {
|
|
||||||
return
|
|
||||||
_sub(
|
|
||||||
_bor,
|
|
||||||
_rmul(
|
|
||||||
_rdiv(1e18, _p1LiqRatio),
|
|
||||||
_rdiv(
|
|
||||||
_sub(
|
|
||||||
_rmul(_p1LiqRatio, _col),
|
|
||||||
_rmul(_p1LiqRatio, _rmul(_p2LiqRatio, _bor))
|
|
||||||
),
|
|
||||||
_sub(_p1LiqRatio, _p2LiqRatio)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computation in wad
|
|
||||||
/// @notice return the amount of collateral we need to withdraw during the debt refinancing in wad standard.
|
|
||||||
/// @param _p1LiqRatio the liquidation ratio of protocol 1.
|
|
||||||
/// @param _p2LiqRatio the liquidation ratio of protocol 2.
|
|
||||||
/// @param _col token1 collateral to put on protocol 1.
|
|
||||||
/// @param _bor amount of borrowed token2 on protocol 1.
|
|
||||||
/// @param _colPrice price of the collateral.
|
|
||||||
/// @return collateral to withdraw in wad standard
|
|
||||||
function wcollateralToWithdraw(
|
|
||||||
uint256 _p1LiqRatio,
|
|
||||||
uint256 _p2LiqRatio,
|
|
||||||
uint256 _col,
|
|
||||||
uint256 _bor,
|
|
||||||
uint256 _colPrice
|
|
||||||
) public pure returns (uint256) {
|
|
||||||
return
|
|
||||||
_wdiv(
|
|
||||||
_sub(
|
|
||||||
_col,
|
|
||||||
_wdiv(
|
|
||||||
_sub(
|
|
||||||
_wmul(_p1LiqRatio, _col),
|
|
||||||
_wmul(_p1LiqRatio, _wmul(_p2LiqRatio, _bor))
|
|
||||||
),
|
|
||||||
_sub(_p1LiqRatio, _p2LiqRatio)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
_colPrice
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computation in wad
|
|
||||||
/// @notice return the amount of borrowed token we need to payback during the debt refinancing in wad standard.
|
|
||||||
/// @param _p1LiqRatio the liquidation ratio of protocol 1.
|
|
||||||
/// @param _p2LiqRatio the liquidation ratio of protocol 2.
|
|
||||||
/// @param _col token1 collateral to put on protocol 1.
|
|
||||||
/// @param _bor amount of borrowed token2 on protocol 1.
|
|
||||||
/// @return amount of borrowed token to pay back in wad standard
|
|
||||||
function wborrowedTokenToPayback(
|
|
||||||
uint256 _p1LiqRatio,
|
|
||||||
uint256 _p2LiqRatio,
|
|
||||||
uint256 _col,
|
|
||||||
uint256 _bor
|
|
||||||
) public pure returns (uint256) {
|
|
||||||
return
|
|
||||||
_sub(
|
|
||||||
_bor,
|
|
||||||
_wmul(
|
|
||||||
_wdiv(1e18, _p1LiqRatio),
|
|
||||||
_wdiv(
|
|
||||||
_sub(
|
|
||||||
_wmul(_p1LiqRatio, _col),
|
|
||||||
_wmul(_p1LiqRatio, _wmul(_p2LiqRatio, _bor))
|
|
||||||
),
|
|
||||||
_sub(_p1LiqRatio, _p2LiqRatio)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,11 +24,12 @@ Furtheremore the following contracts were added to showcase the automation of th
|
||||||
|
|
||||||
To see for yourself check out the [contracts](./contracts) folder and make sure to check out `test/mv-DAI-DSR-Compound.test.js`, to see an end-to-end test showcasing the prototype. To do so follow the steps below:
|
To see for yourself check out the [contracts](./contracts) folder and make sure to check out `test/mv-DAI-DSR-Compound.test.js`, to see an end-to-end test showcasing the prototype. To do so follow the steps below:
|
||||||
|
|
||||||
#### Debt Bridge
|
## Debt Bridge
|
||||||
|
|
||||||
Debt Bridge is a finance process that aim to make the user position safer. InstaDapp DSA could use Gelato to automate this process.
|
Debt Bridge is a financial process that aim to make the user position safer. InstaDapp DSA could use Gelato to automate this process.
|
||||||
|
|
||||||
|
### Full Refinancing from Maker's Vault to Compound.
|
||||||
|
|
||||||
**Full Refinancing from Maker's Vault to Compound.**
|
|
||||||
Based on the [debt bridge](https://docs.instadapp.io/usecases/debt-bridge/) documentation of Instadapp, we automated this process by adding two connectors `ConnectGelatoDebtBridge`, `ConnectGelatoProviderPayment` and a Gelato condition contract.
|
Based on the [debt bridge](https://docs.instadapp.io/usecases/debt-bridge/) documentation of Instadapp, we automated this process by adding two connectors `ConnectGelatoDebtBridge`, `ConnectGelatoProviderPayment` and a Gelato condition contract.
|
||||||
|
|
||||||
- `ConditionMakerVaultIsSafe.sol`: determine if a specific vault is on an unsafe position.
|
- `ConditionMakerVaultIsSafe.sol`: determine if a specific vault is on an unsafe position.
|
||||||
|
|
|
@ -270,6 +270,13 @@ contract ConnectGelatoDebtBridgeFromMaker is MakerResolver {
|
||||||
__id = _id;
|
__id = _id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @notice Get gas price from gelato Gas Price Oracle and multiply
|
||||||
|
/// this gas price by the estimated amount of needed for executing
|
||||||
|
/// the transaction
|
||||||
|
function _getFees() internal view returns (uint256 gasCost) {
|
||||||
|
gasCost = _mul(GAS_LIMIT, _getGasPrice());
|
||||||
|
}
|
||||||
|
|
||||||
/// @notice Saves Data to InstaMemory that can be used for DebtBridge Maker->Compound
|
/// @notice Saves Data to InstaMemory that can be used for DebtBridge Maker->Compound
|
||||||
/// @dev Use wad for colRatios. The user has no influence over setUint or getUint.
|
/// @dev Use wad for colRatios. The user has no influence over setUint or getUint.
|
||||||
/// @param _vaultId The id of the makerDAO vault.
|
/// @param _vaultId The id of the makerDAO vault.
|
||||||
|
@ -309,6 +316,27 @@ contract ConnectGelatoDebtBridgeFromMaker is MakerResolver {
|
||||||
setUint(605, gasFeesPaidFromCol); // pay the Gelato Provider (TO DO: unsafe)
|
setUint(605, gasFeesPaidFromCol); // pay the Gelato Provider (TO DO: unsafe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @notice Save in instaMemory the needed values for doing full refinancing between makerDAO and Compound.
|
||||||
|
/// @param _vaultID The ID of the makerDAO vault.
|
||||||
|
// @param _getID Id for writting in instaMemory.
|
||||||
|
// @param _setID Id for loading from instaMemory.
|
||||||
|
function saveFullRefinanceFromMakerDataToMemory(
|
||||||
|
uint256 _vaultID,
|
||||||
|
uint256, /*_getId,*/
|
||||||
|
uint256 /*_setId*/
|
||||||
|
) external payable {
|
||||||
|
uint256 fees = _getFees(); // get Fees
|
||||||
|
uint256 paybackAmount = getMakerVaultDebt(_vaultID);
|
||||||
|
uint256 collateralToWithdraw = getMakerVaultCollateralBalance(_vaultID);
|
||||||
|
|
||||||
|
setUint(600, paybackAmount);
|
||||||
|
setUint(601, paybackAmount); // payback maker
|
||||||
|
setUint(602, collateralToWithdraw); // withdraw maker
|
||||||
|
setUint(603, _sub(collateralToWithdraw, fees)); // deposit compound
|
||||||
|
setUint(604, paybackAmount); // borrow compound
|
||||||
|
setUint(605, fees); // pay the provider
|
||||||
|
}
|
||||||
|
|
||||||
/// @notice Computes values needed for DebtBridge Maker->ProtocolB
|
/// @notice Computes values needed for DebtBridge Maker->ProtocolB
|
||||||
/// @dev Use wad for colRatios.
|
/// @dev Use wad for colRatios.
|
||||||
/// @param _vaultId The id of the makerDAO vault.
|
/// @param _vaultId The id of the makerDAO vault.
|
||||||
|
@ -357,7 +385,7 @@ contract ConnectGelatoDebtBridgeFromMaker is MakerResolver {
|
||||||
|
|
||||||
// TO DO: add fee mechanism for non-ETH collateral debt bridge
|
// TO DO: add fee mechanism for non-ETH collateral debt bridge
|
||||||
// uint256 gasFeesPaidFromCol = _mul(GAS_LIMIT, wmul(_getGasPrice(), latestPrice));
|
// uint256 gasFeesPaidFromCol = _mul(GAS_LIMIT, wmul(_getGasPrice(), latestPrice));
|
||||||
gasFeesPaidFromCol = _mul(GAS_LIMIT, _getGasPrice());
|
gasFeesPaidFromCol = _getFees();
|
||||||
|
|
||||||
uint256 wPricedCol = wmul(
|
uint256 wPricedCol = wmul(
|
||||||
_sub(getMakerVaultCollateralBalance(_vaultId), gasFeesPaidFromCol),
|
_sub(getMakerVaultCollateralBalance(_vaultId), gasFeesPaidFromCol),
|
||||||
|
|
265
test/2_Full-Refinance-External-Provider.test.js
Normal file
265
test/2_Full-Refinance-External-Provider.test.js
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
const {expect} = require("chai");
|
||||||
|
const hre = require("hardhat");
|
||||||
|
const {ethers} = hre;
|
||||||
|
const GelatoCoreLib = require("@gelatonetwork/core");
|
||||||
|
|
||||||
|
const Helper = require("./helpers/Full-Refinance-External-Provider.helper");
|
||||||
|
const helper = new Helper();
|
||||||
|
|
||||||
|
describe("Debt Bridge with External Provider", 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 address;
|
||||||
|
let constants;
|
||||||
|
let ABI;
|
||||||
|
|
||||||
|
// Payload Params for ConnectGelatoDebtBridgeFromMaker and ConditionMakerVaultUnsafe
|
||||||
|
let vaultId;
|
||||||
|
|
||||||
|
// For TaskSpec and for Task
|
||||||
|
let spells = [];
|
||||||
|
|
||||||
|
// Cross test var
|
||||||
|
let taskReceipt;
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
const result = await helper.setup();
|
||||||
|
|
||||||
|
address = result.address;
|
||||||
|
contracts = result.contracts;
|
||||||
|
vaultId = result.vaultId;
|
||||||
|
spells = result.spells;
|
||||||
|
|
||||||
|
ABI = await helper.getABI();
|
||||||
|
constants = await helper.getConstants();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#1: DSA give Authorization to Gelato to execute action his behalf.", 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],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
address.userAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(await contracts.dsa.isAuth(contracts.gelatoCore.address)).to.be.true;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#2 : User submit Debt refinancing task if market move", 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(
|
||||||
|
vaultId,
|
||||||
|
contracts.priceOracleResolver.address,
|
||||||
|
await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ABI.PriceOracleResolverABI,
|
||||||
|
functionname: "getMockPrice",
|
||||||
|
inputs: [address.userAddress],
|
||||||
|
}),
|
||||||
|
constants.MIN_COL_RATIO_MAKER
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ======= GELATO TASK SETUP ======
|
||||||
|
const refinanceIfCompoundBorrowIsBetter = new GelatoCoreLib.Task({
|
||||||
|
conditions: [conditionMakerVaultUnsafeObj],
|
||||||
|
actions: spells,
|
||||||
|
});
|
||||||
|
|
||||||
|
const gelatoExternalProvider = new GelatoCoreLib.GelatoProvider({
|
||||||
|
addr: address.providerAddress,
|
||||||
|
module: contracts.dsaProviderModule.address,
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
refinanceIfCompoundBorrowIsBetter,
|
||||||
|
expiryDate,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
], // datas
|
||||||
|
address.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: [refinanceIfCompoundBorrowIsBetter],
|
||||||
|
expiryDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
});
|
||||||
|
|
||||||
|
it("#2: Use Maker Compound refinancing if the maker vault become unsafe after a market move.", 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(address.executorWallet)
|
||||||
|
.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(address.executorWallet)
|
||||||
|
.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 gasFeesPaidFromCol = ethers.utils
|
||||||
|
.parseUnits(String(1933090 + 19331 * 2), 0)
|
||||||
|
.mul(gelatoGasPrice);
|
||||||
|
const debtOnMakerBefore = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultDebt(
|
||||||
|
vaultId
|
||||||
|
);
|
||||||
|
const pricedCollateral = (
|
||||||
|
await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultCollateralBalance(
|
||||||
|
vaultId
|
||||||
|
)
|
||||||
|
).sub(gasFeesPaidFromCol);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
const providerBalanceBeforeExecution = await address.providerWallet.getBalance();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contracts.gelatoCore.connect(address.executorWallet).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 gelatoCore
|
||||||
|
// .connect(providerWallet)
|
||||||
|
// .exec(taskReceipt, {
|
||||||
|
// gasPrice: gelatoGasPrice,
|
||||||
|
// gasLimit: 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);
|
||||||
|
|
||||||
|
expect(await address.providerWallet.getBalance()).to.be.gt(
|
||||||
|
providerBalanceBeforeExecution
|
||||||
|
);
|
||||||
|
|
||||||
|
// compound position of DSA on cDai and cEth
|
||||||
|
const compoundPosition = await contracts.compoundResolver.getCompoundData(
|
||||||
|
contracts.dsa.address,
|
||||||
|
[contracts.cDaiToken.address, contracts.cEthToken.address]
|
||||||
|
);
|
||||||
|
|
||||||
|
// https://compound.finance/docs/ctokens#exchange-rate
|
||||||
|
// calculate cEth/ETH rate to convert back cEth to ETH
|
||||||
|
// for comparing with the withdrew Ether to the deposited one.
|
||||||
|
const exchangeRateCethToEth = (await contracts.cEthToken.getCash())
|
||||||
|
.add(await contracts.cEthToken.totalBorrows())
|
||||||
|
.sub(await contracts.cEthToken.totalReserves())
|
||||||
|
.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
|
||||||
|
);
|
||||||
|
|
||||||
|
// Estimated amount of collateral should be equal to the actual one read on compound contracts
|
||||||
|
expect(
|
||||||
|
pricedCollateral.sub(
|
||||||
|
compoundPosition[1].balanceOfUser.mul(exchangeRateCethToEth)
|
||||||
|
)
|
||||||
|
).to.be.lt(ethers.utils.parseUnits("1", 12));
|
||||||
|
|
||||||
|
const debtOnMakerAfter = await contracts.connectGelatoDebtBridgeFromMaker.getMakerVaultDebt(
|
||||||
|
vaultId
|
||||||
|
);
|
||||||
|
const collateralOnMakerAfter = await contracts.connectGelatoDebtBridgeFromMaker.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);
|
||||||
|
expect(collateralOnMakerAfter).to.be.equal(ethers.constants.Zero);
|
||||||
|
|
||||||
|
// DSA contain 1000 DAI
|
||||||
|
expect(
|
||||||
|
await contracts.daiToken.balanceOf(contracts.dsa.address)
|
||||||
|
).to.be.equal(constants.MAKER_INITIAL_DEBT);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
});
|
||||||
|
});
|
642
test/helpers/Full-Refinance-External-Provider.helper.js
Normal file
642
test/helpers/Full-Refinance-External-Provider.helper.js
Normal file
|
@ -0,0 +1,642 @@
|
||||||
|
const {expect} = require("chai");
|
||||||
|
const hre = require("hardhat");
|
||||||
|
const {ethers} = hre;
|
||||||
|
const GelatoCoreLib = require("@gelatonetwork/core");
|
||||||
|
|
||||||
|
const InstaIndex = require("../../pre-compiles/InstaIndex.json");
|
||||||
|
const InstaList = require("../../pre-compiles/InstaList.json");
|
||||||
|
const InstaAccount = require("../../pre-compiles/InstaAccount.json");
|
||||||
|
const ConnectGelato = require("../../pre-compiles/ConnectGelato.json");
|
||||||
|
const ConnectMaker = require("../../pre-compiles/ConnectMaker.json");
|
||||||
|
const ConnectCompound = require("../../pre-compiles/ConnectCompound.json");
|
||||||
|
const ConnectInstaPool = require("../../pre-compiles/ConnectInstaPool.json");
|
||||||
|
const ConnectAuth = require("../../pre-compiles/ConnectAuth.json");
|
||||||
|
|
||||||
|
const InstaConnector = require("../../pre-compiles/InstaConnectors.json");
|
||||||
|
const DssCdpManager = require("../../pre-compiles/DssCdpManager.json");
|
||||||
|
const GetCdps = require("../../pre-compiles/GetCdps.json");
|
||||||
|
const IERC20 = require("../../pre-compiles/IERC20.json");
|
||||||
|
const CTokenInterface = require("../../pre-compiles/CTokenInterface.json");
|
||||||
|
const CompoundResolver = require("../../pre-compiles/InstaCompoundResolver.json");
|
||||||
|
const PriceOracleResolverABI = require("../../artifacts/contracts/resolvers/PriceOracleResolver.sol/PriceOracleResolver.json")
|
||||||
|
.abi;
|
||||||
|
const ConnectGelatoProviderPaymentABI = require("../../artifacts/contracts/connectors/ConnectGelatoProviderPayment.sol/ConnectGelatoProviderPayment.json")
|
||||||
|
.abi;
|
||||||
|
const ConnectGelatoDebtBridgeFromMakerABI = require("../../artifacts/contracts/connectors/ConnectGelatoDebtBridgeFromMaker.sol/ConnectGelatoDebtBridgeFromMaker.json")
|
||||||
|
.abi;
|
||||||
|
|
||||||
|
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
||||||
|
const GAS_LIMIT = "4000000";
|
||||||
|
const GAS_PRICE_CEIL = ethers.utils.parseUnits("1000", "gwei");
|
||||||
|
|
||||||
|
const MIN_COL_RATIO_MAKER = ethers.utils.parseUnits("3", 18);
|
||||||
|
|
||||||
|
// TO DO: make dynamic based on real time Collateral Price and Ratios
|
||||||
|
const MAKER_INITIAL_ETH = ethers.utils.parseEther("10");
|
||||||
|
const MAKER_INITIAL_DEBT = ethers.utils.parseUnits("1000", 18);
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
class Helper {
|
||||||
|
async address() {
|
||||||
|
let userWallet;
|
||||||
|
let userAddress;
|
||||||
|
let providerWallet;
|
||||||
|
let providerAddress;
|
||||||
|
let executorWallet;
|
||||||
|
let executorAddress;
|
||||||
|
|
||||||
|
[userWallet, providerWallet, executorWallet] = await ethers.getSigners();
|
||||||
|
userAddress = await userWallet.getAddress();
|
||||||
|
providerAddress = await providerWallet.getAddress();
|
||||||
|
executorAddress = await executorWallet.getAddress();
|
||||||
|
|
||||||
|
// Hardhat default accounts prefilled with 100 ETH
|
||||||
|
expect(await userWallet.getBalance()).to.be.gt(
|
||||||
|
ethers.utils.parseEther("10")
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
userWallet: userWallet,
|
||||||
|
userAddress: userAddress,
|
||||||
|
providerWallet: providerWallet,
|
||||||
|
providerAddress: providerAddress,
|
||||||
|
executorWallet: executorWallet,
|
||||||
|
executorAddress: executorAddress,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async contracts() {
|
||||||
|
// Deployed instances
|
||||||
|
let connectGelato;
|
||||||
|
let connectMaker;
|
||||||
|
let connectInstaPool;
|
||||||
|
let connectCompound;
|
||||||
|
let instaIndex;
|
||||||
|
let instaList;
|
||||||
|
let dssCdpManager;
|
||||||
|
let getCdps;
|
||||||
|
let daiToken;
|
||||||
|
let gelatoCore;
|
||||||
|
let cDaiToken;
|
||||||
|
let cEthToken;
|
||||||
|
let instaMaster;
|
||||||
|
let instaConnectors;
|
||||||
|
let compoundResolver;
|
||||||
|
// Contracts to deploy and use for local testing
|
||||||
|
let conditionMakerVaultUnsafe;
|
||||||
|
let connectGelatoDebtBridgeFromMaker;
|
||||||
|
let connectGelatoProviderPayment;
|
||||||
|
let priceOracleResolver;
|
||||||
|
let dsaProviderModule;
|
||||||
|
|
||||||
|
instaMaster = await ethers.provider.getSigner(
|
||||||
|
hre.network.config.InstaMaster
|
||||||
|
);
|
||||||
|
|
||||||
|
// ===== Get Deployed Contract Instance ==================
|
||||||
|
instaIndex = await ethers.getContractAt(
|
||||||
|
InstaIndex.abi,
|
||||||
|
hre.network.config.InstaIndex
|
||||||
|
);
|
||||||
|
instaList = await ethers.getContractAt(
|
||||||
|
InstaList.abi,
|
||||||
|
hre.network.config.InstaList
|
||||||
|
);
|
||||||
|
connectGelato = await ethers.getContractAt(
|
||||||
|
ConnectGelato.abi,
|
||||||
|
hre.network.config.ConnectGelato
|
||||||
|
);
|
||||||
|
connectMaker = await ethers.getContractAt(
|
||||||
|
ConnectMaker.abi,
|
||||||
|
hre.network.config.ConnectMaker
|
||||||
|
);
|
||||||
|
connectInstaPool = await ethers.getContractAt(
|
||||||
|
ConnectInstaPool.abi,
|
||||||
|
hre.network.config.ConnectInstaPool
|
||||||
|
);
|
||||||
|
connectCompound = await ethers.getContractAt(
|
||||||
|
ConnectCompound.abi,
|
||||||
|
hre.network.config.ConnectCompound
|
||||||
|
);
|
||||||
|
dssCdpManager = await ethers.getContractAt(
|
||||||
|
DssCdpManager.abi,
|
||||||
|
hre.network.config.DssCdpManager
|
||||||
|
);
|
||||||
|
getCdps = await ethers.getContractAt(
|
||||||
|
GetCdps.abi,
|
||||||
|
hre.network.config.GetCdps
|
||||||
|
);
|
||||||
|
daiToken = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
|
||||||
|
gelatoCore = await ethers.getContractAt(
|
||||||
|
GelatoCoreLib.GelatoCore.abi,
|
||||||
|
hre.network.config.GelatoCore
|
||||||
|
);
|
||||||
|
cDaiToken = await ethers.getContractAt(
|
||||||
|
CTokenInterface.abi,
|
||||||
|
hre.network.config.CDAI
|
||||||
|
);
|
||||||
|
cEthToken = await ethers.getContractAt(
|
||||||
|
CTokenInterface.abi,
|
||||||
|
hre.network.config.CETH
|
||||||
|
);
|
||||||
|
instaConnectors = await ethers.getContractAt(
|
||||||
|
InstaConnector.abi,
|
||||||
|
hre.network.config.InstaConnectors
|
||||||
|
);
|
||||||
|
compoundResolver = await ethers.getContractAt(
|
||||||
|
CompoundResolver.abi,
|
||||||
|
hre.network.config.CompoundResolver
|
||||||
|
);
|
||||||
|
|
||||||
|
// ===== Deploy Needed Contract ==================
|
||||||
|
|
||||||
|
const PriceOracleResolver = await ethers.getContractFactory(
|
||||||
|
"PriceOracleResolver"
|
||||||
|
);
|
||||||
|
priceOracleResolver = await PriceOracleResolver.deploy();
|
||||||
|
await priceOracleResolver.deployed();
|
||||||
|
|
||||||
|
const ConditionMakerVaultUnsafe = await ethers.getContractFactory(
|
||||||
|
"ConditionMakerVaultUnsafe"
|
||||||
|
);
|
||||||
|
conditionMakerVaultUnsafe = await ConditionMakerVaultUnsafe.deploy();
|
||||||
|
await conditionMakerVaultUnsafe.deployed();
|
||||||
|
|
||||||
|
const ConnectGelatoDebtBridgeFromMaker = await ethers.getContractFactory(
|
||||||
|
"ConnectGelatoDebtBridgeFromMaker"
|
||||||
|
);
|
||||||
|
connectGelatoDebtBridgeFromMaker = await ConnectGelatoDebtBridgeFromMaker.deploy(
|
||||||
|
(await instaConnectors.connectorLength()).add(1)
|
||||||
|
);
|
||||||
|
await connectGelatoDebtBridgeFromMaker.deployed();
|
||||||
|
|
||||||
|
const ConnectGelatoProviderPayment = await ethers.getContractFactory(
|
||||||
|
"ConnectGelatoProviderPayment"
|
||||||
|
);
|
||||||
|
connectGelatoProviderPayment = await ConnectGelatoProviderPayment.deploy(
|
||||||
|
(await instaConnectors.connectorLength()).add(2)
|
||||||
|
);
|
||||||
|
await connectGelatoProviderPayment.deployed();
|
||||||
|
|
||||||
|
const ProviderModuleDSA = await ethers.getContractFactory(
|
||||||
|
"ProviderModuleDSA"
|
||||||
|
);
|
||||||
|
dsaProviderModule = await ProviderModuleDSA.deploy(
|
||||||
|
hre.network.config.GelatoCore,
|
||||||
|
connectGelatoProviderPayment.address
|
||||||
|
);
|
||||||
|
await dsaProviderModule.deployed();
|
||||||
|
|
||||||
|
return {
|
||||||
|
connectGelato: connectGelato,
|
||||||
|
connectMaker: connectMaker,
|
||||||
|
connectInstaPool: connectInstaPool,
|
||||||
|
connectCompound: connectCompound,
|
||||||
|
instaIndex: instaIndex,
|
||||||
|
instaList: instaList,
|
||||||
|
dssCdpManager: dssCdpManager,
|
||||||
|
getCdps: getCdps,
|
||||||
|
daiToken: daiToken,
|
||||||
|
gelatoCore: gelatoCore,
|
||||||
|
cDaiToken: cDaiToken,
|
||||||
|
cEthToken: cEthToken,
|
||||||
|
instaMaster: instaMaster,
|
||||||
|
instaConnectors: instaConnectors,
|
||||||
|
compoundResolver: compoundResolver,
|
||||||
|
conditionMakerVaultUnsafe: conditionMakerVaultUnsafe,
|
||||||
|
connectGelatoDebtBridgeFromMaker: connectGelatoDebtBridgeFromMaker,
|
||||||
|
connectGelatoProviderPayment: connectGelatoProviderPayment,
|
||||||
|
priceOracleResolver: priceOracleResolver,
|
||||||
|
dsaProviderModule: dsaProviderModule,
|
||||||
|
dsa: ethers.constants.AddressZero,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup() {
|
||||||
|
let address = await this.address();
|
||||||
|
let contracts = await this.contracts();
|
||||||
|
let dsa;
|
||||||
|
let vaultId;
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/////////////////////////////////////////////// Setup ///////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Gelato Testing environment setup.
|
||||||
|
// Step 1 : Add EUR/USD Maker Medianizer in the PriceOracleResolver
|
||||||
|
// Step 2 : Enable Debt Bridge Connector and Gelato Provider Payment Connector
|
||||||
|
// Step 3 : Executor Staking on Gelato
|
||||||
|
// Step 4 : Provider put some fund on gelato for paying future tasks executions
|
||||||
|
// Step 5 : Provider choose a executor
|
||||||
|
// Step 6 : Provider will add a module
|
||||||
|
// Step 7 : User create a DeFi Smart Account
|
||||||
|
// Step 8 : User open a Vault, put some ether on it and borrow some dai
|
||||||
|
// Step 9 : Provider should whitelist task
|
||||||
|
|
||||||
|
//#region Step 1 Add EUR/USD Maker Medianizer in the PriceOracleResolver
|
||||||
|
|
||||||
|
// PriceOracleResolver is a price feeder aggregator
|
||||||
|
// You will be able to query price from multiple source through this aggregator
|
||||||
|
// For the demo we add the ETH/USD Medianizer to the aggregator
|
||||||
|
// MakerDAO price oracle are called Medianizer
|
||||||
|
|
||||||
|
// await priceOracleResolver.addOracle(
|
||||||
|
// ORACLE_MAKER_ETH_USD,
|
||||||
|
// ORACLE_MAKER_ETH_USD_ADDR,
|
||||||
|
// PRICE_ORACLE_MAKER_PAYLOAD
|
||||||
|
// );
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 2 Enable Debt Bridge Connector and Gelato Provider Payment Connector
|
||||||
|
|
||||||
|
// Debt Bridge Connector is used during refinancing of debt
|
||||||
|
// This Connect help the user to split a position in one protocol.
|
||||||
|
// to 2 protocol in a safe way. Both debt position will be safe.
|
||||||
|
|
||||||
|
// Gelato Provider Payment Connector is used for paying the provider
|
||||||
|
// for task execution. So when futur task will be executed, through a self financing
|
||||||
|
// transaction (user will pay during the execution of the task) task will
|
||||||
|
// be executed. Improvind user experience.
|
||||||
|
|
||||||
|
await address.userWallet.sendTransaction({
|
||||||
|
to: hre.network.config.InstaMaster,
|
||||||
|
value: ethers.utils.parseEther("0.1"),
|
||||||
|
});
|
||||||
|
|
||||||
|
await hre.network.provider.request({
|
||||||
|
method: "hardhat_impersonateAccount",
|
||||||
|
params: [await contracts.instaMaster.getAddress()],
|
||||||
|
});
|
||||||
|
|
||||||
|
await contracts.instaConnectors
|
||||||
|
.connect(contracts.instaMaster)
|
||||||
|
.enable(contracts.connectGelatoDebtBridgeFromMaker.address);
|
||||||
|
|
||||||
|
await contracts.instaConnectors
|
||||||
|
.connect(contracts.instaMaster)
|
||||||
|
.enable(contracts.connectGelatoProviderPayment.address);
|
||||||
|
|
||||||
|
await hre.network.provider.request({
|
||||||
|
method: "hardhat_stopImpersonatingAccount",
|
||||||
|
params: [await contracts.instaMaster.getAddress()],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.instaConnectors.isConnector([
|
||||||
|
contracts.connectGelatoDebtBridgeFromMaker.address,
|
||||||
|
])
|
||||||
|
).to.be.true;
|
||||||
|
expect(
|
||||||
|
await contracts.instaConnectors.isConnector([
|
||||||
|
contracts.connectGelatoProviderPayment.address,
|
||||||
|
])
|
||||||
|
).to.be.true;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 3 Executor Staking on Gelato
|
||||||
|
|
||||||
|
// For task execution provider will ask a executor to watch the
|
||||||
|
// blockchain for possible execution autorization given by
|
||||||
|
// the condition that user choose when submitting the task.
|
||||||
|
// And if all condition are meet executor will execute the task.
|
||||||
|
// For safety measure Gelato ask the executor to stake a minimum
|
||||||
|
// amount.
|
||||||
|
|
||||||
|
await contracts.gelatoCore.connect(address.executorWallet).stakeExecutor({
|
||||||
|
value: await contracts.gelatoCore.minExecutorStake(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.gelatoCore.isExecutorMinStaked(address.executorAddress)
|
||||||
|
).to.be.true;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 4 Provider put some fund on gelato for paying future tasks executions
|
||||||
|
|
||||||
|
// Provider put some funds in gelato system for paying the
|
||||||
|
// Executor when this one will execute task on behalf of the
|
||||||
|
// Provider. At each provider's task execution, some funds (approximatively
|
||||||
|
// the gas cost value) will be transfered to the Executor stake.
|
||||||
|
|
||||||
|
const TASK_AUTOMATION_FUNDS = await contracts.gelatoCore.minExecProviderFunds(
|
||||||
|
GAS_LIMIT,
|
||||||
|
GAS_PRICE_CEIL
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.provideFunds(address.providerAddress, {
|
||||||
|
value: TASK_AUTOMATION_FUNDS,
|
||||||
|
})
|
||||||
|
).to.emit(contracts.gelatoCore, "LogFundsProvided");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.gelatoCore.providerFunds(address.providerAddress)
|
||||||
|
).to.be.equal(TASK_AUTOMATION_FUNDS);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 5 Provider choose a executor
|
||||||
|
|
||||||
|
// Provider choose a executor who will execute futur task
|
||||||
|
// for the provider, it will be compensated by the provider.
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.providerAssignsExecutor(address.executorAddress)
|
||||||
|
).to.emit(contracts.gelatoCore, "LogProviderAssignedExecutor");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.gelatoCore.executorByProvider(address.providerAddress)
|
||||||
|
).to.be.equal(address.executorAddress);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 6 Provider will add a module
|
||||||
|
|
||||||
|
// By adding a module the provider will format future task's
|
||||||
|
// payload by adding some specificity like his address to the
|
||||||
|
// Payment connector for receiving payment of User.
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.addProviderModules([contracts.dsaProviderModule.address])
|
||||||
|
).to.emit(contracts.gelatoCore, "LogProviderModuleAdded");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.isModuleProvided(
|
||||||
|
address.providerAddress,
|
||||||
|
contracts.dsaProviderModule.address
|
||||||
|
)
|
||||||
|
).to.be.true;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 7 User create a DeFi Smart Account
|
||||||
|
|
||||||
|
// User create a Instadapp DeFi Smart Account
|
||||||
|
// who give him the possibility to interact
|
||||||
|
// with a large list of DeFi protocol through one
|
||||||
|
// Proxy account.
|
||||||
|
|
||||||
|
const dsaAccountCount = await contracts.instaList.accounts();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contracts.instaIndex.build(address.userAddress, 1, address.userAddress)
|
||||||
|
).to.emit(contracts.instaIndex, "LogAccountCreated");
|
||||||
|
const dsaID = dsaAccountCount.add(1);
|
||||||
|
await expect(await contracts.instaList.accounts()).to.be.equal(dsaID);
|
||||||
|
|
||||||
|
// Instantiate the DSA
|
||||||
|
dsa = await ethers.getContractAt(
|
||||||
|
InstaAccount.abi,
|
||||||
|
await contracts.instaList.accountAddr(dsaID)
|
||||||
|
);
|
||||||
|
|
||||||
|
contracts.dsa = dsa;
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#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-A"],
|
||||||
|
});
|
||||||
|
|
||||||
|
await dsa.cast(
|
||||||
|
[hre.network.config.ConnectMaker],
|
||||||
|
[openVault],
|
||||||
|
address.userAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
const cdps = await contracts.getCdps.getCdpsAsc(
|
||||||
|
contracts.dssCdpManager.address,
|
||||||
|
dsa.address
|
||||||
|
);
|
||||||
|
vaultId = 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: [vaultId, MAKER_INITIAL_ETH, 0, 0],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
address.userAddress,
|
||||||
|
{
|
||||||
|
value: MAKER_INITIAL_ETH,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await dsa.cast(
|
||||||
|
[hre.network.config.ConnectMaker],
|
||||||
|
[
|
||||||
|
await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectMaker.abi,
|
||||||
|
functionname: "borrow",
|
||||||
|
inputs: [vaultId, MAKER_INITIAL_DEBT, 0, 0],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
address.userAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(await contracts.daiToken.balanceOf(dsa.address)).to.be.equal(
|
||||||
|
MAKER_INITIAL_DEBT
|
||||||
|
);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Step 9 Provider should whitelist task
|
||||||
|
|
||||||
|
// By WhiteList task, the provider can constrain the type
|
||||||
|
// of task the user can submitting.
|
||||||
|
|
||||||
|
//#region Actions
|
||||||
|
|
||||||
|
let spells = [];
|
||||||
|
|
||||||
|
const debtBridgeCalculationForFullRefinance = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectGelatoDebtBridgeFromMaker.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectGelatoDebtBridgeFromMakerABI,
|
||||||
|
functionname: "saveFullRefinanceFromMakerDataToMemory",
|
||||||
|
inputs: [vaultId, 0, 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(debtBridgeCalculationForFullRefinance);
|
||||||
|
|
||||||
|
const flashBorrow = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectInstaPool.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectInstaPool.abi,
|
||||||
|
functionname: "flashBorrow",
|
||||||
|
inputs: [hre.network.config.DAI, 0, "600", 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(flashBorrow);
|
||||||
|
|
||||||
|
const paybackMaker = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectMaker.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectMaker.abi,
|
||||||
|
functionname: "payback",
|
||||||
|
inputs: [vaultId, 0, "601", 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(paybackMaker);
|
||||||
|
|
||||||
|
const withdrawMaker = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectMaker.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectMaker.abi,
|
||||||
|
functionname: "withdraw",
|
||||||
|
inputs: [vaultId, 0, "602", 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(withdrawMaker);
|
||||||
|
|
||||||
|
const depositCompound = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectCompound.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectCompound.abi,
|
||||||
|
functionname: "deposit",
|
||||||
|
inputs: [ETH, 0, "603", 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(depositCompound);
|
||||||
|
|
||||||
|
const borrowCompound = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectCompound.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectCompound.abi,
|
||||||
|
functionname: "borrow",
|
||||||
|
inputs: [hre.network.config.DAI, 0, "604", 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(borrowCompound);
|
||||||
|
|
||||||
|
const flashPayBack = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectInstaPool.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectInstaPool.abi,
|
||||||
|
functionname: "flashPayback",
|
||||||
|
inputs: [hre.network.config.DAI, 0, 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(flashPayBack);
|
||||||
|
|
||||||
|
const payProvider = new GelatoCoreLib.Action({
|
||||||
|
addr: contracts.connectGelatoProviderPayment.address,
|
||||||
|
data: await hre.run("abi-encode-withselector", {
|
||||||
|
abi: ConnectGelatoProviderPaymentABI,
|
||||||
|
functionname: "payProvider",
|
||||||
|
inputs: [address.providerAddress, ETH, 0, "605", 0],
|
||||||
|
}),
|
||||||
|
operation: GelatoCoreLib.Operation.Delegatecall,
|
||||||
|
});
|
||||||
|
|
||||||
|
spells.push(payProvider);
|
||||||
|
|
||||||
|
const gasPriceCeil = ethers.constants.MaxUint256;
|
||||||
|
|
||||||
|
const connectGelatoDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec(
|
||||||
|
{
|
||||||
|
conditions: [contracts.conditionMakerVaultUnsafe.address],
|
||||||
|
actions: spells,
|
||||||
|
gasPriceCeil,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.provideTaskSpecs([connectGelatoDebtBridgeFromMakerTaskSpec])
|
||||||
|
).to.emit(contracts.gelatoCore, "LogTaskSpecProvided");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.isTaskSpecProvided(
|
||||||
|
address.providerAddress,
|
||||||
|
connectGelatoDebtBridgeFromMakerTaskSpec
|
||||||
|
)
|
||||||
|
).to.be.equal("OK");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.taskSpecGasPriceCeil(
|
||||||
|
address.providerAddress,
|
||||||
|
await contracts.gelatoCore
|
||||||
|
.connect(address.providerWallet)
|
||||||
|
.hashTaskSpec(connectGelatoDebtBridgeFromMakerTaskSpec)
|
||||||
|
)
|
||||||
|
).to.be.equal(gasPriceCeil);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
return {
|
||||||
|
address: address,
|
||||||
|
contracts: contracts,
|
||||||
|
vaultId: vaultId,
|
||||||
|
spells: spells,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getABI() {
|
||||||
|
return {
|
||||||
|
PriceOracleResolverABI: PriceOracleResolverABI,
|
||||||
|
ConnectGelatoABI: ConnectGelato.abi,
|
||||||
|
ConnectAuthABI: ConnectAuth.abi,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConstants() {
|
||||||
|
return {
|
||||||
|
MIN_COL_RATIO_MAKER: MIN_COL_RATIO_MAKER,
|
||||||
|
GAS_PRICE_CEIL: GAS_PRICE_CEIL,
|
||||||
|
GAS_LIMIT: GAS_LIMIT,
|
||||||
|
MAKER_INITIAL_DEBT: MAKER_INITIAL_DEBT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getConnectAuth() {
|
||||||
|
return ConnectAuth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Helper;
|
|
@ -4,13 +4,13 @@ const {ethers} = hre;
|
||||||
|
|
||||||
// #region Contracts ABI
|
// #region Contracts ABI
|
||||||
|
|
||||||
const ConnectMaker = require("../pre-compiles/ConnectMaker.json");
|
const ConnectMaker = require("../../pre-compiles/ConnectMaker.json");
|
||||||
const GetCdps = require("../pre-compiles/GetCdps.json");
|
const GetCdps = require("../../pre-compiles/GetCdps.json");
|
||||||
const DssCdpManager = require("../pre-compiles/DssCdpManager.json");
|
const DssCdpManager = require("../../pre-compiles/DssCdpManager.json");
|
||||||
const InstaList = require("../pre-compiles/InstaList.json");
|
const InstaList = require("../../pre-compiles/InstaList.json");
|
||||||
const InstaAccount = require("../pre-compiles/InstaAccount.json");
|
const InstaAccount = require("../../pre-compiles/InstaAccount.json");
|
||||||
const InstaIndex = require("../pre-compiles/InstaIndex.json");
|
const InstaIndex = require("../../pre-compiles/InstaIndex.json");
|
||||||
const IERC20 = require("../pre-compiles/IERC20.json");
|
const IERC20 = require("../../pre-compiles/IERC20.json");
|
||||||
|
|
||||||
const ORACLE_MAKER_ETH_USD = "ETH/USD-Maker-v1";
|
const ORACLE_MAKER_ETH_USD = "ETH/USD-Maker-v1";
|
||||||
const ORACLE_MAKER_ETH_USD_ADDR = "0x729D19f657BD0614b4985Cf1D82531c67569197B";
|
const ORACLE_MAKER_ETH_USD_ADDR = "0x729D19f657BD0614b4985Cf1D82531c67569197B";
|
|
@ -4,15 +4,15 @@ const {ethers} = hre;
|
||||||
|
|
||||||
// #region Contracts ABI
|
// #region Contracts ABI
|
||||||
|
|
||||||
const ConnectMaker = require("../pre-compiles/ConnectMaker.json");
|
const ConnectMaker = require("../../pre-compiles/ConnectMaker.json");
|
||||||
const GetCdps = require("../pre-compiles/GetCdps.json");
|
const GetCdps = require("../../pre-compiles/GetCdps.json");
|
||||||
const DssCdpManager = require("../pre-compiles/DssCdpManager.json");
|
const DssCdpManager = require("../../pre-compiles/DssCdpManager.json");
|
||||||
const ConnectBasic = require("../pre-compiles/ConnectBasic.json");
|
const ConnectBasic = require("../../pre-compiles/ConnectBasic.json");
|
||||||
const InstaList = require("../pre-compiles/InstaList.json");
|
const InstaList = require("../../pre-compiles/InstaList.json");
|
||||||
const InstaAccount = require("../pre-compiles/InstaAccount.json");
|
const InstaAccount = require("../../pre-compiles/InstaAccount.json");
|
||||||
const InstaIndex = require("../pre-compiles/InstaIndex.json");
|
const InstaIndex = require("../../pre-compiles/InstaIndex.json");
|
||||||
const IERC20 = require("../pre-compiles/IERC20.json");
|
const IERC20 = require("../../pre-compiles/IERC20.json");
|
||||||
const InstaConnector = require("../pre-compiles/InstaConnectors.json");
|
const InstaConnector = require("../../pre-compiles/InstaConnectors.json");
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
|
@ -5,11 +5,11 @@ const GelatoCoreLib = require("@gelatonetwork/core");
|
||||||
|
|
||||||
// #region Contracts ABI
|
// #region Contracts ABI
|
||||||
|
|
||||||
const ConnectAuth = require("../pre-compiles/ConnectAuth.json");
|
const ConnectAuth = require("../../pre-compiles/ConnectAuth.json");
|
||||||
const InstaList = require("../pre-compiles/InstaList.json");
|
const InstaList = require("../../pre-compiles/InstaList.json");
|
||||||
const InstaAccount = require("../pre-compiles/InstaAccount.json");
|
const InstaAccount = require("../../pre-compiles/InstaAccount.json");
|
||||||
const InstaIndex = require("../pre-compiles/InstaIndex.json");
|
const InstaIndex = require("../../pre-compiles/InstaIndex.json");
|
||||||
const InstaConnectors = require("../pre-compiles/InstaConnectors.json");
|
const InstaConnectors = require("../../pre-compiles/InstaConnectors.json");
|
||||||
|
|
||||||
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
||||||
|
|
|
@ -1,831 +0,0 @@
|
||||||
const {expect} = require("chai");
|
|
||||||
const bre = require("@nomiclabs/buidler");
|
|
||||||
const {constants} = require("ethers");
|
|
||||||
const {ethers} = bre;
|
|
||||||
const GelatoCoreLib = require("@gelatonetwork/core");
|
|
||||||
|
|
||||||
// #region Contracts ABI
|
|
||||||
|
|
||||||
const InstaIndex = require("../pre-compiles/InstaIndex.json");
|
|
||||||
const InstaList = require("../pre-compiles/InstaList.json");
|
|
||||||
const InstaAccount = require("../pre-compiles/InstaAccount.json");
|
|
||||||
const ConnectGelato = require("../pre-compiles/ConnectGelato.json");
|
|
||||||
const ConnectMaker = require("../pre-compiles/ConnectMaker.json");
|
|
||||||
const ConnectCompound = require("../pre-compiles/ConnectCompound.json");
|
|
||||||
const ConnectInstaPool = require("../pre-compiles/ConnectInstaPool.json");
|
|
||||||
const ConnectAuth = require("../pre-compiles/ConnectAuth.json");
|
|
||||||
const ConnectGelatoDebtBridgeABI = require("../artifacts/ConnectGelatoDebtBridge.json");
|
|
||||||
const ConnectGelatoProviderPaymentABI = require("../artifacts/ConnectGelatoProviderPayment.json");
|
|
||||||
const InstaConnector = require("../pre-compiles/InstaConnectors.json");
|
|
||||||
const DssCdpManager = require("../pre-compiles/DssCdpManager.json");
|
|
||||||
const GetCdps = require("../pre-compiles/GetCdps.json");
|
|
||||||
const IERC20 = require("../pre-compiles/IERC20.json");
|
|
||||||
const CTokenInterface = require("../pre-compiles/CTokenInterface.json");
|
|
||||||
const GelatoGasPriceOracle = require("../pre-compiles/GelatoGasPriceOracle.json");
|
|
||||||
const CompoundResolver = require("../pre-compiles/InstaCompoundResolver.json");
|
|
||||||
|
|
||||||
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
||||||
const GAS_LIMIT = "4000000";
|
|
||||||
const GAS_PRICE_CEIL = ethers.utils.parseUnits("1000", "gwei");
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
describe("Debt Bridge with External Provider", function () {
|
|
||||||
this.timeout(0);
|
|
||||||
if (bre.network.name !== "ganache") {
|
|
||||||
console.error("Test Suite is meant to be run on ganache only");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wallet to use for local testing
|
|
||||||
let userWallet;
|
|
||||||
let userAddress;
|
|
||||||
let providerWallet;
|
|
||||||
let providerAddress;
|
|
||||||
|
|
||||||
// Deployed instances
|
|
||||||
let connectGelato;
|
|
||||||
let connectMaker;
|
|
||||||
let connectInstaPool;
|
|
||||||
let connectCompound;
|
|
||||||
let instaIndex;
|
|
||||||
let instaList;
|
|
||||||
let dssCdpManager;
|
|
||||||
let getCdps;
|
|
||||||
let daiToken;
|
|
||||||
let gelatoCore;
|
|
||||||
let cDaiToken;
|
|
||||||
let cEthToken;
|
|
||||||
let instaMaster;
|
|
||||||
let instaConnectors;
|
|
||||||
let gelatoGasPriceOracle;
|
|
||||||
let compoundResolver;
|
|
||||||
|
|
||||||
// Contracts to deploy and use for local testing
|
|
||||||
let conditionMakerVaultIsSafe;
|
|
||||||
let connectGelatoDebtBridge;
|
|
||||||
let connectGelatoProviderPayment;
|
|
||||||
let oracleAggregator;
|
|
||||||
let dsaProviderModule;
|
|
||||||
|
|
||||||
// Creation during test
|
|
||||||
let dsa;
|
|
||||||
let connectedGelatoCore;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
// Get Test Wallet for local testnet
|
|
||||||
[userWallet] = await ethers.getSigners();
|
|
||||||
userAddress = await userWallet.getAddress();
|
|
||||||
|
|
||||||
[, providerWallet] = await ethers.getSigners();
|
|
||||||
providerAddress = await providerWallet.getAddress();
|
|
||||||
|
|
||||||
instaMaster = await ethers.provider.getSigner(
|
|
||||||
bre.network.config.InstaMaster
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ganache default accounts prefilled with 100 ETH
|
|
||||||
expect(await userWallet.getBalance()).to.be.gt(
|
|
||||||
ethers.utils.parseEther("10")
|
|
||||||
);
|
|
||||||
|
|
||||||
// ===== Get Deployed Contract Instance ==================
|
|
||||||
instaIndex = await ethers.getContractAt(
|
|
||||||
InstaIndex.abi,
|
|
||||||
bre.network.config.InstaIndex
|
|
||||||
);
|
|
||||||
instaList = await ethers.getContractAt(
|
|
||||||
InstaList.abi,
|
|
||||||
bre.network.config.InstaList
|
|
||||||
);
|
|
||||||
connectGelato = await ethers.getContractAt(
|
|
||||||
ConnectGelato.abi,
|
|
||||||
bre.network.config.ConnectGelato
|
|
||||||
);
|
|
||||||
connectMaker = await ethers.getContractAt(
|
|
||||||
ConnectMaker.abi,
|
|
||||||
bre.network.config.ConnectMaker
|
|
||||||
);
|
|
||||||
connectInstaPool = await ethers.getContractAt(
|
|
||||||
ConnectInstaPool.abi,
|
|
||||||
bre.network.config.ConnectInstaPool
|
|
||||||
);
|
|
||||||
connectCompound = await ethers.getContractAt(
|
|
||||||
ConnectCompound.abi,
|
|
||||||
bre.network.config.ConnectCompound
|
|
||||||
);
|
|
||||||
dssCdpManager = await ethers.getContractAt(
|
|
||||||
DssCdpManager.abi,
|
|
||||||
bre.network.config.DssCdpManager
|
|
||||||
);
|
|
||||||
getCdps = await ethers.getContractAt(
|
|
||||||
GetCdps.abi,
|
|
||||||
bre.network.config.GetCdps
|
|
||||||
);
|
|
||||||
daiToken = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
|
|
||||||
gelatoCore = await ethers.getContractAt(
|
|
||||||
GelatoCoreLib.GelatoCore.abi,
|
|
||||||
bre.network.config.GelatoCore
|
|
||||||
);
|
|
||||||
cDaiToken = await ethers.getContractAt(
|
|
||||||
CTokenInterface.abi,
|
|
||||||
bre.network.config.CDAI
|
|
||||||
);
|
|
||||||
cEthToken = await ethers.getContractAt(
|
|
||||||
CTokenInterface.abi,
|
|
||||||
bre.network.config.CETH
|
|
||||||
);
|
|
||||||
instaConnectors = await ethers.getContractAt(
|
|
||||||
InstaConnector.abi,
|
|
||||||
bre.network.config.InstaConnectors
|
|
||||||
);
|
|
||||||
gelatoGasPriceOracle = await ethers.getContractAt(
|
|
||||||
GelatoGasPriceOracle.abi,
|
|
||||||
bre.network.config.GelatoGasPriceOracle
|
|
||||||
);
|
|
||||||
compoundResolver = await ethers.getContractAt(
|
|
||||||
CompoundResolver.abi,
|
|
||||||
bre.network.config.CompoundResolver
|
|
||||||
);
|
|
||||||
// instaEvent = await ethers.getContractAt(
|
|
||||||
// InstaEvent.abi,
|
|
||||||
// bre.network.config.InstaEvent
|
|
||||||
// )
|
|
||||||
|
|
||||||
// ===== Deploy Needed Contract ==================
|
|
||||||
|
|
||||||
const OracleAggregator = await ethers.getContractFactory(
|
|
||||||
"OracleAggregator"
|
|
||||||
);
|
|
||||||
oracleAggregator = await OracleAggregator.deploy();
|
|
||||||
await oracleAggregator.deployed();
|
|
||||||
|
|
||||||
const ConditionMakerVaultIsSafe = await ethers.getContractFactory(
|
|
||||||
"ConditionMakerVaultIsSafe"
|
|
||||||
);
|
|
||||||
conditionMakerVaultIsSafe = await ConditionMakerVaultIsSafe.deploy(
|
|
||||||
oracleAggregator.address
|
|
||||||
);
|
|
||||||
await conditionMakerVaultIsSafe.deployed();
|
|
||||||
|
|
||||||
const connectorLength = await instaConnectors.connectorLength();
|
|
||||||
const connectorId = connectorLength.add(1);
|
|
||||||
|
|
||||||
const ConnectGelatoDebtBridge = await ethers.getContractFactory(
|
|
||||||
"ConnectGelatoDebtBridge"
|
|
||||||
);
|
|
||||||
connectGelatoDebtBridge = await ConnectGelatoDebtBridge.deploy(
|
|
||||||
connectorId,
|
|
||||||
oracleAggregator.address
|
|
||||||
);
|
|
||||||
await connectGelatoDebtBridge.deployed();
|
|
||||||
|
|
||||||
const ConnectGelatoProviderPayment = await ethers.getContractFactory(
|
|
||||||
"ConnectGelatoProviderPayment"
|
|
||||||
);
|
|
||||||
connectGelatoProviderPayment = await ConnectGelatoProviderPayment.deploy(
|
|
||||||
connectorId.add(1)
|
|
||||||
);
|
|
||||||
await connectGelatoProviderPayment.deployed();
|
|
||||||
|
|
||||||
const ProviderModuleDSA = await ethers.getContractFactory(
|
|
||||||
"ProviderModuleDSA"
|
|
||||||
);
|
|
||||||
dsaProviderModule = await ProviderModuleDSA.deploy(
|
|
||||||
bre.network.config.InstaIndex,
|
|
||||||
bre.network.config.GelatoCore,
|
|
||||||
connectGelatoProviderPayment.address
|
|
||||||
);
|
|
||||||
await dsaProviderModule.deployed();
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/////////////////////////////// After Contracts Deployement : Setup ///////////////////////////
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Gelato Testing environment setup.
|
|
||||||
// Step 1 : Add EUR/USD Maker Medianizer in the Oracle Aggregator
|
|
||||||
// Step 2 : Enable Debt Bridge Connector and Gelato Provider Payment Connector
|
|
||||||
// Step 3 : Executor Staking on Gelato
|
|
||||||
// Step 4 : Provider put some fund on gelato for paying future tasks executions
|
|
||||||
// Step 5 : Provider choose a executor
|
|
||||||
// Step 6 : Provider will add a module
|
|
||||||
// Step 7 : Provider should whitelist task
|
|
||||||
|
|
||||||
//#region Step 1 Add EUR/USD Maker Medianizer in the Oracle Aggregator
|
|
||||||
|
|
||||||
// Oracle Aggregator is a price feeder aggregator
|
|
||||||
// You will be able to query price from multiple source through this aggregator
|
|
||||||
// For the demo we add the ETH/USD Medianizer to the aggregator
|
|
||||||
// MakerDAO price oracle are called Medianizer
|
|
||||||
|
|
||||||
await oracleAggregator.addOracle(
|
|
||||||
"ETH/USD",
|
|
||||||
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
|
|
||||||
);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 2 Enable Debt Bridge Connector and Gelato Provider Payment Connector
|
|
||||||
|
|
||||||
// Debt Bridge Connector is used during refinancing of debt
|
|
||||||
// This Connect help the user to split a position in one protocol.
|
|
||||||
// to 2 protocol in a safe way. Both debt position will be safe.
|
|
||||||
|
|
||||||
// Gelato Provider Payment Connector is used for paying the provider
|
|
||||||
// for task execution. So when futur task will be executed, through a self financing
|
|
||||||
// transaction (user will pay during the execution of the task) task will
|
|
||||||
// be executed. Improvind user experience.
|
|
||||||
|
|
||||||
await userWallet.sendTransaction({
|
|
||||||
to: bre.network.config.InstaMaster,
|
|
||||||
value: ethers.utils.parseEther("0.1"),
|
|
||||||
});
|
|
||||||
await instaConnectors
|
|
||||||
.connect(instaMaster)
|
|
||||||
.enable(connectGelatoDebtBridge.address);
|
|
||||||
await instaConnectors
|
|
||||||
.connect(instaMaster)
|
|
||||||
.enable(connectGelatoProviderPayment.address);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await instaConnectors.isConnector([connectGelatoDebtBridge.address])
|
|
||||||
).to.be.true;
|
|
||||||
expect(
|
|
||||||
await instaConnectors.isConnector([connectGelatoProviderPayment.address])
|
|
||||||
).to.be.true;
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 3 Executor Staking on Gelato
|
|
||||||
|
|
||||||
// For task execution provider will ask a executor to watch the
|
|
||||||
// blockchain for possible execution autorization given by
|
|
||||||
// the condition that user choose when submitting the task.
|
|
||||||
// And if all condition are meet executor will execute the task.
|
|
||||||
// For safety measure Gelato ask the executor to stake a minimum
|
|
||||||
// amount.
|
|
||||||
|
|
||||||
connectedGelatoCore = gelatoCore.connect(providerWallet);
|
|
||||||
gelatoCore = gelatoCore.connect(userWallet);
|
|
||||||
await connectedGelatoCore.stakeExecutor({
|
|
||||||
from: providerAddress,
|
|
||||||
value: await connectedGelatoCore.minExecutorStake(),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.isExecutorMinStaked(providerAddress)
|
|
||||||
).to.be.true;
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 4 Provider put some fund on gelato for paying future tasks executions
|
|
||||||
|
|
||||||
// Provider put some funds in gelato system for paying the
|
|
||||||
// Executor when this one will execute task on behalf of the
|
|
||||||
// Provider. At each provider's task execution, some funds (approximatively
|
|
||||||
// the gas cost value) will be transfered to the Executor stake.
|
|
||||||
|
|
||||||
const TASK_AUTOMATION_FUNDS = await gelatoCore.minExecProviderFunds(
|
|
||||||
GAS_LIMIT,
|
|
||||||
GAS_PRICE_CEIL
|
|
||||||
);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
connectedGelatoCore.provideFunds(providerAddress, {
|
|
||||||
value: TASK_AUTOMATION_FUNDS,
|
|
||||||
})
|
|
||||||
).to.emit(gelatoCore, "LogFundsProvided");
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.providerFunds(providerAddress)
|
|
||||||
).to.be.equal(TASK_AUTOMATION_FUNDS);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 5 Provider choose a executor
|
|
||||||
|
|
||||||
// Provider choose a executor who will execute futur task
|
|
||||||
// for the provider, it will be compensated by the provider.
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
connectedGelatoCore.providerAssignsExecutor(providerAddress)
|
|
||||||
).to.emit(gelatoCore, "LogProviderAssignedExecutor");
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.executorByProvider(providerAddress)
|
|
||||||
).to.be.equal(providerAddress);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 6 Provider will add a module
|
|
||||||
|
|
||||||
// By adding a module the provider will format future task's
|
|
||||||
// payload by adding some specificity like his address to the
|
|
||||||
// Payment connector for receiving payment of User.
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
connectedGelatoCore.addProviderModules([dsaProviderModule.address])
|
|
||||||
).to.emit(gelatoCore, "LogProviderModuleAdded");
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.isModuleProvided(
|
|
||||||
providerAddress,
|
|
||||||
dsaProviderModule.address
|
|
||||||
)
|
|
||||||
).to.be.true;
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 7 Provider should whitelist task
|
|
||||||
|
|
||||||
// By WhiteList task, the provider can constrain the type
|
|
||||||
// of task the user can submitting.
|
|
||||||
|
|
||||||
//#region Actions
|
|
||||||
|
|
||||||
const spells = [];
|
|
||||||
|
|
||||||
let debtBridge = new GelatoCoreLib.Action({
|
|
||||||
addr: connectGelatoDebtBridge.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(debtBridge);
|
|
||||||
|
|
||||||
let flashBorrow = new GelatoCoreLib.Action({
|
|
||||||
addr: connectInstaPool.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(flashBorrow);
|
|
||||||
|
|
||||||
let paybackMaker = new GelatoCoreLib.Action({
|
|
||||||
addr: connectMaker.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(paybackMaker);
|
|
||||||
|
|
||||||
let withdrawMaker = new GelatoCoreLib.Action({
|
|
||||||
addr: connectMaker.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(withdrawMaker);
|
|
||||||
|
|
||||||
let depositCompound = new GelatoCoreLib.Action({
|
|
||||||
addr: connectCompound.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(depositCompound);
|
|
||||||
|
|
||||||
let borrowCompound = new GelatoCoreLib.Action({
|
|
||||||
addr: connectCompound.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(borrowCompound);
|
|
||||||
|
|
||||||
let flashPayBack = new GelatoCoreLib.Action({
|
|
||||||
addr: connectInstaPool.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(flashPayBack);
|
|
||||||
|
|
||||||
let payProvider = new GelatoCoreLib.Action({
|
|
||||||
addr: connectGelatoProviderPayment.address,
|
|
||||||
data: constants.HashZero,
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
dataFlow: GelatoCoreLib.DataFlow.None,
|
|
||||||
termsOkCheck: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(payProvider);
|
|
||||||
|
|
||||||
const gasPriceCeil = constants.MaxUint256;
|
|
||||||
|
|
||||||
const gelatoFlashLoanTaskSpec = new GelatoCoreLib.TaskSpec({
|
|
||||||
conditions: [conditionMakerVaultIsSafe.address],
|
|
||||||
actions: spells,
|
|
||||||
gasPriceCeil,
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
connectedGelatoCore.provideTaskSpecs([gelatoFlashLoanTaskSpec])
|
|
||||||
).to.emit(gelatoCore, "LogTaskSpecProvided");
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.isTaskSpecProvided(
|
|
||||||
providerAddress,
|
|
||||||
gelatoFlashLoanTaskSpec
|
|
||||||
)
|
|
||||||
).to.be.equal("OK");
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.taskSpecGasPriceCeil(
|
|
||||||
providerAddress,
|
|
||||||
await connectedGelatoCore.hashTaskSpec(gelatoFlashLoanTaskSpec)
|
|
||||||
)
|
|
||||||
).to.be.equal(gasPriceCeil);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
});
|
|
||||||
|
|
||||||
it("#1: Use Maker Compound refinancing if the maker vault become unsafe after a market move.", async function () {
|
|
||||||
// User Actions
|
|
||||||
// Step 1 : User create a DeFi Smart Account
|
|
||||||
// Step 2 : User open a Vault, put some ether on it and borrow some dai
|
|
||||||
// Step 3 : User give authorization to gelato to use his DSA on his behalf.
|
|
||||||
// Step 4 : User submit a Debt Refinancing task if market move against him
|
|
||||||
// Step 5 : Market Move against the user (Mock)
|
|
||||||
// Step 6 : Executor execute the user's task
|
|
||||||
|
|
||||||
//#region Step 1 User create a DeFi Smart Account
|
|
||||||
|
|
||||||
// User create a Instadapp DeFi Smart Account
|
|
||||||
// who give him the possibility to interact
|
|
||||||
// with a large list of DeFi protocol through one
|
|
||||||
// Proxy 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)
|
|
||||||
);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 2 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 bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectMaker.abi,
|
|
||||||
functionname: "open",
|
|
||||||
inputs: ["ETH-A"],
|
|
||||||
});
|
|
||||||
|
|
||||||
await dsa.cast([bre.network.config.ConnectMaker], [openVault], userAddress);
|
|
||||||
|
|
||||||
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
|
|
||||||
let cdpId = String(cdps.ids[0]);
|
|
||||||
|
|
||||||
expect(cdps.ids[0].isZero()).to.be.false;
|
|
||||||
|
|
||||||
await dsa.cast(
|
|
||||||
[bre.network.config.ConnectMaker],
|
|
||||||
[
|
|
||||||
await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectMaker.abi,
|
|
||||||
functionname: "deposit",
|
|
||||||
inputs: [cdpId, ethers.utils.parseEther("10"), 0, 0],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
userAddress,
|
|
||||||
{
|
|
||||||
value: ethers.utils.parseEther("10"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
let makerVaultInitialBorrow = ethers.utils.parseUnits("1000", 18);
|
|
||||||
|
|
||||||
await dsa.cast(
|
|
||||||
[bre.network.config.ConnectMaker],
|
|
||||||
[
|
|
||||||
await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectMaker.abi,
|
|
||||||
functionname: "borrow",
|
|
||||||
inputs: [cdpId, makerVaultInitialBorrow, 0, 0],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
userAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(await daiToken.balanceOf(dsa.address)).to.be.equal(
|
|
||||||
ethers.utils.parseEther("1000")
|
|
||||||
);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 3 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 dsa.cast(
|
|
||||||
[bre.network.config.ConnectAuth],
|
|
||||||
[
|
|
||||||
await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectAuth.abi,
|
|
||||||
functionname: "add",
|
|
||||||
inputs: [gelatoCore.address],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
userAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(await dsa.isAuth(gelatoCore.address)).to.be.true;
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 4 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 debtBridgeCondition = new GelatoCoreLib.Condition({
|
|
||||||
inst: conditionMakerVaultIsSafe.address,
|
|
||||||
data: await conditionMakerVaultIsSafe.getConditionData(
|
|
||||||
cdpId,
|
|
||||||
"ETH/USD",
|
|
||||||
ethers.utils.parseUnits("40", 17)
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
// ======= Action/Spells setup ======
|
|
||||||
const spells = [];
|
|
||||||
|
|
||||||
let debtBridgeCalculation = new GelatoCoreLib.Action({
|
|
||||||
addr: connectGelatoDebtBridge.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectGelatoDebtBridgeABI.abi,
|
|
||||||
functionname: "fullRefinanceMakerToCompound",
|
|
||||||
inputs: [cdpId, 0, 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(debtBridgeCalculation);
|
|
||||||
|
|
||||||
let flashBorrow = new GelatoCoreLib.Action({
|
|
||||||
addr: connectInstaPool.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectInstaPool.abi,
|
|
||||||
functionname: "flashBorrow",
|
|
||||||
inputs: [bre.network.config.DAI, 0, "100", 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(flashBorrow);
|
|
||||||
|
|
||||||
let paybackMaker = new GelatoCoreLib.Action({
|
|
||||||
addr: connectMaker.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectMaker.abi,
|
|
||||||
functionname: "payback",
|
|
||||||
inputs: [cdpId, 0, "101", 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(paybackMaker);
|
|
||||||
|
|
||||||
let withdrawMaker = new GelatoCoreLib.Action({
|
|
||||||
addr: connectMaker.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectMaker.abi,
|
|
||||||
functionname: "withdraw",
|
|
||||||
inputs: [cdpId, 0, "102", 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(withdrawMaker);
|
|
||||||
|
|
||||||
let depositCompound = new GelatoCoreLib.Action({
|
|
||||||
addr: connectCompound.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectCompound.abi,
|
|
||||||
functionname: "deposit",
|
|
||||||
inputs: [ETH, 0, "103", 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(depositCompound);
|
|
||||||
|
|
||||||
let borrowCompound = new GelatoCoreLib.Action({
|
|
||||||
addr: connectCompound.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectCompound.abi,
|
|
||||||
functionname: "borrow",
|
|
||||||
inputs: [bre.network.config.DAI, 0, "104", 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(borrowCompound);
|
|
||||||
|
|
||||||
let flashPayBack = new GelatoCoreLib.Action({
|
|
||||||
addr: connectInstaPool.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectInstaPool.abi,
|
|
||||||
functionname: "flashPayback",
|
|
||||||
inputs: [bre.network.config.DAI, 0, 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(flashPayBack);
|
|
||||||
|
|
||||||
let payProvider = new GelatoCoreLib.Action({
|
|
||||||
addr: connectGelatoProviderPayment.address,
|
|
||||||
data: await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectGelatoProviderPaymentABI.abi,
|
|
||||||
functionname: "payProvider",
|
|
||||||
inputs: [ethers.constants.AddressZero, ETH, 0, "105", 0],
|
|
||||||
}),
|
|
||||||
operation: GelatoCoreLib.Operation.Delegatecall,
|
|
||||||
});
|
|
||||||
|
|
||||||
spells.push(payProvider);
|
|
||||||
|
|
||||||
const refinanceIfCompoundBorrowIsBetter = new GelatoCoreLib.Task({
|
|
||||||
conditions: [debtBridgeCondition],
|
|
||||||
actions: spells,
|
|
||||||
});
|
|
||||||
|
|
||||||
const gelatoExternalProvider = new GelatoCoreLib.GelatoProvider({
|
|
||||||
addr: providerAddress,
|
|
||||||
module: dsaProviderModule.address,
|
|
||||||
});
|
|
||||||
|
|
||||||
const expiryDate = 0;
|
|
||||||
await expect(
|
|
||||||
dsa.cast(
|
|
||||||
[connectGelato.address], // targets
|
|
||||||
[
|
|
||||||
await bre.run("abi-encode-withselector", {
|
|
||||||
abi: ConnectGelato.abi,
|
|
||||||
functionname: "submitTask",
|
|
||||||
inputs: [
|
|
||||||
gelatoExternalProvider,
|
|
||||||
refinanceIfCompoundBorrowIsBetter,
|
|
||||||
expiryDate,
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
], // datas
|
|
||||||
userAddress, // origin
|
|
||||||
{
|
|
||||||
gasLimit: 5000000,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
).to.emit(gelatoCore, "LogTaskSubmitted");
|
|
||||||
|
|
||||||
const taskReceipt = new GelatoCoreLib.TaskReceipt({
|
|
||||||
id: await gelatoCore.currentTaskReceiptId(),
|
|
||||||
userProxy: dsa.address,
|
|
||||||
provider: gelatoExternalProvider,
|
|
||||||
tasks: [refinanceIfCompoundBorrowIsBetter],
|
|
||||||
expiryDate,
|
|
||||||
});
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 5 Market Move against the user (Mock)
|
|
||||||
|
|
||||||
// Ether market price went from the current price to 250$
|
|
||||||
|
|
||||||
const gelatoGasPrice = await bre.run("fetchGelatoGasPrice");
|
|
||||||
expect(gelatoGasPrice).to.be.lte(GAS_PRICE_CEIL);
|
|
||||||
|
|
||||||
await oracleAggregator.mock(true, ethers.utils.parseUnits("400", 18));
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.canExec(taskReceipt, GAS_LIMIT, gelatoGasPrice)
|
|
||||||
).to.be.equal("ConditionNotOk:NotOKMakerVaultIsSafe");
|
|
||||||
|
|
||||||
await oracleAggregator.mock(true, ethers.utils.parseUnits("380", 18));
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await connectedGelatoCore.canExec(taskReceipt, GAS_LIMIT, gelatoGasPrice)
|
|
||||||
).to.be.equal("OK");
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
//#region Step 6 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
|
|
||||||
|
|
||||||
let fees = ethers.utils
|
|
||||||
.parseUnits(String(1933090 + 19331 * 2), 0)
|
|
||||||
.mul(await gelatoGasPriceOracle.latestAnswer());
|
|
||||||
let debt = await connectGelatoDebtBridge.getMakerVaultDebt(cdpId);
|
|
||||||
let collateral = (
|
|
||||||
await connectGelatoDebtBridge.getMakerVaultCollateralBalance(cdpId)
|
|
||||||
).sub(fees);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
|
|
||||||
let providerBalanceBeforeExecution = await providerWallet.getBalance();
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
connectedGelatoCore.exec(taskReceipt, {
|
|
||||||
gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei)
|
|
||||||
gasLimit: GAS_LIMIT,
|
|
||||||
})
|
|
||||||
).to.emit(gelatoCore, "LogExecSuccess");
|
|
||||||
|
|
||||||
let providerBalanceAfterExecution = await providerWallet.getBalance();
|
|
||||||
|
|
||||||
expect(providerBalanceAfterExecution).to.be.gt(
|
|
||||||
providerBalanceBeforeExecution
|
|
||||||
);
|
|
||||||
|
|
||||||
// compound position of DSA on cDai and cEth
|
|
||||||
let compoundPosition = await compoundResolver.getCompoundData(dsa.address, [
|
|
||||||
cDaiToken.address,
|
|
||||||
cEthToken.address,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// https://compound.finance/docs/ctokens#exchange-rate
|
|
||||||
// calculate cEth/ETH rate to convert back cEth to ETH
|
|
||||||
// for comparing with the withdrew Ether to the deposited one.
|
|
||||||
let exchangeRateCethToEth = (await cEthToken.getCash())
|
|
||||||
.add(await cEthToken.totalBorrows())
|
|
||||||
.sub(await cEthToken.totalReserves())
|
|
||||||
.div(await cEthToken.totalSupply());
|
|
||||||
|
|
||||||
// Estimated amount to borrowed token should be equal to the actual one read on compound contracts
|
|
||||||
expect(debt).to.be.equal(compoundPosition[0].borrowBalanceStoredUser);
|
|
||||||
|
|
||||||
// Estimated amount of collateral should be equal to the actual one read on compound contracts
|
|
||||||
expect(
|
|
||||||
collateral.sub(
|
|
||||||
compoundPosition[1].balanceOfUser.mul(exchangeRateCethToEth)
|
|
||||||
)
|
|
||||||
).to.be.lt(ethers.utils.parseUnits("1", 12));
|
|
||||||
|
|
||||||
debt = await connectGelatoDebtBridge.getMakerVaultDebt(cdpId);
|
|
||||||
collateral = await connectGelatoDebtBridge.getMakerVaultCollateralBalance(
|
|
||||||
cdpId
|
|
||||||
); // in Ether.
|
|
||||||
|
|
||||||
expect(debt).to.be.equal(ethers.constants.Zero);
|
|
||||||
expect(collateral).to.be.equal(ethers.constants.Zero);
|
|
||||||
|
|
||||||
// DSA contain 1000 DAI
|
|
||||||
expect(await daiToken.balanceOf(dsa.address)).to.be.equal(
|
|
||||||
makerVaultInitialBorrow
|
|
||||||
);
|
|
||||||
|
|
||||||
//#endregion
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user