feat: DebtBridge v1 contracts refactored

This commit is contained in:
gitpusha 2020-10-19 18:17:27 +02:00 committed by Luis Schliesske
parent 36b2a4f6be
commit 62ba6fe93d
40 changed files with 2224 additions and 2246 deletions

View File

@ -20,16 +20,16 @@ jobs: # a collection of steps
- ./node_modules
- run: # Compile
name: Compile
command: npx buidler compile
command: npx hardhat compile
- run: # Formatting
name: Prettier Check
command: yarn prettier --check .
- run: # Linting
name: ESLint
command: yarn eslint .
command: yarn eslint . && yarn lint:sol
- run: # Tests
name: Tests using buidler-ganache mainnet fork
command: npx buidler test
name: Tests using hardhat mainnet fork
command: npx hardhat test
# - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
# path: coverage
# prefix: coverage

2
.gitignore vendored
View File

@ -4,7 +4,7 @@ node_modules
# local env variables
.env
#buidler
#hardhat
artifacts/
cache/

View File

@ -2,4 +2,5 @@
#artifacts
cache
assets
coverage
coverage
contracts/vendor

View File

@ -5,8 +5,8 @@
"prettier/prettier": "error",
"code-complexity": ["warn", 5],
"function-max-lines": ["warn", 40],
"max-states-count": ["warn", 2],
"reason-string": ["error", {"maxLength": 32}],
"max-states-count": ["warn", 3],
"reason-string": ["off", {"maxLength": 32}],
"const-name-snakecase": "error",
"contract-name-camelcase": "error",
"event-name-camelcase": "error",
@ -19,8 +19,8 @@
"visibility-modifier-order": "error",
"avoid-call-value": "off",
"avoid-low-level-calls": "off",
"compiler-version": ["warn", "0.6.12"],
"func-visibility": ["error", {"ignoreConstructors": false}],
"compiler-version": ["warn", "0.7.4"],
"func-visibility": ["error", {"ignoreConstructors": true}],
"no-inline-assembly": "off",
"state-visibility": "error"
}

View File

@ -1 +1,2 @@
node_modules/
node_modules/
contracts/vendor

View File

@ -1,6 +1,8 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
// Solidity
"solidity.compileUsingRemoteVersion": "latest",
"solidity.enableLocalNodeCompiler": false,
"solidity.formatter": "prettier",
"solidity.linter": "solhint",
"solidity.packageDefaultDependenciesContractsDirectory": "",

View File

@ -20,11 +20,11 @@ Furtheremore the following contracts were added to showcase the automation of th
- `ProviderModuleDSA`: this is needed for any Gelato integration. It tells Gelato how the execution payload should be formatted. In this prototype, it formats the payload for the `DSA.cast` function.
- `ConnectGelato`: this is a Connector needed for the DSA to be able to submit Tasks to Gelato. In the test suite we unlock the DSA MultiSig Master account at 0xfCD22438AD6eD564a1C26151Df73F6B33B817B56, in order to be able to enable this Connector in our mainnet fork running on the local ganache instance.
- `ConnectGelato`: this is a Connector needed for the DSA to be able to submit Tasks to Gelato. In the test suite we unlock the DSA MultiSig Master account at 0xfCD22438AD6eD564a1C26151Df73F6B33B817B56, in order to be able to enable this Connector in our mainnet fork running on the local hardhat network instance.
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:
1. Clone this repo
2. Put your Infura ID in .env
2. Put your Alchemy ID in .env
3. yarn install
4. npx buidler test
4. npx hardhat test

View File

@ -1,106 +0,0 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {
GelatoConditionsStandard
} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
import {GelatoBytes} from "./GelatoBytes.sol";
import "./DSMath.sol";
interface IOracleAggregator {
function getMakerTokenPrice(string memory _pair)
external
view
returns (uint256);
}
interface IVaultResolver {
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);
}
contract ConditionMakerVaultIsSafe is GelatoConditionsStandard, DSMath {
using GelatoBytes for bytes;
address public oracleAggregator;
constructor(address _oracleAggregator) public {
oracleAggregator = _oracleAggregator;
}
function getConditionData(
uint256 _vaultID,
string memory _pair,
uint256 _unSafeLimit
) public pure virtual returns (bytes memory) {
return abi.encode(_vaultID, _pair, _unSafeLimit);
}
function ok(
uint256,
bytes calldata _conditionData,
uint256
) public view virtual override returns (string memory) {
(uint256 vaultID, string memory pair, uint256 unSafeLimit) = abi.decode(
_conditionData,
(uint256, string, uint256)
);
return _isVaultUnSafe(vaultID, pair, unSafeLimit);
}
function _isVaultUnSafe(
uint256 _vaultID,
string memory _pair,
uint256 _unSafeLimit
) internal view returns (string memory) {
uint256 latestPriceInRay = _getLatestPrice(_pair);
IVaultResolver.VaultData memory vault = IVaultResolver(
_getVaultResolverAddress()
)
.getVaultById(_vaultID);
uint256 colRatio = _vaultCollaterizationRatio(
_wmul(vault.collateral, latestPriceInRay),
vault.debt
);
if (_unSafeLimit > colRatio) {
return OK;
}
return "NotOKMakerVaultIsSafe";
}
function _getVaultResolverAddress() internal pure returns (address) {
return 0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5;
}
function _vaultCollaterizationRatio(uint256 _col, uint256 _debt)
internal
pure
returns (uint256)
{
return _wdiv(_col, _debt);
}
function _getLatestPrice(string memory _pair)
internal
view
returns (uint256)
{
return IOracleAggregator(oracleAggregator).getMakerTokenPrice(_pair);
}
}

View File

@ -1,506 +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 the 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 debtBridgeMakerToCompound(
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 = _mul(GASLIMIT, _getGasPrice());
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 _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)
)
)
);
}
}

View File

@ -1,76 +0,0 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
import "./IMemoryInterface.sol";
import {IERC20} from "@gelatonetwork/core/contracts/external/IERC20.sol";
interface ConnectorInterface {
function connectorID() external view returns (uint256 _type, uint256 _id);
function name() external view returns (string memory);
}
abstract contract ConnectGelatoProviderPaymentHelper is ConnectorInterface {
uint256 internal _id;
function connectorID()
public
view
override
returns (uint256 _type, uint256 _iD)
{
(_type, _iD) = (1, _id); // Should put specific value.
}
function _getMemoryAddr() internal pure returns (address) {
return 0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F; // InstaMemory Address
}
function _getUint(uint256 _getId, uint256 _val)
internal
returns (uint256 returnVal)
{
returnVal = _getId == 0
? _val
: IMemoryInterface(_getMemoryAddr()).getUint(_getId);
}
function _setUint(uint256 setId, uint256 val) internal {
if (setId != 0) IMemoryInterface(_getMemoryAddr()).setUint(setId, val);
}
function _getAddressETH() internal pure returns (address) {
return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH Address
}
}
contract ConnectGelatoProviderPayment is ConnectGelatoProviderPaymentHelper {
// Constant name must be in capitalized SNAKE_CASE
// solhint-disable-next-line
string public constant override name = "GelatoProviderPayement-v1.0";
constructor(uint256 _iD) public {
_id = _iD;
}
function payProvider(
address _provider,
address _token,
uint256 _amt,
uint256 _getID,
uint256 _setID
) public payable {
// Desable linter for too long require statement
// solhint-disable-next-line
require(
_provider != address(0x0),
"ConnectGelatoProviderPayment.payProvider:INVALIDADDESS."
);
uint256 amt = _getUint(_getID, _amt);
if (_token == _getAddressETH()) {
payable(_provider).transfer(amt);
return;
}
IERC20(_token).transfer(_provider, amt);
}
}

View File

@ -1,82 +0,0 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
contract DSMath {
function _add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-_add-overflow");
}
function _sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-_sub-underflow");
}
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-_mul-overflow");
}
function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x <= y ? x : y;
}
function _max(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x >= y ? x : y;
}
function _imin(int256 x, int256 y) internal pure returns (int256 z) {
return x <= y ? x : y;
}
function _imax(int256 x, int256 y) internal pure returns (int256 z) {
return x >= y ? x : y;
}
uint256 internal constant _WAD = 10**18;
uint256 internal constant _RAY = 10**27;
//rounds to zero if x*y < _WAD / 2
function _wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, y), _WAD / 2) / _WAD;
}
//rounds to zero if x*y < _WAD / 2
function _rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, y), _RAY / 2) / _RAY;
}
//rounds to zero if x*y < _WAD / 2
function _wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, _WAD), y / 2) / y;
}
//rounds to zero if x*y < _RAY / 2
function _rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, _RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated _multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function _rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
z = n % 2 != 0 ? x : _RAY;
for (n /= 2; n != 0; n /= 2) {
x = _rmul(x, x);
if (n % 2 != 0) {
z = _rmul(z, x);
}
}
}
}

View File

@ -1,8 +0,0 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
interface IMemoryInterface {
function setUint(uint256 _id, uint256 _val) external;
function getUint(uint256 _id) external returns (uint256);
}

View File

@ -1,62 +0,0 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {Ownable} from "@gelatonetwork/core/contracts/external/Ownable.sol";
import "./DSMath.sol";
interface IMakerPriceFeed {
function read() external view returns (bytes32);
}
contract OracleAggregatorStorage {
mapping(string => address) public makerOracle;
mapping(string => address) public compoundOracle;
mapping(string => address) public chainlinkOracle;
}
// 0x729D19f657BD0614b4985Cf1D82531c67569197B for ETH/USD medianizer it return value in wad standard.
contract OracleAggregator is OracleAggregatorStorage, Ownable, DSMath {
bool public mockMode;
uint256 public mockValue;
constructor() public Ownable() {
mockMode = false;
mockValue = 0;
}
function mock(bool _mockMode, uint256 _mockValue) public onlyOwner {
mockMode = _mockMode;
mockValue = _mockValue;
}
function addOracle(string memory _pair, address _oracleAddress)
external
onlyOwner
{
// Desable linter for too long require statement
// solhint-disable-next-line
require(
makerOracle[_pair] == address(0x0),
"OracleAggregator.Maker: Oracle already set."
);
makerOracle[_pair] = _oracleAddress;
}
function getMakerTokenPrice(string memory _pair)
external
view
returns (uint256)
{
// Desable linter for too long require statement
// solhint-disable-next-line
require(
makerOracle[_pair] != address(0x0),
"OracleAggregator.getMakerTokenPrice: CurrencyPairNotSupported."
);
if (mockMode) {
return mockValue;
}
return uint256(IMakerPriceFeed(makerOracle[_pair]).read());
}
}

View File

@ -1,121 +0,0 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {
GelatoProviderModuleStandard
} from "@gelatonetwork/core/contracts/provider_modules/GelatoProviderModuleStandard.sol";
import {
Task
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";
import "./ConnectGelatoProviderPayment.sol";
/// @dev InstaDapp Index
interface IndexInterface {
function connectors(uint256 version) external view returns (address);
function list() external view returns (address);
}
/// @dev InstaDapp List
interface ListInterface {
function accountID(address _account) external view returns (uint64);
}
/// @dev InstaDapp Defi Smart Account wallet
interface AccountInterface {
function version() external view returns (uint256);
function isAuth(address user) external view returns (bool);
function shield() external view returns (bool);
function cast(
address[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32[] memory responses);
}
contract ProviderModuleDSA is GelatoProviderModuleStandard {
IndexInterface public immutable index;
address public immutable gelatoCore;
address public immutable connectGelatoProviderPayment;
constructor(
IndexInterface _index,
address _gelatoCore,
address _connectGelatoProviderPayment
) public {
index = _index;
gelatoCore = _gelatoCore;
connectGelatoProviderPayment = _connectGelatoProviderPayment;
}
// ================= GELATO PROVIDER MODULE STANDARD ================
function isProvided(
address _userProxy,
address,
Task calldata _task
) external view override returns (string memory) {
// Verify InstaDapp account identity
if (ListInterface(index.list()).accountID(_userProxy) == 0)
return "ProviderModuleDSA.isProvided:InvalidUserProxy";
// Is GelatoCore authorized
if (!AccountInterface(_userProxy).isAuth(gelatoCore))
return "ProviderModuleDSA.isProvided:GelatoCoreNotAuth";
return OK;
}
/// @dev DS PROXY ONLY ALLOWS DELEGATE CALL for single actions, that's why we also use multisend
function execPayload(
uint256,
address,
address _provider,
Task calldata _task,
uint256
) external view override returns (bytes memory payload, bool) {
address[] memory targets = new address[](_task.actions.length);
for (uint256 i = 0; i < _task.actions.length; i++)
targets[i] = _task.actions[i].addr;
bytes[] memory datas = new bytes[](_task.actions.length);
for (uint256 i = 0; i < _task.actions.length; i++) {
if (_task.actions[i].addr == connectGelatoProviderPayment) {
// input the exact address of the provider
datas[i] = _getDelegateCallDataForProviderPaymentConnector(
_provider,
_task.actions[i].data
);
} else {
datas[i] = _task.actions[i].data;
}
}
payload = abi.encodeWithSelector(
AccountInterface.cast.selector,
targets,
datas,
gelatoCore
);
}
function _getDelegateCallDataForProviderPaymentConnector(
address _provider,
bytes calldata _data
) internal pure returns (bytes memory) {
(, address token, uint256 amt, uint256 getID, uint256 setID) = abi
.decode(_data[4:], (address, address, uint256, uint256, uint256));
return
abi.encodeWithSelector(
ConnectGelatoProviderPayment.payProvider.selector,
_provider,
token,
amt,
getID,
setID
);
}
}

View File

@ -0,0 +1,450 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
/* solhint-disable */
interface ConnectorInterface {
function connectorID() external view returns (uint256 _type, uint256 _id);
function name() external view returns (string memory);
}
interface MemoryInterface {
function setUint(uint256 _id, uint256 _val) external;
function getUint(uint256 _id) external 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);
}
library GelatoBytes {
function revertWithErrorString(
bytes memory _bytes,
string memory _tracingInfo
) internal pure {
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
} else {
revert(
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
);
}
} else {
revert(
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
);
}
}
}
abstract contract DSMath {
// _add, _sub, _mul to avoid clash with Inline Assembly op naming
function _add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function _sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-_mul-overflow");
}
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x <= y ? x : y;
}
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
return x >= y ? x : y;
}
function imin(int256 x, int256 y) internal pure returns (int256 z) {
return x <= y ? x : y;
}
function imax(int256 x, int256 y) internal pure returns (int256 z) {
return x >= y ? x : y;
}
uint256 constant WAD = 10**18;
uint256 constant RAY = 10**27;
//rounds to zero if x*y < WAD / 2
function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, y), WAD / 2) / WAD;
}
//rounds to zero if x*y < WAD / 2
function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, y), RAY / 2) / RAY;
}
//rounds to zero if x*y < WAD / 2
function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, WAD), y / 2) / y;
}
//rounds to zero if x*y < RAY / 2
function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = _add(_mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
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) MemoryInterface(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
: MemoryInterface(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))
}
}
}
/* solhint-enable */
abstract contract GelatoHelpers is Helpers {
function _getGelatoGasPriceOracle() internal pure returns (address) {
return 0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C;
}
function _getGasPrice() internal view returns (uint256) {
return
uint256(
GelatoGasPriceOracle(_getGelatoGasPriceOracle()).latestAnswer()
);
}
}
abstract contract MakerResolver is GelatoHelpers {
function getMakerVault(uint256 _vaultId)
public
view
returns (IMakerResolver.VaultData memory)
{
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 _getMakerResolver() internal pure returns (address) {
return 0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5;
}
}
contract ConnectGelatoDebtBridgeFromMaker is MakerResolver {
using GelatoBytes for bytes;
// solhint-disable-next-line const-name-snakecase
string public constant override name = "GelatoDebtBridge-v1.0";
uint256 public constant GAS_LIMIT = 1933090 + (19331 * 2); // 1933080 + ~2% (Estimated Value)
constructor(uint256 _id) {
__id = _id;
}
/// @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.
/// @param _vaultId The id of the makerDAO vault.
/// @param _wMinColRatioMaker Min col ratio (wad) on Maker debt position
/// @param _wMinColRatioB Min col ratio (wad) on debt position B (e.g. Compound, Maker, ...)
/// @param _priceOracle The price oracle contract to supply the collateral price
/// e.g. Maker's ETH/USD oracle for ETH collateral pricing.
/// @param _oraclePayload The data for making the staticcall to the oracle's read
/// method e.g. the function selector of MakerOracle's read function.
// @param _getId Id for writting in instaMemory.
// @param _setId Id for loading from instaMemory.
function saveDebtBridgeDataToMemory(
uint256 _vaultId,
uint256 _wMinColRatioMaker, // should be in ray because maker use ray standard
uint256 _wMinColRatioB, // should be in wad because compound use wad standard
address _priceOracle,
bytes calldata _oraclePayload,
uint256, /*_getId,*/
uint256 /*_setId*/
) public virtual {
(
uint256 wDaiDebtToMove,
uint256 wCollateralToMove,
uint256 gasFeesPaidFromCol
) = computeDebtBridge(
_vaultId,
_wMinColRatioMaker,
_wMinColRatioB,
_priceOracle,
_oraclePayload
);
setUint(600, wDaiDebtToMove); // flashloan borrow
setUint(601, wDaiDebtToMove); // payback maker
setUint(602, _add(wCollateralToMove, gasFeesPaidFromCol)); // withdraw maker
setUint(603, wCollateralToMove); // deposit compound
setUint(604, wDaiDebtToMove); // borrow compound
setUint(605, gasFeesPaidFromCol); // pay the Gelato Provider (TO DO: unsafe)
}
/// @notice Computes values needed for DebtBridge Maker->ProtocolB
/// @dev Use wad for colRatios.
/// @param _vaultId The id of the makerDAO vault.
/// @param _wMinColRatioMaker Min col ratio (wad) on Maker debt position
/// @param _wMinColRatioB Min col ratio (wad) on debt position B (e.g. Compound, Maker, ...)
/// @param _priceOracle The price oracle contract to supply the collateral price
/// e.g. Maker's ETH/USD oracle for ETH collateral pricing.
/// @param _oraclePayload The data for making the staticcall to the oracle's read
/// method e.g. the function selector of MakerOracle's read function.
/// @return wDaiDebtToMove DAI Debt (wad) to: flashBorrow->repay Maker->withdraw from B->flashPayback.
/// @return wCollateralToMove (wad) to: withdraw from Maker and deposit on B.
/// @return gasFeesPaidFromCol Gelato automation-gas-fees paid from user's collateral
// solhint-disable function-max-lines
function computeDebtBridge(
uint256 _vaultId,
uint256 _wMinColRatioMaker,
uint256 _wMinColRatioB,
address _priceOracle,
bytes calldata _oraclePayload
)
public
view
virtual
returns (
uint256 wDaiDebtToMove,
uint256 wCollateralToMove,
uint256 gasFeesPaidFromCol
)
{
uint256 wColPrice;
// Stack too deep
{
(bool success, bytes memory returndata) = _priceOracle.staticcall(
_oraclePayload
);
if (!success) {
returndata.revertWithErrorString(
"ConnectGelatoDebtBridgeFromMaker.computeDebtBridge:oracle:"
);
}
wColPrice = abi.decode(returndata, (uint256));
}
// TO DO: add fee mechanism for non-ETH collateral debt bridge
// uint256 gasFeesPaidFromCol = _mul(GAS_LIMIT, wmul(_getGasPrice(), latestPrice));
gasFeesPaidFromCol = _mul(GAS_LIMIT, _getGasPrice());
uint256 wPricedCol = wmul(
_sub(getMakerVaultCollateralBalance(_vaultId), gasFeesPaidFromCol),
wColPrice
);
uint256 wDaiDebtOnMaker = getMakerVaultDebt(_vaultId);
wCollateralToMove = wCalcCollateralToWithdraw(
_wMinColRatioMaker,
_wMinColRatioB,
wColPrice,
wPricedCol,
wDaiDebtOnMaker
);
wDaiDebtToMove = wCalcDebtToRepay(
_wMinColRatioMaker,
_wMinColRatioB,
wPricedCol,
wDaiDebtOnMaker
);
}
/// @notice Compute collateral (wad) to move from Maker to B.
/// @dev TO DO: explain or link to formula paper.
/// @param _wMinColRatioMaker Min col ratio (wad) on Maker debt position
/// @param _wMinColRatioB Min col ratio (wad) on debt position B (e.g. Compound, Maker, ...)
/// @param _wColPrice Price of the collateral (wad) in oracle price units.
/// @param _wPricedCol Collateral to move (wad) valued in oracle price.
/// @param _wDaiDebtOnMaker Amount of DAI (wad) borrowed from Maker.
/// @return collateral to withdraw from A in wad
function wCalcCollateralToWithdraw(
uint256 _wMinColRatioMaker,
uint256 _wMinColRatioB,
uint256 _wColPrice,
uint256 _wPricedCol,
uint256 _wDaiDebtOnMaker
) public pure virtual returns (uint256) {
return
wdiv(
_sub(
_wPricedCol,
wdiv(
_sub(
wmul(_wMinColRatioMaker, _wPricedCol),
wmul(
_wMinColRatioMaker,
wmul(_wMinColRatioB, _wDaiDebtOnMaker)
)
),
_sub(_wMinColRatioMaker, _wMinColRatioB)
)
),
_wColPrice
);
}
/// @notice Compute debt (wad) to flashBorrow->repay Maker->withdraw from B->flashPayback
/// @dev TO DO: explain or link to formula paper.
/// @param _wMinColRatioMaker Min col ratio (wad) on Maker debt position
/// @param _wMinColRatioB Min col ratio (wad) on debt position B (e.g. Compound, Maker, ...)
/// @param _wPricedCol Collateral to move (wad) valued in oracle price.
/// @param _wDaiDebtOnMaker Amount of DAI (wad) borrowed from Maker.
/// @return amount of borrowed token to pay back in wad
function wCalcDebtToRepay(
uint256 _wMinColRatioMaker,
uint256 _wMinColRatioB,
uint256 _wPricedCol,
uint256 _wDaiDebtOnMaker
) public pure virtual returns (uint256) {
return
_sub(
_wDaiDebtOnMaker,
wmul(
wdiv(1e18, _wMinColRatioMaker),
wdiv(
_sub(
wmul(_wMinColRatioMaker, _wPricedCol),
wmul(
_wMinColRatioMaker,
wmul(_wMinColRatioB, _wDaiDebtOnMaker)
)
),
_sub(_wMinColRatioMaker, _wMinColRatioB)
)
)
);
}
}

View File

@ -0,0 +1,334 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.4;
/* solhint-disable */
interface MemoryInterface {
function setUint(uint256 _id, uint256 _val) external;
function getUint(uint256 _id) external returns (uint256);
}
interface ConnectorInterface {
function connectorID() external view returns (uint256 _type, uint256 _id);
function name() external view returns (string memory);
}
interface IERC20 {
function transfer(address recipient, uint256 amount)
external
returns (bool);
}
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(
address(this).balance >= amount,
"Address: insufficient balance"
);
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{value: amount}("");
require(
success,
"Address: unable to send value, recipient may have reverted"
);
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data)
internal
returns (bytes memory)
{
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return
functionCallWithValue(
target,
data,
value,
"Address: low-level call with value failed"
);
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(
address(this).balance >= value,
"Address: insufficient balance for call"
);
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{value: value}(
data
);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(
token,
abi.encodeWithSelector(token.transfer.selector, to, value)
);
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(
data,
"SafeERC20: low-level call failed"
);
if (returndata.length > 0) {
// Return data is optional
// solhint-disable-next-line max-line-length
require(
abi.decode(returndata, (bool)),
"SafeERC20: ERC20 operation did not succeed"
);
}
}
}
/* solhint-enable */
/* solhint-disable private-vars-leading-underscore */
abstract contract Helpers is ConnectorInterface {
uint256 internal __id;
/**
* @dev Connector Details
*/
function connectorID()
public
view
override
returns (uint256 _type, uint256 id)
{
(_type, id) = (1, __id); // Should put specific value.
}
/**
* @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) MemoryInterface(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
: MemoryInterface(getMemoryAddr()).getUint(getId);
}
}
/// @title ConnectGelatoProviderPayment
/// @notice InstaDapp Connector to compensate Gelato automation-gas Providers.
/// @author Gelato Team
contract ConnectGelatoProviderPayment is Helpers {
using Address for address payable;
using SafeERC20 for IERC20;
// solhint-disable-next-line const-name-snakecase
string public constant override name = "GelatoProviderPayement-v1.0";
constructor(uint256 _id) {
__id = _id;
}
/// @notice Transfers automation gas fees to Gelato Provider
/// @dev Gelato Provider risks:
/// - _getId does not match actual InstaMemory provider payment slot
/// - _token balance not in DSA
/// - worthless _token risk
/// @param _provider The Provider who pays the Gelato network for automation.
// This param should be verified / replaced by the ProviderModule in Gelato on-chain.
// In the latter case, it does not matter what address is passed off-chain.
/// @param _token The token used to pay the Provider.
/// @param _amt The amount of _token to pay the Gelato Provider.
/// @param _getId The InstaMemory slot at which the payment amount was stored.
/// @param _setId The InstaMemory slot to save the provider payout amound in.
function payProvider(
address _provider,
address _token,
uint256 _amt,
uint256 _getId,
uint256 _setId
) public payable virtual {
require(
_provider != address(0x0),
"ConnectGelatoProviderPayment.payProvider:addr0"
);
uint256 amt = getUint(_getId, _amt);
setUint(_setId, amt);
_token == getAddressETH()
? payable(_provider).sendValue(amt)
: IERC20(_token).safeTransfer(_provider, amt);
}
}

View File

@ -0,0 +1,121 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import {
GelatoProviderModuleStandard
} from "@gelatonetwork/core/contracts/provider_modules/GelatoProviderModuleStandard.sol";
import {
Task
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";
import {AccountInterface} from "../interfaces/InstaDapp.sol";
import {
ConnectGelatoProviderPayment
} from "../connectors/ConnectGelatoProviderPayment.sol";
/// @notice Gelato Provider Module for the InstaDapp DSA
/// @dev Used by Provider to sanity check any third-party Tasks they pay for
/// @author Gelato Network Team
contract ProviderModuleDSA is GelatoProviderModuleStandard {
/// @dev DSA must have gelatoCore as auth and gelatoCore is emitted as origin of cast
address public immutable gelatoCore;
/// @notice A trusted Connector to pay Provider for e.g. User's Gelato gas usage.
/// @dev Automated InstaDapp Use Cases that rely on a third-party Gelato Provider
/// to pay for automation will likely have this Connector in their spells.
address public immutable connectGelatoProviderPayment;
// TO DO: remove `public` after hardhat file import bugfix
// https://github.com/nomiclabs/hardhat/issues/916
constructor(address _gelatoCore, address _connectGelatoProviderPayment)
public
{
gelatoCore = _gelatoCore;
connectGelatoProviderPayment = _connectGelatoProviderPayment;
}
// ================= GELATO PROVIDER MODULE STANDARD ================
/// @notice Standard Gelato function for Provider's Task sanity checks
/// @dev For more Provider security we should also check:
/// - ListInterface(index.list()).accountID(_userProxy)
/// - if (shield) connectors.isStaticConnector(targets)
/// - connectors.isConnector(targets)
/// But we skip those here to save gas
/// @param _userProxy The DSA which submitted the Task
/// @return whether the Provider is pays for the Task.
function isProvided(
address _userProxy,
address,
Task calldata
) public view virtual override returns (string memory) {
try AccountInterface(_userProxy).isAuth(gelatoCore) returns (
bool gelatoCoreIsAuth
) {
if (!gelatoCoreIsAuth)
return "ProviderModuleDSA.isProvided:GelatoCoreNotAuth";
} catch Error(string memory err) {
return
string(abi.encodePacked("ProviderModuleDSA.isProvided:", err));
} catch {
return "ProviderModuleDSA.isProvided:undefined";
}
return OK;
}
/// @notice Gelato Standard Provider function to retrieve payload for the DSA
/// @dev This formats the Gelato Task into a DSA compatible payload and
/// it also inserts the _provider into the ConnectGelatoProviderPayment payload,
/// to make sure that it cannot be spoofed thus e.g. securing Provider payments.
/// @param _provider the actual Provider address verified by GelatoCore system.
/// @param _task The Task in Gelato format.
/// @return The execution payload in DSA format
/// @return bool=false because no execRevert checks must be handled on GelatoCore
/// because the DSA reverts, if a spell revert is caught during delegatecall.
function execPayload(
uint256,
address,
address _provider,
Task calldata _task,
uint256
) public view virtual override returns (bytes memory, bool) {
address[] memory targets = new address[](_task.actions.length);
for (uint256 i = 0; i < _task.actions.length; i++)
targets[i] = _task.actions[i].addr;
bytes[] memory datas = new bytes[](_task.actions.length);
for (uint256 i = 0; i < _task.actions.length; i++) {
if (_task.actions[i].addr == connectGelatoProviderPayment)
datas[i] = _replaceProvider(_provider, _task.actions[i].data);
else datas[i] = _task.actions[i].data;
}
return (
abi.encodeWithSelector(
AccountInterface.cast.selector,
targets,
datas,
gelatoCore
),
false
);
}
function _replaceProvider(address _provider, bytes calldata _data)
internal
pure
returns (bytes memory)
{
(, address token, uint256 amt, uint256 getID, uint256 setID) = abi
.decode(_data[4:], (address, address, uint256, uint256, uint256));
return
abi.encodeWithSelector(
ConnectGelatoProviderPayment.payProvider.selector,
_provider,
token,
amt,
getID,
setID
);
}
}

View File

@ -1,5 +1,5 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma solidity 0.7.4;
import {
GelatoConditionsStandard
@ -9,7 +9,7 @@ import {IERC20} from "@gelatonetwork/core/contracts/external/IERC20.sol";
import {
IGelatoCore
} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol";
import {GelatoBytes} from "./GelatoBytes.sol";
import {GelatoBytes} from "../../lib/GelatoBytes.sol";
/// @notice A general contract for retrieving and comparing 2 uints from 2 contracts.
/// @dev This contract only works if the refContracts fns returndata has a uint in
@ -18,7 +18,7 @@ contract ConditionCompareUintsFromTwoSources is GelatoConditionsStandard {
using GelatoBytes for bytes;
using SafeMath for uint256;
/// @notice Helper to encode the Condition data field off-chain
/// @notice Helper to encode the Condition.data field off-chain
function getConditionData(
address _sourceA,
address _sourceB,

View File

@ -0,0 +1,90 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import {
GelatoConditionsStandard
} from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol";
import {wmul, wdiv} from "../../vendor/DSMath.sol";
import {GelatoBytes} from "../../lib/GelatoBytes.sol";
import {IInstaMakerResolver} from "../../interfaces/IInstaMakerResolver.sol";
/// @title ConditionMakerVaultUnsafe
/// @notice Condition tracking Maker vault collateralization safety requirements.
/// @author Gelato Team
contract ConditionMakerVaultUnsafe is GelatoConditionsStandard {
using GelatoBytes for bytes;
/// @notice Convenience function for off-chain _conditionData encoding
/// @dev Use the return for your Task's Condition.data field off-chain.
/// @return The encoded payload for your Task's Condition.data field.
function getConditionData(
uint256 _vaultId,
address _priceOracle,
bytes calldata _oraclePayload,
uint256 _minColRatio
) public pure virtual returns (bytes memory) {
return abi.encode(_vaultId, _priceOracle, _oraclePayload, _minColRatio);
}
/// @notice Standard GelatoCore system function
/// @dev A standard interface for GelatoCore to read Conditions
/// @param _conditionData The data you get from `getConditionData()`
/// @return OK if the Condition is there, else some error message.
function ok(
uint256,
bytes calldata _conditionData,
uint256
) public view virtual override returns (string memory) {
(
uint256 _vaultID,
address _priceOracle,
bytes memory _oraclePayload,
uint256 _minColRatio
) = abi.decode(_conditionData, (uint256, address, bytes, uint256));
return
isVaultUnsafe(_vaultID, _priceOracle, _oraclePayload, _minColRatio);
}
/// @notice Specific implementation of this Condition's ok function
/// @dev The price oracle must return a uint256 WAD (10**18) value.
/// @param _vaultId The id of the Maker vault
/// @param _priceOracle The price oracle contract to supply the collateral price
/// e.g. Maker's ETH/USD oracle for ETH collateral pricing.
/// @param _oraclePayload The data for making the staticcall to the oracle's read
/// method e.g. the selector for MakerOracle's read fn.
/// @param _minColRatio The minimum collateral ratio measured in the price
/// of the collateral as specified by the _priceOracle.
/// @return OK if the Maker Vault is unsafe, otherwise some error message.
function isVaultUnsafe(
uint256 _vaultId,
address _priceOracle,
bytes memory _oraclePayload,
uint256 _minColRatio
) public view virtual returns (string memory) {
(bool success, bytes memory returndata) = _priceOracle.staticcall(
_oraclePayload
);
if (!success) {
returndata.revertWithErrorString(
"ConditionMakerVaultUnsafe.isVaultUnsafe:oracle:"
);
}
uint256 colPriceInWad = abi.decode(returndata, (uint256));
IInstaMakerResolver.VaultData memory vault = IInstaMakerResolver(
0x0A7008B38E7015F8C36A49eEbc32513ECA8801E5
)
.getVaultById(_vaultId);
uint256 colRatio = wdiv(
wmul(vault.collateral, colPriceInWad),
vault.debt
);
return colRatio < _minColRatio ? OK : "MakerVaultNotUnsafe";
}
}

View File

@ -0,0 +1,21 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
interface IInstaMakerResolver {
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);
}

View File

@ -0,0 +1,6 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
interface IMakerPriceFeed {
function read() external view returns (bytes32);
}

View File

@ -0,0 +1,37 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
/// @notice Interface InstaDapp Index
interface IndexInterface {
function connectors(uint256 version) external view returns (address);
function list() external view returns (address);
}
/// @notice Interface InstaDapp List
interface ListInterface {
function accountID(address _account) external view returns (uint64);
}
/// @notice Interface InstaDapp InstaMemory
interface MemoryInterface {
function setUint(uint256 _id, uint256 _val) external;
function getUint(uint256 _id) external returns (uint256);
}
/// @notice Interface InstaDapp Defi Smart Account wallet
interface AccountInterface {
function cast(
address[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32[] memory responses);
function version() external view returns (uint256);
function isAuth(address user) external view returns (bool);
function shield() external view returns (bool);
}

View File

@ -1,5 +1,5 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma solidity 0.7.4;
library GelatoBytes {
function calldataSliceSelector(bytes calldata _bytes)

View File

@ -1,5 +1,5 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma solidity 0.7.4;
contract MockCDAI {
// DSR
@ -11,7 +11,7 @@ contract MockCDAI {
uint256 public supplyRatePerSecond;
constructor(uint256 _sRPS) public {
constructor(uint256 _sRPS) {
supplyRatePerSecond = _sRPS;
}

View File

@ -1,5 +1,5 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.6.12;
pragma solidity 0.7.4;
contract MockDSR {
// DSR
@ -13,7 +13,7 @@ contract MockDSR {
uint256 public dsr;
constructor(uint256 _dsr) public {
constructor(uint256 _dsr) {
dsr = _dsr;
}

View File

@ -0,0 +1,69 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
import {Ownable} from "../vendor/Ownable.sol";
import {GelatoBytes} from "../lib/GelatoBytes.sol";
/// @title PriceOracleResolver
/// @notice Contract with convenience methods to retrieve oracle addresses or to mock test.
/// @dev Can be used to:
/// - Query oracle address for Gelato Condition payloads on frontend
/// - Test Conditions by using `getMockPrice(address _test)` as `oraclePayload`
contract PriceOracleResolver is Ownable {
using GelatoBytes for bytes;
mapping(string => address) public oracle;
mapping(string => bytes) public oraclePayload;
mapping(address => uint256) public mockPrice;
/// @notice Adds a new Oracle address
/// @dev Only owner can call this, but existing oracle entries are immutable
/// @param _oracle The descriptor of the oracle e.g. ETH/USD-Maker-v1
/// @param _oracleAddress The address of the oracle contract
/// @param _oraclePayload The payload with function selector for the oracle request.
function addOracle(
string memory _oracle,
address _oracleAddress,
bytes calldata _oraclePayload
) external onlyOwner {
require(
oracle[_oracle] == address(0),
"PriceOracleResolver.addOracle: set"
);
oracle[_oracle] = _oracleAddress;
oraclePayload[_oracle] = _oraclePayload;
}
/// @notice Function that allows easy oracle data testing in production.
/// @dev Your mock prices cannot be overriden by someone else.
/// @param _mockPrice The mock data you want to test against.
function setMockPrice(uint256 _mockPrice) public {
mockPrice[msg.sender] = _mockPrice;
}
/// @notice Use with setMockPrice for easy testing in production.
/// @dev Encode oracle=PriceOracleResolver and oraclePayload=getMockPrice(tester)
/// to test your Conditions or Actions that make dynamic calls to price oracles.
/// @param _tester The msg.sender during setMockPrice.
/// @return The tester's mockPrice.
function getMockPrice(address _tester) external view returns (uint256) {
return mockPrice[_tester];
}
/// @notice A generelized getter for a price supplied by an oracle contract.
/// @dev The oracle returndata must be formatted as a single uint256.
/// @param _oracle The descriptor of our oracle e.g. ETH/USD-Maker-v1
/// @return The uint256 oracle price
function getPrice(string memory _oracle) external view returns (uint256) {
address oracleAddr = oracle[_oracle];
if (oracleAddr == address(0))
revert("PriceOracleResolver.getPrice: no oracle");
(bool success, bytes memory returndata) = oracleAddr.staticcall(
oraclePayload[_oracle]
);
if (!success)
returndata.revertWithErrorString("PriceOracleResolver.getPrice:");
return abi.decode(returndata, (uint256));
}
}

87
contracts/vendor/DSMath.sol vendored Normal file
View File

@ -0,0 +1,87 @@
// "SPDX-License-Identifier: AGPL-3.0-or-later"
/// math.sol -- mixin for inline numerical wizardry
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity 0.7.4;
function add(uint x, uint y) pure returns (uint z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint x, uint y) pure returns (uint z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function mul(uint x, uint y) pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
function min(uint x, uint y) pure returns (uint z) {
return x <= y ? x : y;
}
function max(uint x, uint y) pure returns (uint z) {
return x >= y ? x : y;
}
function imin(int x, int y) pure returns (int z) {
return x <= y ? x : y;
}
function imax(int x, int y) pure returns (int z) {
return x >= y ? x : y;
}
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
//rounds to zero if x*y < WAD / 2
function wmul(uint x, uint y) pure returns (uint z) {
z = add(mul(x, y), WAD / 2) / WAD;
}
//rounds to zero if x*y < WAD / 2
function rmul(uint x, uint y) pure returns (uint z) {
z = add(mul(x, y), RAY / 2) / RAY;
}
//rounds to zero if x*y < WAD / 2
function wdiv(uint x, uint y) pure returns (uint z) {
z = add(mul(x, WAD), y / 2) / y;
}
//rounds to zero if x*y < RAY / 2
function rdiv(uint x, uint y) pure returns (uint z) {
z = add(mul(x, RAY), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint x, uint n) pure returns (uint z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}

82
contracts/vendor/Ownable.sol vendored Normal file
View File

@ -0,0 +1,82 @@
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.7.4;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal virtual {
require(
newOwner != address(0),
"Ownable: new owner is the zero address"
);
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}

View File

@ -1,5 +1,7 @@
// Buidler
const {task, usePlugin, types} = require("@nomiclabs/buidler/config");
const {task, types} = require("hardhat/config");
require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-waffle");
// Libraries
const assert = require("assert");
@ -9,20 +11,23 @@ const GelatoCoreLib = require("@gelatonetwork/core");
// Process Env Variables
require("dotenv").config();
const INFURA_ID = process.env.INFURA_ID;
assert.ok(INFURA_ID, "no Infura ID in process.env");
// const INFURA_ID = process.env.INFURA_ID;
// assert.ok(INFURA_ID, "no Infura ID in process.env");
const ALCHEMY_ID = process.env.ALCHEMY_ID;
assert.ok(ALCHEMY_ID, "no Alchemy ID in process.env");
const INSTA_MASTER = "0xb1DC62EC38E6E3857a887210C38418E4A17Da5B2";
// ================================= CONFIG =========================================
module.exports = {
defaultNetwork: "ganache",
defaultNetwork: "hardhat",
networks: {
ganache: {
timeout: 150000,
hardhat: {
// Standard config
url: "http://localhost:8545",
fork: `https://mainnet.infura.io/v3/${INFURA_ID}`,
unlocked_accounts: [INSTA_MASTER],
// timeout: 150000,
forking: {
url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
blockNumber: 11104384,
},
// Custom
GelatoCore: "0x1d681d76ce96E4d70a88A00EBbcfc1E47808d0b8",
GelatoGasPriceOracle: "0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C",
@ -48,17 +53,12 @@ module.exports = {
ProviderModuleDSA: "0x0C25452d20cdFeEd2983fa9b9b9Cf4E81D6f2fE2",
},
},
solc: {
version: "0.6.12",
optimizer: {enabled: true},
solidity: {
version: "0.7.4",
optimizer: {enabled: process.env.DEBUG ? false : true},
},
};
// ================================= PLUGINS =========================================
usePlugin("@nomiclabs/buidler-ethers");
usePlugin("@nomiclabs/buidler-ganache");
usePlugin("@nomiclabs/buidler-waffle");
// ================================= TASKS =========================================
task("abi-encode-withselector")
.addPositionalParam(
@ -128,13 +128,13 @@ task(
)
.addOptionalParam("gelatocoreaddress")
.addFlag("log", "Logs return values to stdout")
.setAction(async (taskArgs, bre) => {
.setAction(async (taskArgs, hre) => {
try {
const gelatoCore = await bre.ethers.getContractAt(
const gelatoCore = await hre.ethers.getContractAt(
GelatoCoreLib.GelatoCore.abi,
taskArgs.gelatocoreaddress
? taskArgs.gelatocoreaddress
: bre.network.config.GelatoCore
: hre.network.config.GelatoCore
);
const oracleAbi = ["function latestAnswer() view returns (int256)"];
@ -142,7 +142,7 @@ task(
const gelatoGasPriceOracleAddress = await gelatoCore.gelatoGasPriceOracle();
// Get gelatoGasPriceOracleAddress
const gelatoGasPriceOracle = await bre.ethers.getContractAt(
const gelatoGasPriceOracle = await hre.ethers.getContractAt(
oracleAbi,
gelatoGasPriceOracleAddress
);

View File

@ -7,25 +7,25 @@
"private": false,
"scripts": {
"rebase": "HUSKY_SKIP_HOOKS=1 git rebase",
"compile": "npx buidler compile",
"compile": "npx hardhat compile",
"format": "prettier --write .",
"lint": "eslint --cache . && yarn lint:sol",
"lint:sol": "solhint contracts/**/*.sol",
"lint:fix": "eslint --cache --fix . && solhint --fix contracts/**/*.sol",
"test": "npx buidler test"
"test": "npx hardhat test",
"debug": "DEBUG=true npx hardhat test"
},
"devDependencies": {
"@gelatonetwork/core": "0.5.3",
"@nomiclabs/buidler": "1.4.7",
"@nomiclabs/buidler-ethers": "2.0.2",
"@nomiclabs/buidler-ganache": "1.3.3",
"@nomiclabs/buidler-waffle": "2.1.0",
"@gelatonetwork/core": "1.0.0",
"@nomiclabs/hardhat-ethers": "2.0.0",
"@nomiclabs/hardhat-waffle": "2.0.0",
"chai": "4.2.0",
"dotenv": "8.2.0",
"eslint": "7.10.0",
"eslint-config-prettier": "6.12.0",
"ethereum-waffle": "3.1.1",
"ethers": "5.0.17",
"ethers": "5.0.19",
"hardhat": "2.0.0",
"husky": ">=4",
"lint-staged": ">=10",
"prettier": "2.1.2",
@ -42,7 +42,6 @@
},
"lint-staged": {
"*.js": "eslint --cache --fix",
"*.sol": "solhint",
"*.{js,sol,css,md}": "prettier --write"
}
}

View File

@ -1,8 +1,8 @@
// running `npx buidler test` automatically makes use of buidler-waffle plugin
// running `npx hardhat test` automatically makes use of hardhat-waffle plugin
// => only dependency we need is "chai"
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const hre = require("hardhat");
const {ethers} = hre;
const GelatoCoreLib = require("@gelatonetwork/core");
//const { sleep } = GelatoCoreLib;
@ -20,8 +20,8 @@ const ProviderModuleDSA_ABI = require("../pre-compiles/ProviderModuleDSA_ABI.jso
describe("DSA setup with Gelato Tests", function () {
this.timeout(50000);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
@ -53,19 +53,19 @@ describe("DSA setup with Gelato Tests", function () {
// ===== DSA LOCAL SETUP ==================
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
hre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
hre.network.config.InstaList
);
instaConnectors = await ethers.getContractAt(
InstaConnectors.abi,
bre.network.config.InstaConnectors
hre.network.config.InstaConnectors
);
instaAccount = await ethers.getContractAt(
InstaAccount.abi,
bre.network.config.InstaAccount
hre.network.config.InstaAccount
);
dsaVersion = await instaAccount.version();
@ -86,11 +86,11 @@ describe("DSA setup with Gelato Tests", function () {
// ===== GELATO LOCAL SETUP ==================
gelatoCore = await ethers.getContractAt(
GelatoCoreLib.GelatoCore.abi,
bre.network.config.GelatoCore
hre.network.config.GelatoCore
);
providerModuleDSA = await ethers.getContractAt(
ProviderModuleDSA_ABI,
bre.network.config.ProviderModuleDSA
hre.network.config.ProviderModuleDSA
);
});
@ -100,13 +100,13 @@ describe("DSA setup with Gelato Tests", function () {
expect(await instaIndex.connectors(dsaVersion)).to.be.equal(
instaConnectors.address
);
expect(await instaConnectors.connectors(bre.network.config.ConnectAuth)).to
expect(await instaConnectors.connectors(hre.network.config.ConnectAuth)).to
.be.true;
expect(await instaConnectors.connectors(bre.network.config.ConnectBasic)).to
expect(await instaConnectors.connectors(hre.network.config.ConnectBasic)).to
.be.true;
expect(await instaConnectors.connectors(bre.network.config.ConnectMaker)).to
expect(await instaConnectors.connectors(hre.network.config.ConnectMaker)).to
.be.true;
expect(await instaConnectors.connectors(bre.network.config.ConnectCompound))
expect(await instaConnectors.connectors(hre.network.config.ConnectCompound))
.to.be.true;
});
@ -137,14 +137,14 @@ describe("DSA setup with Gelato Tests", function () {
);
// Encode Payloads for ConnectBasic.withdraw
const withdrawData = await bre.run("abi-encode-withselector", {
const withdrawData = await hre.run("abi-encode-withselector", {
abi: ConnectBasic.abi,
functionname: "withdraw",
inputs: [ETH, ethers.utils.parseEther("1"), userAddress, 0, 0],
});
await expect(
dsa.cast([bre.network.config.ConnectBasic], [withdrawData], userAddress, {
dsa.cast([hre.network.config.ConnectBasic], [withdrawData], userAddress, {
gasLimit,
gasPrice,
})
@ -162,14 +162,14 @@ describe("DSA setup with Gelato Tests", function () {
expect(await dsa.isAuth(gelatoCore.address)).to.be.false;
// Encode Payloads for ConnectAuth.addModule
const addAuthData = await bre.run("abi-encode-withselector", {
const addAuthData = await hre.run("abi-encode-withselector", {
abi: ConnectAuth.abi,
functionname: "add",
inputs: [gelatoCore.address],
});
await expect(
dsa.cast([bre.network.config.ConnectAuth], [addAuthData], userAddress)
dsa.cast([hre.network.config.ConnectAuth], [addAuthData], userAddress)
)
.to.emit(dsa, "LogCast")
.withArgs(userAddress, userAddress, 0);
@ -179,7 +179,7 @@ describe("DSA setup with Gelato Tests", function () {
it("#5: ConnectGelato is deployed and whitelisted on mainnet", async function () {
expect(
await instaConnectors.isConnector([bre.network.config.ConnectGelato])
await instaConnectors.isConnector([hre.network.config.ConnectGelato])
).to.be.true;
});
@ -200,8 +200,8 @@ describe("DSA setup with Gelato Tests", function () {
const withdrawFromDSATask = new GelatoCoreLib.Task({
actions: [
new GelatoCoreLib.Action({
addr: bre.network.config.ConnectBasic,
data: await bre.run("abi-encode-withselector", {
addr: hre.network.config.ConnectBasic,
data: await hre.run("abi-encode-withselector", {
abi: ConnectBasic.abi,
functionname: "withdraw",
inputs: [
@ -218,13 +218,13 @@ describe("DSA setup with Gelato Tests", function () {
});
// otherWallet needs to be an authority to qualify as withdraw to address.
const addAuthData = await bre.run("abi-encode-withselector", {
const addAuthData = await hre.run("abi-encode-withselector", {
abi: ConnectAuth.abi,
functionname: "add",
inputs: [await otherWallet.getAddress()],
});
await dsa.cast(
[bre.network.config.ConnectAuth],
[hre.network.config.ConnectAuth],
[addAuthData],
userAddress
);

View File

@ -1,8 +1,8 @@
// running `npx buidler test` automatically makes use of buidler-waffle plugin
// running `npx hardhat test` automatically makes use of hardhat-waffle plugin
// => only dependency we need is "chai"
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const hre = require("hardhat");
const {ethers} = hre;
const GelatoCoreLib = require("@gelatonetwork/core");
//const { sleep } = GelatoCoreLib;
@ -25,8 +25,8 @@ const IUniswapExchange = require("../pre-compiles/IUniswapExchange.json");
describe("Move DAI lending from DSR to Compound", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
@ -52,7 +52,7 @@ describe("Move DAI lending from DSR to Compound", function () {
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
// Ganache default accounts prefilled with 100 ETH
// Hardhat default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
@ -60,19 +60,19 @@ describe("Move DAI lending from DSR to Compound", function () {
// ===== DSA SETUP ==================
const instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
hre.network.config.InstaIndex
);
const instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
hre.network.config.InstaList
);
connectMaker = await ethers.getContractAt(
ConnectMaker.abi,
bre.network.config.ConnectMaker
hre.network.config.ConnectMaker
);
connectCompound = await ethers.getContractAt(
ConnectCompound.abi,
bre.network.config.ConnectCompound
hre.network.config.ConnectCompound
);
// Deploy DSA and get and verify ID of newly deployed DSA
@ -91,17 +91,17 @@ describe("Move DAI lending from DSR to Compound", function () {
// ===== GELATO SETUP ==================
gelatoCore = await ethers.getContractAt(
GelatoCoreLib.GelatoCore.abi,
bre.network.config.GelatoCore
hre.network.config.GelatoCore
);
// Add GelatoCore as auth on DSA
const addAuthData = await bre.run("abi-encode-withselector", {
const addAuthData = await hre.run("abi-encode-withselector", {
abi: ConnectAuth.abi,
functionname: "add",
inputs: [gelatoCore.address],
});
await dsa.cast(
[bre.network.config.ConnectAuth],
[hre.network.config.ConnectAuth],
[addAuthData],
userAddress
);
@ -125,13 +125,13 @@ describe("Move DAI lending from DSR to Compound", function () {
// ===== Dapp Dependencies SETUP ==================
// This test assumes our user has 100 DAI deposited in Maker DSR
dai = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
dai = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
expect(await dai.balanceOf(userAddress)).to.be.equal(0);
// Let's get the test user 100 DAI++ from Kyber
const daiUniswapExchange = await ethers.getContractAt(
IUniswapExchange.abi,
bre.network.config.DAI_UNISWAP
hre.network.config.DAI_UNISWAP
);
await daiUniswapExchange.ethToTokenTransferInput(
1,
@ -148,14 +148,14 @@ describe("Move DAI lending from DSR to Compound", function () {
expect(await dai.balanceOf(dsa.address)).to.be.eq(DAI_100);
// Next we deposit the 100 DAI into the DSR
const depositDai = await bre.run("abi-encode-withselector", {
const depositDai = await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "depositDai",
inputs: [DAI_100, 0, 0],
});
await expect(
dsa.cast([bre.network.config.ConnectMaker], [depositDai], userAddress)
dsa.cast([hre.network.config.ConnectMaker], [depositDai], userAddress)
)
.to.emit(dsa, "LogCast")
.withArgs(userAddress, userAddress, 0);
@ -172,12 +172,12 @@ describe("Move DAI lending from DSR to Compound", function () {
data: await conditionCompareUints.getConditionData(
mockCDAI.address, // We are in DSR so we compare against CDAI => SourceA=CDAI
mockDSR.address, // SourceB=DSR
await bre.run("abi-encode-withselector", {
abi: require("../artifacts/MockCDAI.json").abi,
await hre.run("abi-encode-withselector", {
abi: (await hre.artifacts.readArtifact("MockCDAI")).abi,
functionname: "supplyRatePerSecond",
}), // CDAI data feed first (sourceAData)
await bre.run("abi-encode-withselector", {
abi: require("../artifacts/MockDSR.json").abi,
await hre.run("abi-encode-withselector", {
abi: (await hre.artifacts.readArtifact("MockDSR")).abi,
functionname: "dsr",
}), // DSR data feed second (sourceBData)
MIN_SPREAD
@ -192,7 +192,7 @@ describe("Move DAI lending from DSR to Compound", function () {
// target2 Compound deposit to fetch DAI amount.
const connectorWithdrawFromDSR = new GelatoCoreLib.Action({
addr: connectMaker.address,
data: await bre.run("abi-encode-withselector", {
data: await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "withdrawDai",
inputs: [ethers.constants.MaxUint256, 0, 1],
@ -204,7 +204,7 @@ describe("Move DAI lending from DSR to Compound", function () {
// We instantiate target2: Deposit DAI to CDAI and getId 1
const connectorDepositCompound = new GelatoCoreLib.Action({
addr: connectCompound.address,
data: await bre.run("abi-encode-withselector", {
data: await hre.run("abi-encode-withselector", {
abi: ConnectCompound.abi,
functionname: "deposit",
inputs: [dai.address, 0, 1, 0],
@ -240,7 +240,7 @@ describe("Move DAI lending from DSR to Compound", function () {
// protocol. Check out ./contracts/ProviderModuleDSA.sol to see what it does.
const gelatoSelfProvider = new GelatoCoreLib.GelatoProvider({
addr: dsa.address,
module: bre.network.config.ProviderModuleDSA,
module: hre.network.config.ProviderModuleDSA,
});
// ======= Executor Setup =========
@ -263,15 +263,15 @@ describe("Move DAI lending from DSR to Compound", function () {
GAS_PRICE_CEIL
);
await dsa.cast(
[bre.network.config.ConnectGelato], // targets
[hre.network.config.ConnectGelato], // targets
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectGelato_ABI,
functionname: "multiProvide",
inputs: [
userAddress,
[],
[bre.network.config.ProviderModuleDSA],
[hre.network.config.ProviderModuleDSA],
TASK_AUTOMATION_FUNDS,
0, // _getId
0, // _setId
@ -296,7 +296,7 @@ describe("Move DAI lending from DSR to Compound", function () {
expect(
await gelatoCore.isModuleProvided(
dsa.address,
bre.network.config.ProviderModuleDSA
hre.network.config.ProviderModuleDSA
)
).to.be.true;
@ -306,9 +306,9 @@ describe("Move DAI lending from DSR to Compound", function () {
const expiryDate = 0;
await expect(
dsa.cast(
[bre.network.config.ConnectGelato], // targets
[hre.network.config.ConnectGelato], // targets
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectGelato_ABI,
functionname: "submitTask",
inputs: [
@ -345,7 +345,7 @@ describe("Move DAI lending from DSR to Compound", function () {
// First we fetch the gelatoGasPrice as fed by ChainLink oracle. Gelato
// allows Users to specify a maximum fast gwei gas price for their Tasks
// to remain executable up until.
const gelatoGasPrice = await bre.run("fetchGelatoGasPrice");
const gelatoGasPrice = await hre.run("fetchGelatoGasPrice");
expect(gelatoGasPrice).to.be.lte(
taskRebalanceDSRToCDAIifBetter.selfProviderGasPriceCeil
);
@ -383,7 +383,7 @@ describe("Move DAI lending from DSR to Compound", function () {
// we look at changes in the CDAI balance of the DSA
const cDAI = await ethers.getContractAt(
IERC20.abi,
bre.network.config.CDAI
hre.network.config.CDAI
);
const dsaCDAIBefore = await cDAI.balanceOf(dsa.address);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const hre = require("hardhat");
const {ethers} = hre;
// #region Contracts ABI
@ -12,12 +12,16 @@ const InstaAccount = require("../pre-compiles/InstaAccount.json");
const InstaIndex = require("../pre-compiles/InstaIndex.json");
const IERC20 = require("../pre-compiles/IERC20.json");
const ORACLE_MAKER_ETH_USD = "ETH/USD-Maker-v1";
const ORACLE_MAKER_ETH_USD_ADDR = "0x729D19f657BD0614b4985Cf1D82531c67569197B";
const PRICE_ORACLE_MAKER_PAYLOAD = "0x57de26a4"; // IMakerOracle.read()
// #endregion
describe("ConditionMakerVaultIsSafe gelato condition contract unit test", function () {
describe("ConditionMakerVaultUnsafe Unit Test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
@ -30,8 +34,8 @@ describe("ConditionMakerVaultIsSafe gelato condition contract unit test", functi
let instaIndex;
let daiToken;
let conditionMakerVaultIsSafe;
let oracleAggregator;
let conditionMakerVaultUnsafe;
let priceOracleResolver;
let cdpId;
let dsa;
@ -41,46 +45,44 @@ describe("ConditionMakerVaultIsSafe gelato condition contract unit test", functi
[userWallet] = await ethers.getSigners();
userAddress = await userWallet.getAddress();
// Ganache default accounts prefilled with 100 ETH
// Hardhat default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
hre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
hre.network.config.InstaList
);
getCdps = await ethers.getContractAt(
GetCdps.abi,
bre.network.config.GetCdps
hre.network.config.GetCdps
);
dssCdpManager = await ethers.getContractAt(
DssCdpManager.abi,
bre.network.config.DssCdpManager
hre.network.config.DssCdpManager
);
daiToken = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
daiToken = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
// ========== Test Setup ============
const OracleAggregator = await ethers.getContractFactory(
"OracleAggregator"
const PriceOracleResolver = await ethers.getContractFactory(
"PriceOracleResolver"
);
oracleAggregator = await OracleAggregator.deploy();
await oracleAggregator.deployed();
priceOracleResolver = await PriceOracleResolver.deploy();
await priceOracleResolver.deployed();
const ConditionMakerVaultIsSafe = await ethers.getContractFactory(
"ConditionMakerVaultIsSafe"
const ConditionMakerVaultUnsafe = await ethers.getContractFactory(
"ConditionMakerVaultUnsafe"
);
conditionMakerVaultIsSafe = await ConditionMakerVaultIsSafe.deploy(
oracleAggregator.address
);
await conditionMakerVaultIsSafe.deployed();
conditionMakerVaultUnsafe = await ConditionMakerVaultUnsafe.deploy();
await conditionMakerVaultUnsafe.deployed();
// Create DeFi Smart Account
@ -100,13 +102,13 @@ describe("ConditionMakerVaultIsSafe gelato condition contract unit test", functi
);
// Create/Deposit/Borrow a Vault
const openVault = await bre.run("abi-encode-withselector", {
const openVault = await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-A"],
});
await dsa.cast([bre.network.config.ConnectMaker], [openVault], userAddress);
await dsa.cast([hre.network.config.ConnectMaker], [openVault], userAddress);
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpId = String(cdps.ids[0]);
@ -114,9 +116,9 @@ describe("ConditionMakerVaultIsSafe gelato condition contract unit test", functi
expect(cdps.ids[0].isZero()).to.be.false;
await dsa.cast(
[bre.network.config.ConnectMaker],
[hre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpId, ethers.utils.parseEther("10"), 0, 0],
@ -128,9 +130,9 @@ describe("ConditionMakerVaultIsSafe gelato condition contract unit test", functi
}
);
await dsa.cast(
[bre.network.config.ConnectMaker],
[hre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpId, ethers.utils.parseUnits("1000", 18), 0, 0],
@ -142,41 +144,48 @@ describe("ConditionMakerVaultIsSafe gelato condition contract unit test", functi
expect(await daiToken.balanceOf(dsa.address)).to.be.equal(
ethers.utils.parseEther("1000")
);
// Add ETH/USD Maker Medianizer in the Oracle Aggregator
// Add ETH/USD Maker Medianizer in the PriceOracleResolver
await oracleAggregator.addOracle(
"ETH/USD",
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
await priceOracleResolver.addOracle(
ORACLE_MAKER_ETH_USD,
ORACLE_MAKER_ETH_USD_ADDR,
PRICE_ORACLE_MAKER_PAYLOAD
);
});
it("#1: ok should return NotOKMakerVaultIsSafe when the ETH/USD price is above the defined limit", async function () {
let data = await conditionMakerVaultIsSafe.getConditionData(
it("#1: ok should return MakerVaultNotUnsafe when the ETH/USD price is above the defined limit", async function () {
const conditionData = await conditionMakerVaultUnsafe.getConditionData(
cdpId,
"ETH/USD",
ORACLE_MAKER_ETH_USD_ADDR,
PRICE_ORACLE_MAKER_PAYLOAD,
ethers.utils.parseUnits("30", 17)
);
expect(await conditionMakerVaultIsSafe.ok(0, data, 0)).to.be.equal(
"NotOKMakerVaultIsSafe"
expect(await conditionMakerVaultUnsafe.ok(0, conditionData, 0)).to.be.equal(
"MakerVaultNotUnsafe"
);
});
it("#2: ok should return OK when the ETH/USD price is lower than the defined limit", async function () {
let data = await conditionMakerVaultIsSafe.getConditionData(
const conditionData = await conditionMakerVaultUnsafe.getConditionData(
cdpId,
"ETH/USD",
priceOracleResolver.address,
await hre.run("abi-encode-withselector", {
abi: (await hre.artifacts.readArtifact("PriceOracleResolver")).abi,
functionname: "getMockPrice",
inputs: [userAddress],
}),
ethers.utils.parseUnits("30", 17)
);
//#region Mock Part
oracleAggregator.mock(true, ethers.utils.parseUnits("299", 18));
priceOracleResolver.setMockPrice(ethers.utils.parseUnits("299", 18));
//#endregion
expect(await conditionMakerVaultIsSafe.ok(0, data, 0)).to.be.equal(
"NotOKMakerVaultIsSafe"
expect(await conditionMakerVaultUnsafe.ok(0, conditionData, 0)).to.be.equal(
"OK"
);
});
});

View File

@ -1,6 +1,6 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const hre = require("hardhat");
const {ethers} = hre;
// #region Contracts ABI
@ -13,14 +13,13 @@ const InstaAccount = require("../pre-compiles/InstaAccount.json");
const InstaIndex = require("../pre-compiles/InstaIndex.json");
const IERC20 = require("../pre-compiles/IERC20.json");
const InstaConnector = require("../pre-compiles/InstaConnectors.json");
const ConnectGelatoProviderPaymentABI = require("../artifacts/ConnectGelatoProviderPayment.json");
// #endregion
describe("Gelato Provider Payment Connector unit test", function () {
describe("ConnectGelatoProviderPayment Unit Test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
@ -52,39 +51,39 @@ describe("Gelato Provider Payment Connector unit test", function () {
providerAddress = await providerWallet.getAddress();
instaMaster = await ethers.provider.getSigner(
bre.network.config.InstaMaster
hre.network.config.InstaMaster
);
// Ganache default accounts prefilled with 100 ETH
// Hardhat default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
hre.network.config.InstaIndex
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
hre.network.config.InstaList
);
connectBasic = await ethers.getContractAt(
ConnectBasic.abi,
bre.network.config.ConnectBasic
hre.network.config.ConnectBasic
);
instaConnectors = await ethers.getContractAt(
InstaConnector.abi,
bre.network.config.InstaConnectors
hre.network.config.InstaConnectors
);
getCdps = await ethers.getContractAt(
GetCdps.abi,
bre.network.config.GetCdps
hre.network.config.GetCdps
);
dssCdpManager = await ethers.getContractAt(
DssCdpManager.abi,
bre.network.config.DssCdpManager
hre.network.config.DssCdpManager
);
daiToken = await ethers.getContractAt(IERC20.abi, bre.network.config.DAI);
daiToken = await ethers.getContractAt(IERC20.abi, hre.network.config.DAI);
// ========== Test Setup ============
@ -99,13 +98,23 @@ describe("Gelato Provider Payment Connector unit test", function () {
);
connectGelatoProviderPayment.deployed();
await userWallet.sendTransaction({
to: hre.network.config.InstaMaster,
value: ethers.utils.parseEther("0.1"),
});
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [await instaMaster.getAddress()],
});
await instaConnectors
.connect(instaMaster)
.enable(connectGelatoProviderPayment.address);
await userWallet.sendTransaction({
to: bre.network.config.InstaMaster,
value: ethers.utils.parseEther("0.1"),
await hre.network.provider.request({
method: "hardhat_stopImpersonatingAccount",
params: [await instaMaster.getAddress()],
});
expect(
@ -130,13 +139,13 @@ describe("Gelato Provider Payment Connector unit test", function () {
);
});
it("#1: payProvider should pay to Provider 300x10^18 token dai", async function () {
let providerDAIBalanceBefore = await daiToken.balanceOf(providerAddress);
it("#1: payProvider should pay to Provider 300 Dai", async function () {
const providerDAIBalanceBefore = await daiToken.balanceOf(providerAddress);
await dsa.cast(
[bre.network.config.ConnectMaker],
[hre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "open",
inputs: ["ETH-A"],
@ -145,15 +154,15 @@ describe("Gelato Provider Payment Connector unit test", function () {
userAddress
);
let cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
const cdps = await getCdps.getCdpsAsc(dssCdpManager.address, dsa.address);
cdpId = String(cdps.ids[0]);
expect(cdps.ids[0].isZero()).to.be.false;
await dsa.cast(
[bre.network.config.ConnectMaker],
[hre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "deposit",
inputs: [cdpId, ethers.utils.parseEther("10"), 0, 0],
@ -165,9 +174,9 @@ describe("Gelato Provider Payment Connector unit test", function () {
}
);
await dsa.cast(
[bre.network.config.ConnectMaker],
[hre.network.config.ConnectMaker],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectMaker.abi,
functionname: "borrow",
inputs: [cdpId, ethers.utils.parseUnits("1000", 18), 0, 0],
@ -183,8 +192,10 @@ describe("Gelato Provider Payment Connector unit test", function () {
await dsa.cast(
[connectGelatoProviderPayment.address],
[
await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
await hre.run("abi-encode-withselector", {
abi: (
await hre.artifacts.readArtifact("ConnectGelatoProviderPayment")
).abi,
functionname: "payProvider",
inputs: [
providerAddress,
@ -204,12 +215,12 @@ describe("Gelato Provider Payment Connector unit test", function () {
});
it("#2: payProvider should pay to Provider 1 ether", async function () {
let providerBalanceBefore = await providerWallet.getBalance();
const providerBalanceBefore = await providerWallet.getBalance();
await dsa.cast(
[connectBasic.address, connectGelatoProviderPayment.address],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectBasic.abi,
functionname: "deposit",
inputs: [
@ -219,8 +230,10 @@ describe("Gelato Provider Payment Connector unit test", function () {
"105",
],
}),
await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
await hre.run("abi-encode-withselector", {
abi: (
await hre.artifacts.readArtifact("ConnectGelatoProviderPayment")
).abi,
functionname: "payProvider",
inputs: [
providerAddress,
@ -243,11 +256,11 @@ describe("Gelato Provider Payment Connector unit test", function () {
});
it("#3: payProvider should return error message ConnectGelatoProviderPayment.payProvider:INVALIDADDESS when provider is Zero Address", async function () {
expect(
await expect(
dsa.cast(
[connectBasic.address, connectGelatoProviderPayment.address],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectBasic.abi,
functionname: "deposit",
inputs: [
@ -257,8 +270,10 @@ describe("Gelato Provider Payment Connector unit test", function () {
"105",
],
}),
await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
await hre.run("abi-encode-withselector", {
abi: (
await hre.artifacts.readArtifact("ConnectGelatoProviderPayment")
).abi,
functionname: "payProvider",
inputs: [
ethers.constants.AddressZero,
@ -274,8 +289,6 @@ describe("Gelato Provider Payment Connector unit test", function () {
value: ethers.utils.parseEther("1"),
}
)
).to.be.revertedWith(
"ConnectGelatoProviderPayment.payProvider:INVALIDADDESS."
);
).to.be.revertedWith("ConnectGelatoProviderPayment.payProvider:addr0");
});
});

View File

@ -1,51 +0,0 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
describe("Oracle Aggregator unit test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
process.exit(1);
}
let oracleAggregator;
before(async function () {
const OracleAggregator = await ethers.getContractFactory(
"OracleAggregator"
);
oracleAggregator = await OracleAggregator.deploy();
oracleAggregator.deployed();
});
it("#1: addOracle should add a maker medianizer for a currencyPair", async function () {
await oracleAggregator.addOracle(
"ETH/USD",
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
);
expect(await oracleAggregator.makerOracle("ETH/USD")).to.be.equal(
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
);
});
it("#2: addOracle should revert when adding a maker medianizer and for this currency pair it was been already added", async function () {
expect(
oracleAggregator.addOracle(
"ETH/USD",
"0x729D19f657BD0614b4985Cf1D82531c67569197B"
)
).to.be.revertedWith("OracleAggregator.Maker: Oracle already set.");
});
it("#3: getMakerTokenPrice should return ETH/USD prize", async function () {
expect((await oracleAggregator.getMakerTokenPrice("ETH/USD")).isZero()).to
.be.false;
});
it("#4: getMakerTokenPrice should return OracleAggregator.getMakerTokenPrice: CurrencyPairNotSupported. when currencyPair are not supported / not been added", async function () {
expect(oracleAggregator.getMakerTokenPrice("ETH/DAI")).to.be.revertedWith(
"OracleAggregator.getMakerTokenPrice: CurrencyPairNotSupported."
);
});
});

View File

@ -0,0 +1,60 @@
const {expect} = require("chai");
const hre = require("hardhat");
const {ethers} = hre;
const ORACLE_MAKER_ETH_USD = "ETH/USD-Maker-v1";
const ORACLE_MAKER_ETH_USD_ADDR = "0x729D19f657BD0614b4985Cf1D82531c67569197B";
const PRICE_ORACLE_MAKER_PAYLOAD = "0x57de26a4"; // IMakerOracle.read()
describe("PriceOracleResolver Unit Test", function () {
this.timeout(0);
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
let priceOracleResolver;
before(async function () {
const PriceOracleResolver = await ethers.getContractFactory(
"PriceOracleResolver"
);
priceOracleResolver = await PriceOracleResolver.deploy();
priceOracleResolver.deployed();
});
it("#1: addOracle should add a maker medianizer for a currencyPair", async function () {
await priceOracleResolver.addOracle(
ORACLE_MAKER_ETH_USD,
ORACLE_MAKER_ETH_USD_ADDR,
PRICE_ORACLE_MAKER_PAYLOAD
);
expect(await priceOracleResolver.oracle(ORACLE_MAKER_ETH_USD)).to.be.equal(
ORACLE_MAKER_ETH_USD_ADDR
);
expect(
await priceOracleResolver.oraclePayload(ORACLE_MAKER_ETH_USD)
).to.be.equal(PRICE_ORACLE_MAKER_PAYLOAD);
});
it("#2: addOracle should revert when adding a maker medianizer and for this currency pair it was been already added", async function () {
await expect(
priceOracleResolver.addOracle(
ORACLE_MAKER_ETH_USD,
ethers.constants.AddressZero,
PRICE_ORACLE_MAKER_PAYLOAD
)
).to.be.revertedWith("PriceOracleResolver.addOracle: set");
});
it("#3: getPrice returns price", async function () {
expect((await priceOracleResolver.getPrice(ORACLE_MAKER_ETH_USD)).isZero())
.to.be.false;
});
it("#4: getMakerTokenPrice should return PriceOracleResolver.getMakerTokenPrice: CurrencyPairNotSupported. when currencyPair are not supported / not been added", async function () {
await expect(priceOracleResolver.getPrice("ETH/DAI")).to.be.revertedWith(
"PriceOracleResolver.getPrice: no oracle"
);
});
});

View File

@ -1,6 +1,6 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const hre = require("hardhat");
const {ethers} = hre;
const GelatoCoreLib = require("@gelatonetwork/core");
// #region Contracts ABI
@ -10,16 +10,15 @@ const InstaList = require("../pre-compiles/InstaList.json");
const InstaAccount = require("../pre-compiles/InstaAccount.json");
const InstaIndex = require("../pre-compiles/InstaIndex.json");
const InstaConnectors = require("../pre-compiles/InstaConnectors.json");
const ConnectGelatoProviderPaymentABI = require("../artifacts/ConnectGelatoProviderPayment.json");
const ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
// #endregion
describe("Provider Module unit test", function () {
describe("Provider Module Unit Test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
@ -45,26 +44,26 @@ describe("Provider Module unit test", function () {
[, providerWallet] = await ethers.getSigners();
providerAddress = await providerWallet.getAddress();
// Ganache default accounts prefilled with 100 ETH
// Hardhat default accounts prefilled with 100 ETH
expect(await userWallet.getBalance()).to.be.gt(
ethers.utils.parseEther("10")
);
/////////////////// Get Deployed Contract //////////////////
instaIndex = await ethers.getContractAt(
InstaIndex.abi,
bre.network.config.InstaIndex
hre.network.config.InstaIndex
);
instaConnectors = await ethers.getContractAt(
InstaConnectors.abi,
bre.network.config.InstaConnectors
hre.network.config.InstaConnectors
);
gelatoCore = await ethers.getContractAt(
GelatoCoreLib.GelatoCore.abi,
bre.network.config.GelatoCore
hre.network.config.GelatoCore
);
instaList = await ethers.getContractAt(
InstaList.abi,
bre.network.config.InstaList
hre.network.config.InstaList
);
////////////////// Deploy Needed Contracts ////////////////////////
@ -83,7 +82,6 @@ describe("Provider Module unit test", function () {
"ProviderModuleDSA"
);
providerModuleDSA = await ProviderModuleDSA.deploy(
instaIndex.address,
gelatoCore.address,
connectGelatoProviderPayment.address
);
@ -128,11 +126,21 @@ describe("Provider Module unit test", function () {
// });
it("#1: isProvided should return OK", async function () {
expect(await dsa.isAuth(gelatoCore.address)).to.be.false;
expect(
await providerModuleDSA.isProvided(
dsa.address,
ethers.constants.AddressZero,
[[], [], 0, 0]
)
).to.not.be.equal("OK");
// Give authorization to Gelato on user DSA
await dsa.cast(
[bre.network.config.ConnectAuth],
[hre.network.config.ConnectAuth],
[
await bre.run("abi-encode-withselector", {
await hre.run("abi-encode-withselector", {
abi: ConnectAuth.abi,
functionname: "add",
inputs: [gelatoCore.address],
@ -154,10 +162,11 @@ describe("Provider Module unit test", function () {
it("#2: execPayload should return payload with the right provider", async function () {
// Task creation for sending to execPayload
let payProvider = new GelatoCoreLib.Action({
const payProvider = new GelatoCoreLib.Action({
addr: connectGelatoProviderPayment.address,
data: await bre.run("abi-encode-withselector", {
abi: ConnectGelatoProviderPaymentABI.abi,
data: await hre.run("abi-encode-withselector", {
abi: (await hre.artifacts.readArtifact("ConnectGelatoProviderPayment"))
.abi,
functionname: "payProvider",
inputs: [ethers.constants.AddressZero, ETH, 0, "105", 0],
}),
@ -169,7 +178,7 @@ describe("Provider Module unit test", function () {
actions: [payProvider],
});
let result = await providerModuleDSA.execPayload(
const result = await providerModuleDSA.execPayload(
0,
ethers.constants.AddressZero,
providerAddress,
@ -178,14 +187,14 @@ describe("Provider Module unit test", function () {
);
//#region retrieving the replaced provider address from the payload
let abi = new ethers.utils.AbiCoder();
const abi = new ethers.utils.AbiCoder();
let datas = abi.decode(
const datas = abi.decode(
["address[]", "bytes[]", "address"],
ethers.utils.hexDataSlice(result.payload, 4)
ethers.utils.hexDataSlice(result[0], 4)
)[1];
let paymentReceivingAddress = abi.decode(
const paymentReceivingAddress = abi.decode(
["address", "address", "uint256", "uint256", "uint256"],
ethers.utils.hexDataSlice(datas[0], 4)
)[0];

View File

@ -1,6 +1,6 @@
const {expect} = require("chai");
const bre = require("@nomiclabs/buidler");
const {ethers} = bre;
const hre = require("hardhat");
const {ethers} = hre;
const WAD = ethers.utils.parseUnits("1", 18);
@ -16,30 +16,29 @@ let wmul = (x, y) => {
//#endregion
describe("Gelato Debt Bridge Connector unit test", function () {
describe("Gelato Debt Bridge Connector Unit Test", function () {
this.timeout(0);
if (bre.network.name !== "ganache") {
console.error("Test Suite is meant to be run on ganache only");
if (hre.network.name !== "hardhat") {
console.error("Test Suite is meant to be run on hardhat only");
process.exit(1);
}
let connectGelatoDebtBridge;
let connectGelatoDebtBridgeFromMaker;
before(async function () {
const ConnectGelatoDebtBridge = await ethers.getContractFactory(
"ConnectGelatoDebtBridge"
const ConnectGelatoDebtBridgeFromMaker = await ethers.getContractFactory(
"ConnectGelatoDebtBridgeFromMaker"
);
connectGelatoDebtBridge = await ConnectGelatoDebtBridge.deploy(
0,
ethers.constants.AddressZero
connectGelatoDebtBridgeFromMaker = await ConnectGelatoDebtBridgeFromMaker.deploy(
0
);
connectGelatoDebtBridge.deployed();
connectGelatoDebtBridgeFromMaker.deployed();
});
it("#1: _wcollateralToWithdraw should return the amount of collateral to withdraw on protocol 1 and to put on protocol 2", async function () {
it("#1: wCalcCollateralToWithdraw should return the amount of collateral to withdraw on protocol 1 and to put on protocol 2", async function () {
// 3 times more collateral than borrowed amount in protocol 1
let wantedLiquidationRatioOnProtocol1 = ethers.utils.parseUnits("3", 18);
let minColRatioOnMaker = ethers.utils.parseUnits("3", 18);
// 1.5 times more collateral than borrowed amount in protocol 2
let wantedLiquidationRatioOnProtocol2 = ethers.utils.parseUnits("15", 17);
let minColRatioOnPositionB = ethers.utils.parseUnits("15", 17);
// The amount of collateral locked
let col = ethers.utils.parseUnits("1", 18);
// The amount of borrowed token
@ -56,18 +55,15 @@ describe("Gelato Debt Bridge Connector unit test", function () {
//#region CALCULATION REPLICATION
let expectedColToWithdraw = wmul(
wmul(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2
),
wmul(minColRatioOnMaker, minColRatioOnPositionB),
borrowedToken
); // doc ref : c_r x comp_r x d_2
expectedColToWithdraw = expectedColToWithdraw.sub(
wmul(wantedLiquidationRatioOnProtocol1, collateral)
wmul(minColRatioOnMaker, collateral)
); // doc ref : c_r x comp_r x d_2 - c_r x e_2
expectedColToWithdraw = wdiv(
expectedColToWithdraw,
wantedLiquidationRatioOnProtocol2.sub(wantedLiquidationRatioOnProtocol1)
minColRatioOnPositionB.sub(minColRatioOnMaker)
); // doc ref : (c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r)
expectedColToWithdraw = collateral.sub(expectedColToWithdraw); // doc ref : e_2 - ((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
@ -77,21 +73,21 @@ describe("Gelato Debt Bridge Connector unit test", function () {
//#endregion
expect(
await connectGelatoDebtBridge.wcollateralToWithdraw(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
await connectGelatoDebtBridgeFromMaker.wCalcCollateralToWithdraw(
minColRatioOnMaker,
minColRatioOnPositionB,
collateralPrice,
collateral,
borrowedToken,
collateralPrice
borrowedToken
)
).to.be.equal(expectedColToWithdraw);
});
it("#2: _wborrowedTokenToPayback should return the amount of borrowed token to pay back on protocol 1", async function () {
it("#2: _wCalcDebtToRepay should return the amount of borrowed token to pay back on protocol 1", async function () {
// 3 times more collateral than borrowed amount in protocol 1
let wantedLiquidationRatioOnProtocol1 = ethers.utils.parseUnits("3", 18);
let minColRatioOnMaker = ethers.utils.parseUnits("3", 18);
// 1.5 times more collateral than borrowed amount in protocol 2
let wantedLiquidationRatioOnProtocol2 = ethers.utils.parseUnits("15", 17);
let minColRatioOnPositionB = ethers.utils.parseUnits("15", 17);
// The amount of collateral locked
let col = ethers.utils.parseUnits("1", 18);
// The amount of borrowed token
@ -108,21 +104,18 @@ describe("Gelato Debt Bridge Connector unit test", function () {
//#region CALCULATION REPLICATION
let expectedBorToPayBack = wmul(
wmul(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2
),
wmul(minColRatioOnMaker, minColRatioOnPositionB),
borrowedToken
); // doc ref : c_r x comp_r x d_2
expectedBorToPayBack = expectedBorToPayBack.sub(
wmul(wantedLiquidationRatioOnProtocol1, collateral)
wmul(minColRatioOnMaker, collateral)
); // doc ref : c_r x comp_r x d_2 - c_r x e_2
expectedBorToPayBack = wdiv(
expectedBorToPayBack,
wantedLiquidationRatioOnProtocol2.sub(wantedLiquidationRatioOnProtocol1)
minColRatioOnPositionB.sub(minColRatioOnMaker)
); // doc ref : (c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r)
expectedBorToPayBack = wmul(
wdiv(ethers.utils.parseUnits("1", 18), wantedLiquidationRatioOnProtocol1),
wdiv(ethers.utils.parseUnits("1", 18), minColRatioOnMaker),
expectedBorToPayBack
); // doc ref : (1/c_r)((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
expectedBorToPayBack = borrowedToken.sub(expectedBorToPayBack); // doc ref : d_2 - (1/c_r)((c_r x comp_r x d_2 - c_r x e_2)/ (comp_r - c_r))
@ -130,9 +123,9 @@ describe("Gelato Debt Bridge Connector unit test", function () {
//#endregion
expect(
await connectGelatoDebtBridge.wborrowedTokenToPayback(
wantedLiquidationRatioOnProtocol1,
wantedLiquidationRatioOnProtocol2,
await connectGelatoDebtBridgeFromMaker.wCalcDebtToRepay(
minColRatioOnMaker,
minColRatioOnPositionB,
collateral,
borrowedToken
)

826
yarn.lock

File diff suppressed because it is too large Load Diff