From 5e81d8ce75321cb72ead67ec95602be049913489 Mon Sep 17 00:00:00 2001 From: gitpusha Date: Sat, 28 Nov 2020 15:54:22 +0100 Subject: [PATCH] fix: hardhat-gas-reporter and codechecks accuracy --- .circleci/config.yml | 6 +- .vscode/settings.json | 2 +- ...MockConnectGelatoDataFullMakerToMaker.sol} | 120 +++---- .../ConnectGelatoExecutorPayment.sol | 6 +- .../ConditionCompareUintsFromTwoSources.sol | 2 +- .../resolvers/PriceOracleResolver.sol | 2 +- contracts/vendor/Address.sol | 175 ---------- contracts/vendor/IERC20.sol | 89 ----- contracts/vendor/Ownable.sol | 82 ----- contracts/vendor/SafeERC20.sol | 132 -------- contracts/vendor/SafeMath.sol | 170 ---------- ...nnectGelatoDataFullMakerToMaker.deploy.js} | 12 +- package.json | 10 +- .../full/from_maker/0_ETHA-ETHB.mock.test.js | 316 ++++++++++++++++++ .../from_maker/1_ETHA-newETHB.mock.test.js | 21 +- .../helpers/services/exec-ETHA-ETHB.mock.js | 106 ++++++ .../services/getSpells-ETHA-ETHB.mock.js | 82 +++++ .../services/getSpells-ETHA-newETHB.mock.js | 17 +- ...ewMaker.mock.js => setupETHA-ETHB.mock.js} | 4 +- .../helpers/setupETHA-newETHB.mock.js | 66 ++++ test/helpers/services/getContracts.js | 6 +- yarn.lock | 40 +-- 22 files changed, 681 insertions(+), 785 deletions(-) rename contracts/__mocks__/connectors/{MockConnectGelatoDataFullRefinanceMaker.sol => MockConnectGelatoDataFullMakerToMaker.sol} (71%) delete mode 100644 contracts/vendor/Address.sol delete mode 100644 contracts/vendor/IERC20.sol delete mode 100644 contracts/vendor/Ownable.sol delete mode 100644 contracts/vendor/SafeERC20.sol delete mode 100644 contracts/vendor/SafeMath.sol rename deploy/__mocks__/connectors/{MockConnectGelatoDataForFullRefinance.deploy.js => MockConnectGelatoDataFullMakerToMaker.deploy.js} (86%) create mode 100644 test/gas/debt_bridge/full/from_maker/0_ETHA-ETHB.mock.test.js create mode 100644 test/gas/debt_bridge/full/from_maker/helpers/services/exec-ETHA-ETHB.mock.js create mode 100644 test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-ETHB.mock.js rename test/gas/debt_bridge/full/from_maker/helpers/{setupFullRefinanceMakerToNewMaker.mock.js => setupETHA-ETHB.mock.js} (94%) create mode 100644 test/gas/debt_bridge/full/from_maker/helpers/setupETHA-newETHB.mock.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 5461cfc..b3abc9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ jobs: # a collection of steps - ./node_modules - restore_cache: # restore hardhat compile cache name: Restore Hardhat Compilation Cache - key: solidity-files-cache- + key: solidity-files-cache-{{ checksum "./cache/solidity-files-cache.json" }} - run: # Compile name: Compile command: yarn compile @@ -37,13 +37,13 @@ jobs: # a collection of steps command: yarn lint - restore_cache: # restore the Hardhat Network Fork Cache name: Restore Hardhat Network Fork Cache - key: v4-hardhat-network-fork-cache + key: v5-hardhat-network-fork-cache - run: # Tests name: Tests using hardhat mainnet fork and gas reporter command: yarn test:gas - save_cache: # special step to save the Hardhat Network Fork cache name: Save Hardhat Network Fork Cache - key: v4-hardhat-network-fork-cache + key: v5-hardhat-network-fork-cache paths: - ./cache/hardhat-network-fork - run: # Codechecks diff --git a/.vscode/settings.json b/.vscode/settings.json index 5543383..dc21929 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.defaultFormatter": "esbenp.prettier-vscode", // Solidity - "solidity.compileUsingRemoteVersion": "0.7.4", + "solidity.compileUsingRemoteVersion": "v0.7.4+commit.3f05b770", "solidity.enableLocalNodeCompiler": false, "solidity.formatter": "prettier", "solidity.linter": "solhint", diff --git a/contracts/__mocks__/connectors/MockConnectGelatoDataFullRefinanceMaker.sol b/contracts/__mocks__/connectors/MockConnectGelatoDataFullMakerToMaker.sol similarity index 71% rename from contracts/__mocks__/connectors/MockConnectGelatoDataFullRefinanceMaker.sol rename to contracts/__mocks__/connectors/MockConnectGelatoDataFullMakerToMaker.sol index ff8e264..4795c89 100644 --- a/contracts/__mocks__/connectors/MockConnectGelatoDataFullRefinanceMaker.sol +++ b/contracts/__mocks__/connectors/MockConnectGelatoDataFullMakerToMaker.sol @@ -19,7 +19,8 @@ import { } from "../../constants/CInstaDapp.sol"; import { _getMakerVaultDebt, - _getMakerVaultCollateralBalance + _getMakerVaultCollateralBalance, + _isVaultOwner } from "../../functions/dapps/FMaker.sol"; import { _encodeFlashPayback @@ -40,17 +41,23 @@ import { } from "../../functions/InstaDapp/connectors/FConnectCompound.sol"; import {_getGelatoExecutorFees} from "../../functions/gelato/FGelato.sol"; import { + _getFlashLoanRoute, _getGasCostMakerToMaker, _getGasCostMakerToCompound, _getRealisedDebt } from "../../functions/gelato/FGelatoDebtBridge.sol"; +import { + DataFlow +} from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol"; -contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { +contract MockConnectGelatoDataFullMakerToMaker is ConnectorInterface { using GelatoBytes for bytes; + string public constant OK = "OK"; + // solhint-disable const-name-snakecase string public constant override name = - "MockConnectGelatoDataFullRefinanceMaker-v1.0"; + "MockConnectGelatoDataFullMakerToMaker-v1.0"; uint256 internal immutable _id; address internal immutable _connectGelatoExecutorPayment; @@ -69,15 +76,39 @@ contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { (_type, id) = (1, _id); // Should put specific value. } + // ====== ACTION TERMS CHECK ========== + // Overriding IGelatoAction's function (optional) + function termsOk( + uint256, // taskReceipId + address _dsa, + bytes calldata _actionData, + DataFlow, + uint256, // value + uint256 // cycleId + ) public view returns (string memory) { + (, uint256 vaultAId, , , ) = + abi.decode( + _actionData[4:], + (uint256, uint256, uint256, address, string) + ); + + if (vaultAId == 0) + return "ConnectGelatoDataFullMakerToMaker: Vault A Id is not valid"; + if (!_isVaultOwner(vaultAId, _dsa)) + return + "ConnectGelatoDataFullMakerToMaker: Vault A not owned by dsa"; + return OK; + } + /// @notice Entry Point for DSA.cast DebtBridge from e.g ETH-A to ETH-B /// @dev payable to be compatible in conjunction with DSA.cast payable target - /// @param _route we mock this behavior for gas-reporter testing + /// @param _mockRoute we mock this behavior for gas-reporter testing /// @param _vaultAId Id of the unsafe vault of the client of Vault A Collateral. /// @param _vaultBId Id of the vault B Collateral of the client. /// @param _colToken vault's col token address . /// @param _colType colType of the new vault. example : ETH-B, ETH-A. function getDataAndCastMakerToMaker( - uint256 _route, + uint256 _mockRoute, uint256 _vaultAId, uint256 _vaultBId, address _colToken, @@ -85,7 +116,7 @@ contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { ) external payable { (address[] memory targets, bytes[] memory datas) = _dataMakerToMaker( - _route, + _mockRoute, _vaultAId, _vaultBId, _colToken, @@ -95,22 +126,6 @@ contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { _cast(targets, datas); } - /// @notice Entry Point for DSA.cast DebtBridge from Maker to Compound - /// @dev payable to be compatible in conjunction with DSA.cast payable target - /// @param _route we mock this behavior for gas-reporter testing - /// @param _vaultId Id of the unsafe vault of the client. - /// @param _colToken vault's col token address . - function getDataAndCastMakerToCompound( - uint256 _route, - uint256 _vaultId, - address _colToken - ) external payable { - (address[] memory targets, bytes[] memory datas) = - _dataMakerToCompound(_route, _vaultId, _colToken); - - _cast(targets, datas); - } - function _cast(address[] memory targets, bytes[] memory datas) internal { // Instapool V2 / FlashLoan call bytes memory castData = @@ -120,20 +135,20 @@ contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { datas, msg.sender // msg.sender == GelatoCore ); + (bool success, bytes memory returndata) = address(this).delegatecall(castData); if (!success) { returndata.revertWithError( - "ConnectGelatoDataFullRefinanceMaker._cast:" + "ConnectGelatoDataFullMakerToMaker._cast:" ); } } /* solhint-disable function-max-lines */ - // @param _route we mock this behavior for gas-reporter testing function _dataMakerToMaker( - uint256 _route, + uint256 _mockRoute, uint256 _vaultAId, uint256 _vaultBId, address _colToken, @@ -142,10 +157,16 @@ contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { targets = new address[](1); targets[0] = INSTA_POOL_V2; + _vaultBId = _isVaultOwner(_vaultBId, address(this)) ? _vaultBId : 0; + uint256 wDaiToBorrow = _getRealisedDebt(_getMakerVaultDebt(_vaultAId)); uint256 wColToWithdrawFromMaker = _getMakerVaultCollateralBalance(_vaultAId); - uint256 route = _route; + uint256 route = _getFlashLoanRoute(DAI, wDaiToBorrow); + + // Mock Route + route = _mockRoute; + uint256 gasCost = _getGasCostMakerToMaker(_vaultBId == 0, route); uint256 gasFeesPaidFromCol = _getGelatoExecutorFees(gasCost); @@ -240,52 +261,5 @@ contract MockConnectGelatoDataFullRefinanceMaker is ConnectorInterface { datas[5] = _encodeFlashPayback(DAI, _wDaiToBorrow, 0, 0); } - // @param _route we mock this behavior for gas-reporter testing - function _dataMakerToCompound( - uint256 _route, - uint256 _vaultId, - address _colToken - ) internal view returns (address[] memory targets, bytes[] memory datas) { - targets = new address[](1); - targets[0] = INSTA_POOL_V2; - - uint256 wDaiToBorrow = _getRealisedDebt(_getMakerVaultDebt(_vaultId)); - uint256 wColToWithdrawFromMaker = - _getMakerVaultCollateralBalance(_vaultId); - uint256 route = _route; - uint256 gasCost = _getGasCostMakerToCompound(route); - uint256 gasFeesPaidFromCol = _getGelatoExecutorFees(gasCost); - - address[] memory _targets = new address[](6); - _targets[0] = CONNECT_MAKER; // payback - _targets[1] = CONNECT_MAKER; // withdraw - _targets[2] = CONNECT_COMPOUND; // deposit - _targets[3] = CONNECT_COMPOUND; // borrow - _targets[4] = _connectGelatoExecutorPayment; // payExecutor - _targets[5] = INSTA_POOL_V2; // flashPayback - - bytes[] memory _datas = new bytes[](6); - _datas[0] = _encodePaybackMakerVault(_vaultId, uint256(-1), 0, 600); - _datas[1] = _encodedWithdrawMakerVault(_vaultId, uint256(-1), 0, 0); - _datas[2] = _encodeDepositCompound( - _colToken, - sub(wColToWithdrawFromMaker, gasFeesPaidFromCol), - 0, - 0 - ); - _datas[3] = _encodeBorrowCompound(DAI, 0, 600, 0); - _datas[4] = _encodePayExecutor(_colToken, gasFeesPaidFromCol, 0, 0); - _datas[5] = _encodeFlashPayback(DAI, wDaiToBorrow, 0, 0); - - datas = new bytes[](1); - datas[0] = abi.encodeWithSelector( - IConnectInstaPoolV2.flashBorrowAndCast.selector, - DAI, - wDaiToBorrow, - route, - abi.encode(_targets, _datas) - ); - } - /* solhint-enable function-max-lines */ } diff --git a/contracts/contracts/connectors/ConnectGelatoExecutorPayment.sol b/contracts/contracts/connectors/ConnectGelatoExecutorPayment.sol index 02fbb27..948037d 100644 --- a/contracts/contracts/connectors/ConnectGelatoExecutorPayment.sol +++ b/contracts/contracts/connectors/ConnectGelatoExecutorPayment.sol @@ -4,9 +4,9 @@ pragma solidity 0.7.4; import { IConnectGelatoExecutorPayment } from "../../interfaces/InstaDapp/connectors/IConnectGelatoExecutorPayment.sol"; -import {Address} from "../../vendor/Address.sol"; -import {IERC20} from "../../vendor/IERC20.sol"; -import {SafeERC20} from "../../vendor/SafeERC20.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {_getUint, _setUint} from "../../functions/InstaDapp/FInstaDapp.sol"; import {ETH} from "../../constants/CInstaDapp.sol"; diff --git a/contracts/contracts/gelato/conditions/ConditionCompareUintsFromTwoSources.sol b/contracts/contracts/gelato/conditions/ConditionCompareUintsFromTwoSources.sol index 2b0b221..21fc109 100644 --- a/contracts/contracts/gelato/conditions/ConditionCompareUintsFromTwoSources.sol +++ b/contracts/contracts/gelato/conditions/ConditionCompareUintsFromTwoSources.sol @@ -4,7 +4,7 @@ pragma solidity 0.7.4; import { GelatoConditionsStandard } from "@gelatonetwork/core/contracts/conditions/GelatoConditionsStandard.sol"; -import {SafeMath} from "../../../vendor/SafeMath.sol"; +import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import { IGelatoCore } from "@gelatonetwork/core/contracts/gelato_core/interfaces/IGelatoCore.sol"; diff --git a/contracts/contracts/resolvers/PriceOracleResolver.sol b/contracts/contracts/resolvers/PriceOracleResolver.sol index 0c910db..5c4de7c 100644 --- a/contracts/contracts/resolvers/PriceOracleResolver.sol +++ b/contracts/contracts/resolvers/PriceOracleResolver.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.4; pragma experimental ABIEncoderV2; -import {Ownable} from "../../vendor/Ownable.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {GelatoBytes} from "../../lib/GelatoBytes.sol"; /// @title PriceOracleResolver diff --git a/contracts/vendor/Address.sol b/contracts/vendor/Address.sol deleted file mode 100644 index 5a43156..0000000 --- a/contracts/vendor/Address.sol +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.7.4; - -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); - } - } - } -} diff --git a/contracts/vendor/IERC20.sol b/contracts/vendor/IERC20.sol deleted file mode 100644 index b604f05..0000000 --- a/contracts/vendor/IERC20.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.7.4; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address recipient, uint256 amount) - external - returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) - external - view - returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `sender` to `recipient` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); - - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); -} diff --git a/contracts/vendor/Ownable.sol b/contracts/vendor/Ownable.sol deleted file mode 100644 index f642c63..0000000 --- a/contracts/vendor/Ownable.sol +++ /dev/null @@ -1,82 +0,0 @@ -// "SPDX-License-Identifier: MIT" -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; - } -} diff --git a/contracts/vendor/SafeERC20.sol b/contracts/vendor/SafeERC20.sol deleted file mode 100644 index 5a69674..0000000 --- a/contracts/vendor/SafeERC20.sol +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.7.4; - -import {IERC20} from "./IERC20.sol"; -import {SafeMath} from "./SafeMath.sol"; -import {Address} from "./Address.sol"; - -/** - * @title SafeERC20 - * @dev Wrappers around ERC20 operations that throw on failure (when the token - * contract returns false). Tokens that return no value (and instead revert or - * throw on failure) are also supported, non-reverting calls are assumed to be - * successful. - * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, - * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. - */ -library SafeERC20 { - using SafeMath for uint256; - using Address for address; - - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { - _callOptionalReturn( - token, - abi.encodeWithSelector(token.transfer.selector, to, value) - ); - } - - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { - _callOptionalReturn( - token, - abi.encodeWithSelector(token.transferFrom.selector, from, to, value) - ); - } - - /** - * @dev Deprecated. This function has issues similar to the ones found in - * {IERC20-approve}, and its usage is discouraged. - * - * Whenever possible, use {safeIncreaseAllowance} and - * {safeDecreaseAllowance} instead. - */ - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { - // safeApprove should only be called when setting an initial allowance, - // or when resetting it to zero. To increase and decrease it, use - // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' - // solhint-disable-next-line max-line-length - require( - (value == 0) || (token.allowance(address(this), spender) == 0), - "SafeERC20: approve from non-zero to non-zero allowance" - ); - _callOptionalReturn( - token, - abi.encodeWithSelector(token.approve.selector, spender, value) - ); - } - - function safeIncreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - uint256 newAllowance = - token.allowance(address(this), spender).add(value); - _callOptionalReturn( - token, - abi.encodeWithSelector( - token.approve.selector, - spender, - newAllowance - ) - ); - } - - function safeDecreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { - uint256 newAllowance = - token.allowance(address(this), spender).sub( - value, - "SafeERC20: decreased allowance below zero" - ); - _callOptionalReturn( - token, - abi.encodeWithSelector( - token.approve.selector, - spender, - newAllowance - ) - ); - } - - /** - * @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" - ); - } - } -} diff --git a/contracts/vendor/SafeMath.sol b/contracts/vendor/SafeMath.sol deleted file mode 100644 index adf5019..0000000 --- a/contracts/vendor/SafeMath.sol +++ /dev/null @@ -1,170 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.7.4; - -/** - * @dev Wrappers over Solidity's arithmetic operations with added overflow - * checks. - * - * Arithmetic operations in Solidity wrap on overflow. This can easily result - * in bugs, because programmers usually assume that an overflow raises an - * error, which is the standard behavior in high level programming languages. - * `SafeMath` restores this intuition by reverting the transaction when an - * operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, "SafeMath: addition overflow"); - - return c; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return sub(a, b, "SafeMath: subtraction overflow"); - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - require(b <= a, errorMessage); - uint256 c = a - b; - - return c; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, "SafeMath: multiplication overflow"); - - return c; - } - - /** - * @dev Returns the integer division of two unsigned integers. Reverts on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return div(a, b, "SafeMath: division by zero"); - } - - /** - * @dev Returns the integer division of two unsigned integers. Reverts with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - require(b > 0, errorMessage); - uint256 c = a / b; - // assert(a == b * c + a % b); // There is no case in which this doesn't hold - - return c; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * Reverts when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return mod(a, b, "SafeMath: modulo by zero"); - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * Reverts with custom message when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - require(b != 0, errorMessage); - return a % b; - } -} diff --git a/deploy/__mocks__/connectors/MockConnectGelatoDataForFullRefinance.deploy.js b/deploy/__mocks__/connectors/MockConnectGelatoDataFullMakerToMaker.deploy.js similarity index 86% rename from deploy/__mocks__/connectors/MockConnectGelatoDataForFullRefinance.deploy.js rename to deploy/__mocks__/connectors/MockConnectGelatoDataFullMakerToMaker.deploy.js index da2cd48..0d3be8f 100644 --- a/deploy/__mocks__/connectors/MockConnectGelatoDataForFullRefinance.deploy.js +++ b/deploy/__mocks__/connectors/MockConnectGelatoDataFullMakerToMaker.deploy.js @@ -6,7 +6,7 @@ const InstaConnector = require("../../../pre-compiles/InstaConnectors.json"); module.exports = async (hre) => { if (hre.network.name === "mainnet") { console.log( - "Deploying MockConnectGelatoDataFullRefinanceMaker to mainnet. Hit ctrl + c to abort" + "Deploying MockConnectGelatoDataFullMakerToMaker to mainnet. Hit ctrl + c to abort" ); console.log("❗ CONNECTOR DEPLOYMENT: VERIFY & HARDCODE CONNECTOR ID"); await sleep(6000); @@ -39,7 +39,7 @@ module.exports = async (hre) => { const connectorLength = await instaConnectors.connectorLength(); const connectorId = connectorLength.add(1); - await deploy("MockConnectGelatoDataFullRefinanceMaker", { + await deploy("MockConnectGelatoDataFullMakerToMaker", { from: deployer, args: [ connectorId, @@ -50,7 +50,7 @@ module.exports = async (hre) => { await instaConnectors .connect(instaMaster) .enable( - (await ethers.getContract("MockConnectGelatoDataFullRefinanceMaker")) + (await ethers.getContract("MockConnectGelatoDataFullMakerToMaker")) .address ); @@ -59,9 +59,9 @@ module.exports = async (hre) => { params: [await instaMaster.getAddress()], }); } else { - // the following will only deploy "MockConnectGelatoDataFullRefinanceMaker" + // the following will only deploy "MockConnectGelatoDataFullMakerToMaker" // if the contract was never deployed or if the code changed since last deployment - await deploy("MockConnectGelatoDataFullRefinanceMaker", { + await deploy("MockConnectGelatoDataFullMakerToMaker", { from: deployer, args: [ parseInt(process.env.ConnectGelatoDataFullRefinanceMakerId), @@ -80,4 +80,4 @@ module.exports.skip = async (hre) => { return false; }; module.exports.dependencies = ["ConnectGelatoExecutorPayment"]; -module.exports.tags = ["MockConnectGelatoDataFullRefinanceMaker"]; +module.exports.tags = ["MockConnectGelatoDataFullMakerToMaker"]; diff --git a/package.json b/package.json index 4ccbe4f..d907de1 100644 --- a/package.json +++ b/package.json @@ -20,22 +20,22 @@ "devDependencies": { "@codechecks/client": "0.1.10", "@gelatonetwork/core": "1.4.1", - "@nomiclabs/hardhat-ethers": "2.0.0", + "@nomiclabs/hardhat-ethers": "2.0.1", "@nomiclabs/hardhat-waffle": "2.0.0", - "@openzeppelin/contracts": "3.2.0", + "@openzeppelin/contracts": "3.3.0", "chai": "4.2.0", "dotenv": "8.2.0", "eslint": "7.14.0", "eslint-config-prettier": "6.15.0", "ethereum-waffle": "3.2.1", "ethers": "5.0.23", - "hardhat": "2.0.3", - "hardhat-deploy": "0.7.0-beta.30", + "hardhat": "2.0.4", + "hardhat-deploy": "0.7.0-beta.32", "hardhat-deploy-ethers": "0.3.0-beta.6", "hardhat-gas-reporter": "1.0.1", "husky": ">=4", "lint-staged": "10.5.2", - "prettier": "2.2.0", + "prettier": "2.2.1", "prettier-plugin-solidity": "1.0.0-beta.1", "solhint": "3.3.2", "solhint-plugin-prettier": "0.0.5" diff --git a/test/gas/debt_bridge/full/from_maker/0_ETHA-ETHB.mock.test.js b/test/gas/debt_bridge/full/from_maker/0_ETHA-ETHB.mock.test.js new file mode 100644 index 0000000..074de12 --- /dev/null +++ b/test/gas/debt_bridge/full/from_maker/0_ETHA-ETHB.mock.test.js @@ -0,0 +1,316 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { deployments } = hre; +const GelatoCoreLib = require("@gelatonetwork/core"); + +const mockSetupETHAETHB = require("./helpers/setupETHA-ETHB.mock"); +const mockExecETHAETHB = require("./helpers/services/exec-ETHA-ETHB.mock"); + +describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", function () { + this.timeout(0); + if (hre.network.name !== "hardhat") { + console.error("Test Suite is meant to be run on hardhat only"); + process.exit(1); + } + + let contracts; + let wallets; + let constants; + let ABI; + + // Payload Params for ConnectGelatoFullDebtBridgeFromMaker and ConditionMakerVaultUnsafe + let vaultAId; + let vaultBId; + + let conditionMakerVaultUnsafeObj; + let conditionDebtBridgeIsAffordableObj; + let conditionDestVaultWillBeSafe; + + // For TaskSpec and for Task + let gelatoDebtBridgeSpells = []; + let refinanceFromEthAToBIfVaultUnsafe; + let gelatoExternalProvider; + const expiryDate = 0; + + // Cross test var + let taskReceipt; + + let mockRoute = 0; + + let gelatoGasPrice; + + before(async function () { + gelatoGasPrice = await hre.run("fetchGelatoGasPrice"); + }); + + beforeEach(async function () { + // Reset back to a fresh forked state during runtime + await deployments.fixture(); + + const result = await mockSetupETHAETHB(mockRoute); + + wallets = result.wallets; + contracts = result.contracts; + vaultAId = result.vaultAId; + vaultBId = result.vaultBId; + gelatoDebtBridgeSpells = result.spells; + + ABI = result.ABI; + constants = result.constants; + + expect(gelatoGasPrice).to.be.lte(constants.GAS_PRICE_CEIL); + + conditionMakerVaultUnsafeObj = new GelatoCoreLib.Condition({ + inst: contracts.conditionMakerVaultUnsafe.address, + data: await contracts.conditionMakerVaultUnsafe.getConditionData( + vaultAId, + contracts.priceOracleResolver.address, + await hre.run("abi-encode-withselector", { + abi: (await deployments.getArtifact("PriceOracleResolver")).abi, + functionname: "getMockPrice", + inputs: [wallets.userAddress], + }), + constants.MIN_COL_RATIO_MAKER + ), + }); + + conditionDebtBridgeIsAffordableObj = new GelatoCoreLib.Condition({ + inst: contracts.conditionDebtBridgeIsAffordable.address, + data: await contracts.conditionDebtBridgeIsAffordable.getConditionData( + vaultAId, + constants.MAX_FEES_IN_PERCENT + ), + }); + + conditionDestVaultWillBeSafe = new GelatoCoreLib.Condition({ + inst: contracts.conditionDestVaultWillBeSafe.address, + data: await contracts.conditionDestVaultWillBeSafe.getConditionData( + contracts.dsa.address, + vaultAId, + vaultBId, + "ETH-B" + ), + }); + + refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({ + conditions: [ + conditionMakerVaultUnsafeObj, + conditionDebtBridgeIsAffordableObj, + conditionDestVaultWillBeSafe, + ], + actions: gelatoDebtBridgeSpells, + }); + + gelatoExternalProvider = new GelatoCoreLib.GelatoProvider({ + addr: wallets.gelatoProviderAddress, // Gelato Provider Address + module: contracts.dsaProviderModule.address, // Gelato DSA module + }); + + await contracts.dsa.cast( + [hre.network.config.ConnectAuth], + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectAuthABI, + functionname: "add", + inputs: [contracts.gelatoCore.address], + }), + ], + wallets.userAddress + ); + + expect(await contracts.dsa.isAuth(contracts.gelatoCore.address)).to.be.true; + }); + + // Increment mockRoute in between tests + afterEach(function () { + mockRoute = mockRoute === 4 ? 0 : mockRoute + 1; + }); + + it("#1: execViaRoute0", async function () { + //#region User submit a Debt Refinancing task if market move against him + + expect(mockRoute, "mockRoute mismatch").to.be.equal(0); + + // ======= GELATO TASK SETUP ====== + await expect( + contracts.dsa.cast( + [contracts.connectGelato.address], // targets + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectGelatoABI, + functionname: "submitTask", + inputs: [ + gelatoExternalProvider, + refinanceFromEthAToBIfVaultUnsafe, + expiryDate, + ], + }), + ], // datas + wallets.userAddress, // origin + { + gasLimit: 5000000, + } + ) + ).to.emit(contracts.gelatoCore, "LogTaskSubmitted"); + + taskReceipt = new GelatoCoreLib.TaskReceipt({ + id: await contracts.gelatoCore.currentTaskReceiptId(), + userProxy: contracts.dsa.address, + provider: gelatoExternalProvider, + tasks: [refinanceFromEthAToBIfVaultUnsafe], + expiryDate, + }); + + await mockExecETHAETHB( + constants, + contracts, + wallets, + mockRoute, + taskReceipt, + gelatoGasPrice + ); + + //#endregion + }); + + it("#2: execViaRoute1", async function () { + //#region User submit a Debt Refinancing task if market move against him + + expect(mockRoute, "mockRoute mismatch").to.be.equal(1); + + // ======= GELATO TASK SETUP ====== + await expect( + contracts.dsa.cast( + [contracts.connectGelato.address], // targets + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectGelatoABI, + functionname: "submitTask", + inputs: [ + gelatoExternalProvider, + refinanceFromEthAToBIfVaultUnsafe, + expiryDate, + ], + }), + ], // datas + wallets.userAddress, // origin + { + gasLimit: 5000000, + } + ) + ).to.emit(contracts.gelatoCore, "LogTaskSubmitted"); + + taskReceipt = new GelatoCoreLib.TaskReceipt({ + id: await contracts.gelatoCore.currentTaskReceiptId(), + userProxy: contracts.dsa.address, + provider: gelatoExternalProvider, + tasks: [refinanceFromEthAToBIfVaultUnsafe], + expiryDate, + }); + + await mockExecETHAETHB( + constants, + contracts, + wallets, + mockRoute, + taskReceipt, + gelatoGasPrice + ); + + //#endregion + }); + + it("#3: execViaRoute2", async function () { + //#region User submit a Debt Refinancing task if market move against him + + expect(mockRoute, "mockRoute mismatch").to.be.equal(2); + + // ======= GELATO TASK SETUP ====== + await expect( + contracts.dsa.cast( + [contracts.connectGelato.address], // targets + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectGelatoABI, + functionname: "submitTask", + inputs: [ + gelatoExternalProvider, + refinanceFromEthAToBIfVaultUnsafe, + expiryDate, + ], + }), + ], // datas + wallets.userAddress, // origin + { + gasLimit: 5000000, + } + ) + ).to.emit(contracts.gelatoCore, "LogTaskSubmitted"); + + taskReceipt = new GelatoCoreLib.TaskReceipt({ + id: await contracts.gelatoCore.currentTaskReceiptId(), + userProxy: contracts.dsa.address, + provider: gelatoExternalProvider, + tasks: [refinanceFromEthAToBIfVaultUnsafe], + expiryDate, + }); + + await mockExecETHAETHB( + constants, + contracts, + wallets, + mockRoute, + taskReceipt, + gelatoGasPrice + ); + + //#endregion + }); + + it("#4: execViaRoute3", async function () { + //#region User submit a Debt Refinancing task if market move against him + + expect(mockRoute, "mockRoute mismatch").to.be.equal(3); + + // ======= GELATO TASK SETUP ====== + await expect( + contracts.dsa.cast( + [contracts.connectGelato.address], // targets + [ + await hre.run("abi-encode-withselector", { + abi: ABI.ConnectGelatoABI, + functionname: "submitTask", + inputs: [ + gelatoExternalProvider, + refinanceFromEthAToBIfVaultUnsafe, + expiryDate, + ], + }), + ], // datas + wallets.userAddress, // origin + { + gasLimit: 5000000, + } + ) + ).to.emit(contracts.gelatoCore, "LogTaskSubmitted"); + + taskReceipt = new GelatoCoreLib.TaskReceipt({ + id: await contracts.gelatoCore.currentTaskReceiptId(), + userProxy: contracts.dsa.address, + provider: gelatoExternalProvider, + tasks: [refinanceFromEthAToBIfVaultUnsafe], + expiryDate, + }); + + await mockExecETHAETHB( + constants, + contracts, + wallets, + mockRoute, + taskReceipt, + gelatoGasPrice + ); + + //#endregion + }); +}); diff --git a/test/gas/debt_bridge/full/from_maker/1_ETHA-newETHB.mock.test.js b/test/gas/debt_bridge/full/from_maker/1_ETHA-newETHB.mock.test.js index 75ec24d..e7e5787 100644 --- a/test/gas/debt_bridge/full/from_maker/1_ETHA-newETHB.mock.test.js +++ b/test/gas/debt_bridge/full/from_maker/1_ETHA-newETHB.mock.test.js @@ -3,10 +3,10 @@ const hre = require("hardhat"); const { deployments } = hre; const GelatoCoreLib = require("@gelatonetwork/core"); -const mockSetupFullRefinanceMakerToNewMaker = require("./helpers/setupFullRefinanceMakerToNewMaker.mock"); -const mockExec_ETHA_newETHB = require("./helpers/services/exec-ETHA-newETHB.mock"); +const setupETHAToNewETHB = require("./helpers/setupETHA-newETHB.mock"); +const mockExecETHAnewETHB = require("./helpers/services/exec-ETHA-newETHB.mock"); -describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", function () { +describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to NEW ETH-B", function () { this.timeout(0); if (hre.network.name !== "hardhat") { console.error("Test Suite is meant to be run on hardhat only"); @@ -20,7 +20,6 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio // Payload Params for ConnectGelatoFullDebtBridgeFromMaker and ConditionMakerVaultUnsafe let vaultAId; - let vaultBId; let conditionMakerVaultUnsafeObj; let conditionDebtBridgeIsAffordableObj; @@ -47,12 +46,11 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio // Reset back to a fresh forked state during runtime await deployments.fixture(); - const result = await mockSetupFullRefinanceMakerToNewMaker(mockRoute); + const result = await setupETHAToNewETHB(mockRoute); wallets = result.wallets; contracts = result.contracts; vaultAId = result.vaultAId; - vaultBId = result.vaultBId; gelatoDebtBridgeSpells = result.spells; ABI = result.ABI; @@ -87,11 +85,12 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio data: await contracts.conditionDestVaultWillBeSafe.getConditionData( contracts.dsa.address, vaultAId, - vaultBId, + 0, "ETH-B" ), }); + // ======= GELATO TASK SETUP ====== refinanceFromEthAToBIfVaultUnsafe = new GelatoCoreLib.Task({ conditions: [ conditionMakerVaultUnsafeObj, @@ -161,7 +160,7 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio expiryDate, }); - await mockExec_ETHA_newETHB( + await mockExecETHAnewETHB( constants, contracts, wallets, @@ -208,7 +207,7 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio expiryDate, }); - await mockExec_ETHA_newETHB( + await mockExecETHAnewETHB( constants, contracts, wallets, @@ -255,7 +254,7 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio expiryDate, }); - await mockExec_ETHA_newETHB( + await mockExecETHAnewETHB( constants, contracts, wallets, @@ -302,7 +301,7 @@ describe("Gas Measurements: Full Debt Bridge From Maker ETH-A to ETH-B", functio expiryDate, }); - await mockExec_ETHA_newETHB( + await mockExecETHAnewETHB( constants, contracts, wallets, diff --git a/test/gas/debt_bridge/full/from_maker/helpers/services/exec-ETHA-ETHB.mock.js b/test/gas/debt_bridge/full/from_maker/helpers/services/exec-ETHA-ETHB.mock.js new file mode 100644 index 0000000..a7c3e9b --- /dev/null +++ b/test/gas/debt_bridge/full/from_maker/helpers/services/exec-ETHA-ETHB.mock.js @@ -0,0 +1,106 @@ +// This test showcases the part which is automatically done by the Gelato Executor Network on mainnet +// Bots constatly check whether the submitted task is executable (by calling canExec) +// If the task becomes executable (returns "OK"), the "exec" function will be called +// which will execute the debt refinancing on behalf of the user + +const hre = require("hardhat"); +const { ethers } = hre; +const { expect } = require("chai"); + +module.exports = async function ( + constants, + contracts, + wallets, + mockRoute, + taskReceipt, + gelatoGasPrice +) { + // Steps + // Step 1: Market Move against the user (Mock) + // Step 2: Executor execute the user's task + + //#region Step 1 Market Move against the user (Mock) + + // Ether market price went from the current price to 250$ + + // TO DO: base mock price off of real price data + await contracts.priceOracleResolver.setMockPrice( + ethers.utils.parseUnits("400", 18) + ); + + expect( + await contracts.mockDebtBridgeETHBExecutor + .connect(wallets.executor) + .canExec(taskReceipt, constants.GAS_LIMIT, gelatoGasPrice) + ).to.be.equal("ConditionNotOk:MakerVaultNotUnsafe"); + + // TO DO: base mock price off of real price data + await contracts.priceOracleResolver.setMockPrice( + ethers.utils.parseUnits("250", 18) + ); + + expect( + await contracts.mockDebtBridgeETHBExecutor + .connect(wallets.executor) + .canExec(taskReceipt, constants.GAS_LIMIT, gelatoGasPrice) + ).to.be.equal("OK"); + + //#endregion + + //#region Step 2 Executor execute the user's task + + if (mockRoute === 0) { + await expect( + contracts.mockDebtBridgeETHBExecutor + .connect(wallets.executor) + .execViaRoute0(taskReceipt, { + gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei) + gasLimit: constants.GAS_LIMIT, + }) + ).to.emit(contracts.gelatoCore, "LogExecSuccess"); + } else if (mockRoute === 1) { + await expect( + contracts.mockDebtBridgeETHBExecutor + .connect(wallets.executor) + .execViaRoute1(taskReceipt, { + gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei) + gasLimit: constants.GAS_LIMIT, + }) + ).to.emit(contracts.gelatoCore, "LogExecSuccess"); + } else if (mockRoute === 2) { + await expect( + contracts.mockDebtBridgeETHBExecutor + .connect(wallets.executor) + .execViaRoute2(taskReceipt, { + gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei) + gasLimit: constants.GAS_LIMIT, + }) + ).to.emit(contracts.gelatoCore, "LogExecSuccess"); + } else if (mockRoute === 3) { + await expect( + contracts.mockDebtBridgeETHBExecutor + .connect(wallets.executor) + .execViaRoute3(taskReceipt, { + gasPrice: gelatoGasPrice, // Exectutor must use gelatoGasPrice (Chainlink fast gwei) + gasLimit: constants.GAS_LIMIT, + }) + ).to.emit(contracts.gelatoCore, "LogExecSuccess"); + } else { + throw new Error("Invalid mockRoute"); + } + + // 🚧 For Debugging: + // const txResponse2 = await contracts.gelatoCore + // .connect(wallets.executor) + // .exec(taskReceipt, { + // gasPrice: gelatoGasPrice, + // gasLimit: constants.GAS_LIMIT, + // }); + // const {blockHash} = await txResponse2.wait(); + // const logs = await ethers.provider.getLogs({blockHash}); + // const iFace = new ethers.utils.Interface(GelatoCoreLib.GelatoCore.abi); + // for (const log of logs) { + // console.log(iFace.parseLog(log).args.reason); + // } + // await GelatoCoreLib.sleep(10000); +}; diff --git a/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-ETHB.mock.js b/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-ETHB.mock.js new file mode 100644 index 0000000..aef68fc --- /dev/null +++ b/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-ETHB.mock.js @@ -0,0 +1,82 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { deployments, ethers } = hre; +const GelatoCoreLib = require("@gelatonetwork/core"); + +// Instadapp UI should do the same implementation for submitting debt bridge task +module.exports = async function ( + wallets, + contracts, + constants, + mockRoute, + vaultAId, + vaultBId +) { + //#region Step 9 Provider should whitelist task + + // By WhiteList task, the provider can constrain the type + // of task the user can submitting. + + //#region Actions + + const spells = []; + + const debtBridgeCalculationForFullRefinance = new GelatoCoreLib.Action({ + addr: contracts.mockConnectGelatoDataFullMakerToMaker.address, + data: await hre.run("abi-encode-withselector", { + abi: ( + await deployments.getArtifact("MockConnectGelatoDataFullMakerToMaker") + ).abi, + functionname: "getDataAndCastMakerToMaker", + inputs: [mockRoute, vaultAId, vaultBId, constants.ETH, "ETH-B"], + }), + operation: GelatoCoreLib.Operation.Delegatecall, + termsOkCheck: true, + }); + + spells.push(debtBridgeCalculationForFullRefinance); + + const gasPriceCeil = ethers.constants.MaxUint256; + + const mockConnectGelatoDataFullMakerToMakerTaskSpec = new GelatoCoreLib.TaskSpec( + { + conditions: [ + contracts.conditionMakerVaultUnsafe.address, + contracts.conditionDebtBridgeIsAffordable.address, + contracts.conditionDestVaultWillBeSafe.address, + ], + actions: spells, + gasPriceCeil, + } + ); + + await expect( + contracts.gelatoCore + .connect(wallets.gelatoProvider) + .provideTaskSpecs([mockConnectGelatoDataFullMakerToMakerTaskSpec]) + ).to.emit(contracts.gelatoCore, "LogTaskSpecProvided"); + + expect( + await contracts.gelatoCore + .connect(wallets.gelatoProvider) + .isTaskSpecProvided( + wallets.gelatoProviderAddress, + mockConnectGelatoDataFullMakerToMakerTaskSpec + ) + ).to.be.equal("OK"); + + expect( + await contracts.gelatoCore + .connect(wallets.gelatoProvider) + .taskSpecGasPriceCeil( + wallets.gelatoProviderAddress, + await contracts.gelatoCore + .connect(wallets.gelatoProvider) + .hashTaskSpec(mockConnectGelatoDataFullMakerToMakerTaskSpec) + ) + ).to.be.equal(gasPriceCeil); + + //#endregion + + return spells; +}; diff --git a/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-newETHB.mock.js b/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-newETHB.mock.js index a677ee2..55a4e68 100644 --- a/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-newETHB.mock.js +++ b/test/gas/debt_bridge/full/from_maker/helpers/services/getSpells-ETHA-newETHB.mock.js @@ -9,7 +9,7 @@ module.exports = async function ( contracts, constants, mockRoute, - vaultId + vaultAId ) { //#region Step 9 Provider should whitelist task @@ -21,22 +21,23 @@ module.exports = async function ( const spells = []; const debtBridgeCalculationForFullRefinance = new GelatoCoreLib.Action({ - addr: contracts.mockConnectGelatoDataFullRefinanceMaker.address, + addr: contracts.mockConnectGelatoDataFullMakerToMaker.address, data: await hre.run("abi-encode-withselector", { abi: ( - await deployments.getArtifact("MockConnectGelatoDataFullRefinanceMaker") + await deployments.getArtifact("MockConnectGelatoDataFullMakerToMaker") ).abi, functionname: "getDataAndCastMakerToMaker", - inputs: [mockRoute, vaultId, 0, constants.ETH, "ETH-B"], + inputs: [mockRoute, vaultAId, 0, constants.ETH, "ETH-B"], }), operation: GelatoCoreLib.Operation.Delegatecall, + termsOkCheck: true, }); spells.push(debtBridgeCalculationForFullRefinance); const gasPriceCeil = ethers.constants.MaxUint256; - const connectGelatoFullDebtBridgeFromMakerTaskSpec = new GelatoCoreLib.TaskSpec( + const mockConnectGelatoDataFullMakerToMakerTaskSpec = new GelatoCoreLib.TaskSpec( { conditions: [ contracts.conditionMakerVaultUnsafe.address, @@ -51,7 +52,7 @@ module.exports = async function ( await expect( contracts.gelatoCore .connect(wallets.gelatoProvider) - .provideTaskSpecs([connectGelatoFullDebtBridgeFromMakerTaskSpec]) + .provideTaskSpecs([mockConnectGelatoDataFullMakerToMakerTaskSpec]) ).to.emit(contracts.gelatoCore, "LogTaskSpecProvided"); expect( @@ -59,7 +60,7 @@ module.exports = async function ( .connect(wallets.gelatoProvider) .isTaskSpecProvided( wallets.gelatoProviderAddress, - connectGelatoFullDebtBridgeFromMakerTaskSpec + mockConnectGelatoDataFullMakerToMakerTaskSpec ) ).to.be.equal("OK"); @@ -70,7 +71,7 @@ module.exports = async function ( wallets.gelatoProviderAddress, await contracts.gelatoCore .connect(wallets.gelatoProvider) - .hashTaskSpec(connectGelatoFullDebtBridgeFromMakerTaskSpec) + .hashTaskSpec(mockConnectGelatoDataFullMakerToMakerTaskSpec) ) ).to.be.equal(gasPriceCeil); diff --git a/test/gas/debt_bridge/full/from_maker/helpers/setupFullRefinanceMakerToNewMaker.mock.js b/test/gas/debt_bridge/full/from_maker/helpers/setupETHA-ETHB.mock.js similarity index 94% rename from test/gas/debt_bridge/full/from_maker/helpers/setupFullRefinanceMakerToNewMaker.mock.js rename to test/gas/debt_bridge/full/from_maker/helpers/setupETHA-ETHB.mock.js index 1adad4a..fae4bf4 100644 --- a/test/gas/debt_bridge/full/from_maker/helpers/setupFullRefinanceMakerToNewMaker.mock.js +++ b/test/gas/debt_bridge/full/from_maker/helpers/setupETHA-ETHB.mock.js @@ -7,7 +7,7 @@ const addProviderModuleDSA = require("../../../../../helpers/services/gelato/add const createDSA = require("../../../../../helpers/services/InstaDapp/createDSA"); const initializeMakerCdp = require("../../../../../helpers/services/maker/initializeMakerCdp"); const createVaultForETHB = require("../../../../../helpers/services/maker/createVaultForETHB"); -const getMockSpellsETHAnewETHB = require("./services/getSpells-ETHA-newETHB.mock"); +const getMockSpellsETHAETHB = require("./services/getSpells-ETHA-ETHB.mock"); const getABI = require("../../../../../helpers/services/getABI"); module.exports = async function (mockRoute) { @@ -55,7 +55,7 @@ module.exports = async function (mockRoute) { contracts.getCdps, contracts.dssCdpManager ); - const spells = await getMockSpellsETHAnewETHB( + const spells = await getMockSpellsETHAETHB( wallets, contracts, constants, diff --git a/test/gas/debt_bridge/full/from_maker/helpers/setupETHA-newETHB.mock.js b/test/gas/debt_bridge/full/from_maker/helpers/setupETHA-newETHB.mock.js new file mode 100644 index 0000000..8fb4022 --- /dev/null +++ b/test/gas/debt_bridge/full/from_maker/helpers/setupETHA-newETHB.mock.js @@ -0,0 +1,66 @@ +const getWallets = require("../../../../../helpers/services/getWallets"); +const getContracts = require("../../../../../helpers/services/getContracts"); +const getDebtBridgeFromMakerConstants = require("../../../../../integration/debt_bridge/from_maker/services/getDebtBridgeFromMakerConstants"); +const provideFunds = require("../../../../../helpers/services/gelato/provideFunds"); +const providerAssignsExecutor = require("../../../../../helpers/services/gelato/providerAssignsExecutor"); +const addProviderModuleDSA = require("../../../../../helpers/services/gelato/addProviderModuleDSA"); +const createDSA = require("../../../../../helpers/services/InstaDapp/createDSA"); +const initializeMakerCdp = require("../../../../../helpers/services/maker/initializeMakerCdp"); +const getMockSpellsETHAnewETHB = require("./services/getSpells-ETHA-newETHB.mock"); +const getABI = require("../../../../../helpers/services/getABI"); + +module.exports = async function (mockRoute) { + const wallets = await getWallets(); + const contracts = await getContracts(); + const constants = await getDebtBridgeFromMakerConstants(); + const ABI = getABI(); + + // Gelato Testing environment setup. + await provideFunds( + wallets.gelatoProvider, + contracts.gelatoCore, + constants.GAS_LIMIT, + constants.GAS_PRICE_CEIL + ); + await providerAssignsExecutor( + wallets.gelatoProvider, + contracts.mockDebtBridgeETHBExecutor.address, + contracts.gelatoCore + ); + await addProviderModuleDSA( + wallets.gelatoProvider, + contracts.gelatoCore, + contracts.dsaProviderModule.address + ); + contracts.dsa = await createDSA( + wallets.userAddress, + contracts.instaIndex, + contracts.instaList + ); + const vaultAId = await initializeMakerCdp( + wallets.userAddress, + contracts.DAI, + contracts.dsa, + contracts.getCdps, + contracts.dssCdpManager, + constants.MAKER_INITIAL_ETH, + constants.MAKER_INITIAL_DEBT, + ABI.ConnectMakerABI + ); + const spells = await getMockSpellsETHAnewETHB( + wallets, + contracts, + constants, + mockRoute, + vaultAId + ); + + return { + wallets, + contracts, + constants, + vaultAId, + spells, + ABI, + }; +}; diff --git a/test/helpers/services/getContracts.js b/test/helpers/services/getContracts.js index e4eb08c..f993632 100644 --- a/test/helpers/services/getContracts.js +++ b/test/helpers/services/getContracts.js @@ -112,8 +112,8 @@ module.exports = async function () { const mockDebtBridgeETHBExecutor = await ethers.getContract( "MockDebtBridgeETHBExecutor" ); - const mockConnectGelatoDataFullRefinanceMaker = await ethers.getContract( - "MockConnectGelatoDataFullRefinanceMaker" + const mockConnectGelatoDataFullMakerToMaker = await ethers.getContract( + "MockConnectGelatoDataFullMakerToMaker" ); const conditionDestVaultWillBeSafe = await ethers.getContract( @@ -148,7 +148,7 @@ module.exports = async function () { dsaProviderModule, conditionDebtBridgeIsAffordable, mockDebtBridgeETHBExecutor, - mockConnectGelatoDataFullRefinanceMaker, + mockConnectGelatoDataFullMakerToMaker, conditionDestVaultWillBeSafe, }; }; diff --git a/yarn.lock b/yarn.lock index 48c19c8..9e40ce4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,10 +907,10 @@ safe-buffer "^5.1.1" util.promisify "^1.0.0" -"@nomiclabs/hardhat-ethers@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.0.tgz#ebab032b3aed03945ea560f56bb67aec56a30cbc" - integrity sha512-fIi6XP9PgKqwSNVcLDr6S5hvGlc21PendaLD5eGdXEXc9aYQ0OJX8Mk3evs+p78x7W9n9U3ZcKtTiGc1+YScDw== +"@nomiclabs/hardhat-ethers@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.1.tgz#f86a6fa210dbe6270adffccc75e93ed60a856904" + integrity sha512-uTFHDhhvJ+UjfvvMeQxD3ZALuzuI3FXzTYT1Z5N3ebyZL5z4Ogwt55GB0R9tdKY0p5HhDhBjU/gsCjUEwIVoaw== "@nomiclabs/hardhat-waffle@2.0.0": version "2.0.0" @@ -920,10 +920,10 @@ "@types/sinon-chai" "^3.2.3" "@types/web3" "1.0.19" -"@openzeppelin/contracts@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.2.0.tgz#3e6b3a7662d8ed64271ade96ef42655db983fd9d" - integrity sha512-bUOmkSoPkjnUyMiKo6RYnb0VHBk5D9KKDAgNLzF41aqAM3TeE0yGdFF5dVRcV60pZdJLlyFT/jjXIZCWyyEzAQ== +"@openzeppelin/contracts@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.3.0.tgz#ffdb693c5c349fc33bba420248dd3ac0a2d7c408" + integrity sha512-AemZEsQYtUp1WRkcmZm1div5ORfTpLquLaziCIrSagjxyKdmObxuaY1yjQ5SHFMctR8rLwp706NXTbiIRJg7pw== "@resolver-engine/core@^0.3.3": version "0.3.3" @@ -4781,10 +4781,10 @@ hardhat-deploy-ethers@0.3.0-beta.6: resolved "https://registry.yarnpkg.com/hardhat-deploy-ethers/-/hardhat-deploy-ethers-0.3.0-beta.6.tgz#a6b70d0849fa5dd0f00698385c6bbb9c4b70fc96" integrity sha512-dwatT8Cw2FWSgvnUJom8lnPuJ2ULz8Ir844GHFDterfDMWvCcJCqnATX+nMznYoSJa7NEzoSPAm+RiWvvkh/2A== -hardhat-deploy@0.7.0-beta.30: - version "0.7.0-beta.30" - resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.7.0-beta.30.tgz#51179ee9cf4b93bd9a84841fe408a326bb3d719e" - integrity sha512-MColmxi+Ni3Savh9zDhfGD8EI4dUpaF6yUiysXZkv8z7HgMlL6Ho/2c+Z1NEycf1GTJ6p07YpZDD2sLfY3qwDA== +hardhat-deploy@0.7.0-beta.32: + version "0.7.0-beta.32" + resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.7.0-beta.32.tgz#24fc91d7cda12be3ee97f929db8bc6a629030f80" + integrity sha512-po+vW+gtmqls/Od1kc9btDeza6sR+DHbVi1KaiS5tgYLyCbDnf4Ih+2UjGQMDsaJbYxK/19jOOf5rfRsOaTAzA== dependencies: "@ethersproject/abi" "^5.0.2" "@ethersproject/abstract-signer" "^5.0.2" @@ -4813,10 +4813,10 @@ hardhat-gas-reporter@1.0.1: dependencies: eth-gas-reporter "^0.2.19" -hardhat@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.0.3.tgz#35e07060d9854d4182f201aeaa1c05316bd7e4d3" - integrity sha512-mDygAl+1qd5KBdXQBfc3R5XmC/rVdYYbEuOTSQY3rlncVu9gfockZVDsHtAMPw/FiBIRMApLcOceK7D1XQmHRw== +hardhat@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.0.4.tgz#f312c79d57d81861d9a837fbb8cb64302735b58f" + integrity sha512-TPDthnVJ6JNT38l769rRAJk3tkbGUZEaVSGW6TFXN7OuB08VZMH0dVyvUhnRas9BByFozTfIjr6AgnZ7S8o5PQ== dependencies: "@nomiclabs/ethereumjs-vm" "^4.1.1" "@sentry/node" "^5.18.1" @@ -7102,10 +7102,10 @@ prettier-plugin-solidity@1.0.0-beta.1: solidity-comments-extractor "^0.0.4" string-width "^4.2.0" -prettier@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.0.tgz#8a03c7777883b29b37fb2c4348c66a78e980418b" - integrity sha512-yYerpkvseM4iKD/BXLYUkQV5aKt4tQPqaGW6EsZjzyu0r7sVZZNPJW4Y8MyKmicp6t42XUPcBVA+H6sB3gqndw== +prettier@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== prettier@^1.14.3: version "1.19.1"