From 4ee46422cf75e88d1e09361d86c00693f02b12dd Mon Sep 17 00:00:00 2001 From: Mubaris NK Date: Sat, 13 Feb 2021 14:41:50 +0530 Subject: [PATCH] More connectors --- contracts/connectors/dydx/events.sol | 8 + contracts/connectors/dydx/helpers.sol | 72 +++++ contracts/connectors/dydx/interface.sol | 61 ++++ contracts/connectors/dydx/main.sol | 160 +++++++++++ contracts/connectors/dydxFlash/events.sol | 5 + contracts/connectors/dydxFlash/interface.sol | 5 + contracts/connectors/dydxFlash/main.sol | 38 +++ contracts/connectors/fee/main.sol | 30 ++ contracts/connectors/gelato/events.sol | 45 +++ contracts/connectors/gelato/interface.sol | 119 ++++++++ contracts/connectors/gelato/main.sol | 167 +++++++++++ contracts/connectors/instapool/events.sol | 53 ++++ contracts/connectors/instapool/helpers.sol | 81 ++++++ contracts/connectors/instapool/interface.sol | 31 +++ contracts/connectors/instapool/main.sol | 278 +++++++++++++++++++ 15 files changed, 1153 insertions(+) create mode 100644 contracts/connectors/dydx/events.sol create mode 100644 contracts/connectors/dydx/helpers.sol create mode 100644 contracts/connectors/dydx/interface.sol create mode 100644 contracts/connectors/dydx/main.sol create mode 100644 contracts/connectors/dydxFlash/events.sol create mode 100644 contracts/connectors/dydxFlash/interface.sol create mode 100644 contracts/connectors/dydxFlash/main.sol create mode 100644 contracts/connectors/fee/main.sol create mode 100644 contracts/connectors/gelato/events.sol create mode 100644 contracts/connectors/gelato/interface.sol create mode 100644 contracts/connectors/gelato/main.sol create mode 100644 contracts/connectors/instapool/events.sol create mode 100644 contracts/connectors/instapool/helpers.sol create mode 100644 contracts/connectors/instapool/interface.sol create mode 100644 contracts/connectors/instapool/main.sol diff --git a/contracts/connectors/dydx/events.sol b/contracts/connectors/dydx/events.sol new file mode 100644 index 00000000..4c1ea7ea --- /dev/null +++ b/contracts/connectors/dydx/events.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDeposit(address indexed token, uint marketId, uint256 tokenAmt, uint256 getId, uint256 setId); + event LogWithdraw(address indexed token, uint marketId, uint256 tokenAmt, uint256 getId, uint256 setId); + event LogBorrow(address indexed token, uint marketId, uint256 tokenAmt, uint256 getId, uint256 setId); + event LogPayback(address indexed token, uint marketId, uint256 tokenAmt, uint256 getId, uint256 setId); +} diff --git a/contracts/connectors/dydx/helpers.sol b/contracts/connectors/dydx/helpers.sol new file mode 100644 index 00000000..7c5f17b9 --- /dev/null +++ b/contracts/connectors/dydx/helpers.sol @@ -0,0 +1,72 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { SoloMarginContract } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev Solo Margin + */ + SoloMarginContract internal constant solo = SoloMarginContract(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e); + + /** + * @dev Get Dydx Actions args. + */ + function getActionsArgs(uint256 marketId, uint256 amt, bool sign) internal view returns (SoloMarginContract.ActionArgs[] memory) { + SoloMarginContract.ActionArgs[] memory actions = new SoloMarginContract.ActionArgs[](1); + SoloMarginContract.AssetAmount memory amount = SoloMarginContract.AssetAmount( + sign, + SoloMarginContract.AssetDenomination.Wei, + SoloMarginContract.AssetReference.Delta, + amt + ); + bytes memory empty; + SoloMarginContract.ActionType action = sign ? SoloMarginContract.ActionType.Deposit : SoloMarginContract.ActionType.Withdraw; + actions[0] = SoloMarginContract.ActionArgs( + action, + 0, + amount, + marketId, + 0, + address(this), + 0, + empty + ); + return actions; + } + + /** + * @dev Get Dydx Acccount arg + */ + function getAccountArgs() internal view returns (SoloMarginContract.Info[] memory) { + SoloMarginContract.Info[] memory accounts = new SoloMarginContract.Info[](1); + accounts[0] = (SoloMarginContract.Info(address(this), 0)); + return accounts; + } + + /** + * @dev Get Dydx Position + */ + function getDydxPosition(uint256 marketId) internal returns (uint tokenBal, bool tokenSign) { + SoloMarginContract.Wei memory tokenWeiBal = solo.getAccountWei(getAccountArgs()[0], marketId); + tokenBal = tokenWeiBal.value; + tokenSign = tokenWeiBal.sign; + } + + /** + * @dev Get Dydx Market ID from token Address + */ + function getMarketId(address token) internal view returns (uint _marketId) { + uint markets = solo.getNumMarkets(); + address _token = token == ethAddr ? wethAddr : token; + + for (uint i = 0; i < markets; i++) { + if (_token == solo.getMarketTokenAddress(i)) { + _marketId = i; + break; + } + } + } +} diff --git a/contracts/connectors/dydx/interface.sol b/contracts/connectors/dydx/interface.sol new file mode 100644 index 00000000..9e4eb6be --- /dev/null +++ b/contracts/connectors/dydx/interface.sol @@ -0,0 +1,61 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +interface SoloMarginContract { + + struct Info { + address owner; + uint256 number; + } + + enum ActionType { + Deposit, + Withdraw, + Transfer, + Buy, + Sell, + Trade, + Liquidate, + Vaporize, + Call + } + + enum AssetDenomination { + Wei, + Par + } + + enum AssetReference { + Delta, + Target + } + + struct AssetAmount { + bool sign; + AssetDenomination denomination; + AssetReference ref; + uint256 value; + } + + struct ActionArgs { + ActionType actionType; + uint256 accountId; + AssetAmount amount; + uint256 primaryMarketId; + uint256 secondaryMarketId; + address otherAddress; + uint256 otherAccountId; + bytes data; + } + + struct Wei { + bool sign; + uint256 value; + } + + function operate(Info[] calldata accounts, ActionArgs[] calldata actions) external; + function getAccountWei(Info calldata account, uint256 marketId) external returns (Wei memory); + function getNumMarkets() external view returns (uint256); + function getMarketTokenAddress(uint256 marketId) external view returns (address); + +} diff --git a/contracts/connectors/dydx/main.sol b/contracts/connectors/dydx/main.sol new file mode 100644 index 00000000..99345a84 --- /dev/null +++ b/contracts/connectors/dydx/main.sol @@ -0,0 +1,160 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract DyDxResolver is Events, Helpers { + + /** + * @dev Deposit ETH/ERC20_Token. + * @param token token address to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount to deposit. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function deposit( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + uint _marketId = getMarketId(token); + + (uint depositedAmt, bool sign) = getDydxPosition(_marketId); + require(depositedAmt == 0 || sign, "token-borrowed"); + + if (token == ethAddr) { + TokenInterface tokenContract = TokenInterface(wethAddr); + _amt = _amt == uint(-1) ? address(this).balance : _amt; + tokenContract.deposit{value: _amt}(); + tokenContract.approve(address(solo), _amt); + } else { + TokenInterface tokenContract = TokenInterface(token); + _amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt; + tokenContract.approve(address(solo), _amt); + } + + solo.operate(getAccountArgs(), getActionsArgs(_marketId, _amt, true)); + setUint(setId, _amt); + + _eventName = "LogDeposit(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _marketId, _amt, getId, setId); + } + + /** + * @dev Withdraw ETH/ERC20_Token. + * @param token token address to withdraw.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount to withdraw. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function withdraw( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + uint _marketId = getMarketId(token); + + (uint depositedAmt, bool sign) = getDydxPosition(_marketId); + require(sign, "try-payback"); + + _amt = _amt == uint(-1) ? depositedAmt : _amt; + require(_amt <= depositedAmt, "withdraw-exceeds"); + + solo.operate(getAccountArgs(), getActionsArgs(_marketId, _amt, false)); + + if (token == ethAddr) { + TokenInterface tokenContract = TokenInterface(wethAddr); + tokenContract.approve(address(tokenContract), _amt); + tokenContract.withdraw(_amt); + } + + setUint(setId, _amt); + + _eventName = "LogWithdraw(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _marketId, _amt, getId, setId); + } + + /** + * @dev Borrow ETH/ERC20_Token. + * @param token token address to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount to borrow. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function borrow( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + uint _marketId = getMarketId(token); + + (uint borrowedAmt, bool sign) = getDydxPosition(_marketId); + require(borrowedAmt == 0 || !sign, "token-deposited"); + + solo.operate(getAccountArgs(), getActionsArgs(_marketId, _amt, false)); + + if (token == ethAddr) { + TokenInterface tokenContract = TokenInterface(wethAddr); + tokenContract.approve(address(tokenContract), _amt); + tokenContract.withdraw(_amt); + } + + setUint(setId, _amt); + + _eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _marketId, _amt, getId, setId); + } + + /** + * @dev Payback borrowed ETH/ERC20_Token. + * @param token token address to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount to payback. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function payback( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + uint _marketId = getMarketId(token); + + (uint borrowedAmt, bool sign) = getDydxPosition(_marketId); + require(!sign, "try-withdraw"); + + _amt = _amt == uint(-1) ? borrowedAmt : _amt; + require(_amt <= borrowedAmt, "payback-exceeds"); + + if (token == ethAddr) { + TokenInterface tokenContract = TokenInterface(wethAddr); + require(address(this).balance >= _amt, "not-enough-eth"); + tokenContract.deposit{value: _amt}(); + tokenContract.approve(address(solo), _amt); + } else { + TokenInterface tokenContract = TokenInterface(token); + require(tokenContract.balanceOf(address(this)) >= _amt, "not-enough-token"); + tokenContract.approve(address(solo), _amt); + } + + solo.operate(getAccountArgs(), getActionsArgs(_marketId, _amt, true)); + setUint(setId, _amt); + + _eventName = "LogPayback(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _marketId, _amt, getId, setId); + } + +} + +contract ConnectDydx is DyDxResolver { + string public name = "Dydx-v1"; +} diff --git a/contracts/connectors/dydxFlash/events.sol b/contracts/connectors/dydxFlash/events.sol new file mode 100644 index 00000000..2b646778 --- /dev/null +++ b/contracts/connectors/dydxFlash/events.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDydxFlashLoan(address indexed token, uint256 tokenAmt); +} diff --git a/contracts/connectors/dydxFlash/interface.sol b/contracts/connectors/dydxFlash/interface.sol new file mode 100644 index 00000000..a67a958d --- /dev/null +++ b/contracts/connectors/dydxFlash/interface.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.7.0; + +interface DydxFlashInterface { + function initiateFlashLoan(address _token, uint256 _amount, bytes calldata data) external; +} diff --git a/contracts/connectors/dydxFlash/main.sol b/contracts/connectors/dydxFlash/main.sol new file mode 100644 index 00000000..f93f9ed3 --- /dev/null +++ b/contracts/connectors/dydxFlash/main.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../common/interfaces.sol"; +import { Events } from "./events.sol"; +import { DydxFlashInterface } from "./interface.sol"; + +abstract contract FlashLoanResolver is DSMath, Basic, Events { + address internal constant dydxAddr = address(0); // check9898 - change to dydx flash contract address + + /** + * @dev Borrow Flashloan and Cast spells. + * @param token Token Address. + * @param tokenAmt Token Amount. + * @param data targets & data for cast. + */ + function borrowAndCast( + address token, + uint tokenAmt, + bytes memory data + ) public payable returns (string memory _eventName, bytes memory _eventParam) { + AccountInterface(address(this)).enable(dydxAddr); + + address _token = token == ethAddr ? wethAddr : token; + + DydxFlashInterface(dydxAddr).initiateFlashLoan(_token, tokenAmt, data); + + AccountInterface(address(this)).disable(dydxAddr); + + _eventName = "LogDydxFlashLoan(address,uint256)"; + _eventParam = abi.encode(token, tokenAmt); + } +} + +contract ConnectDydxFlashLoan is FlashLoanResolver { + string public constant name = "dydx-flashloan-v1"; +} diff --git a/contracts/connectors/fee/main.sol b/contracts/connectors/fee/main.sol new file mode 100644 index 00000000..e65eb52d --- /dev/null +++ b/contracts/connectors/fee/main.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; + +abstract contract FeeResolver is DSMath, Basic { + /** + * @dev Calculate fee + */ + function calculateFee( + uint amount, + uint fee, + uint getId, + uint setId, + uint setIdFee + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amount); + + uint feeAmt = wmul(_amt, fee); + + uint totalAmt = add(_amt, feeAmt); + + setUint(setId, totalAmt); + setUint(setIdFee, feeAmt); + } +} + +contract ConnectFee is FeeResolver { + string public constant name = "Fee-v1"; +} diff --git a/contracts/connectors/gelato/events.sol b/contracts/connectors/gelato/events.sol new file mode 100644 index 00000000..b5f53180 --- /dev/null +++ b/contracts/connectors/gelato/events.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { Task, Provider, TaskSpec, TaskReceipt } from "./interface.sol"; + +contract Events { + event LogMultiProvide( + address indexed executor, + TaskSpec[] indexed taskspecs, + address[] indexed modules, + uint256 ethToDeposit, + uint256 getId, + uint256 setId + ); + + event LogSubmitTask( + Provider indexed provider, + Task indexed task, + uint256 indexed expiryDate + ); + + event LogSubmitTaskCycle( + Provider indexed provider, + Task[] indexed tasks, + uint256 indexed expiryDate + ); + + event LogSubmitTaskChain( + Provider indexed provider, + Task[] indexed tasks, + uint256 indexed expiryDate + ); + + event LogMultiUnprovide( + TaskSpec[] indexed taskspecs, + address[] indexed modules, + uint256 ethToWithdraw, + uint256 getId, + uint256 setId + ); + + event LogMultiCancelTasks( + TaskReceipt[] indexed taskReceipt + ); +} diff --git a/contracts/connectors/gelato/interface.sol b/contracts/connectors/gelato/interface.sol new file mode 100644 index 00000000..af5d970d --- /dev/null +++ b/contracts/connectors/gelato/interface.sol @@ -0,0 +1,119 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +// Gelato Data Types +struct Provider { + address addr; // if msg.sender == provider => self-Provider + address module; // e.g. DSA Provider Module +} + +struct Condition { + address inst; // can be AddressZero for self-conditional Actions + bytes data; // can be bytes32(0) for self-conditional Actions +} + +enum Operation { Call, Delegatecall } + +enum DataFlow { None, In, Out, InAndOut } + +struct Action { + address addr; + bytes data; + Operation operation; + DataFlow dataFlow; + uint256 value; + bool termsOkCheck; +} + +struct Task { + Condition[] conditions; // optional + Action[] actions; + uint256 selfProviderGasLimit; // optional: 0 defaults to gelatoMaxGas + uint256 selfProviderGasPriceCeil; // optional: 0 defaults to NO_CEIL +} + +struct TaskReceipt { + uint256 id; + address userProxy; + Provider provider; + uint256 index; + Task[] tasks; + uint256 expiryDate; + uint256 cycleId; // auto-filled by GelatoCore. 0 for non-cyclic/chained tasks + uint256 submissionsLeft; +} + +struct TaskSpec { + address[] conditions; // Address: optional AddressZero for self-conditional actions + Action[] actions; + uint256 gasPriceCeil; +} + +// Gelato Interface +interface IGelatoInterface { + + /** + * @dev API to submit a single Task. + */ + function submitTask( + Provider calldata _provider, + Task calldata _task, + uint256 _expiryDate + ) + external; + + + /** + * @dev A Gelato Task Cycle consists of 1 or more Tasks that automatically submit + * the next one, after they have been executed, where the total number of tasks can + * be only be an even number + */ + function submitTaskCycle( + Provider calldata _provider, + Task[] calldata _tasks, + uint256 _expiryDate, + uint256 _cycles + ) + external; + + + /** + * @dev A Gelato Task Chain consists of 1 or more Tasks that automatically submit + * the next one, after they have been executed, where the total number of tasks can + * be an odd number + */ + function submitTaskChain( + Provider calldata _provider, + Task[] calldata _tasks, + uint256 _expiryDate, + uint256 _sumOfRequestedTaskSubmits + ) + external; + + /** + * @dev Cancel multiple tasks at once + */ + function multiCancelTasks(TaskReceipt[] calldata _taskReceipts) external; + + /** + * @dev Whitelist new executor, TaskSpec(s) and Module(s) in one tx + */ + function multiProvide( + address _executor, + TaskSpec[] calldata _taskSpecs, + address[] calldata _modules + ) + external + payable; + + + /** + * @dev De-Whitelist TaskSpec(s), Module(s) and withdraw funds from gelato in one tx + */ + function multiUnprovide( + uint256 _withdrawAmount, + TaskSpec[] calldata _taskSpecs, + address[] calldata _modules + ) + external; +} diff --git a/contracts/connectors/gelato/main.sol b/contracts/connectors/gelato/main.sol new file mode 100644 index 00000000..b761d937 --- /dev/null +++ b/contracts/connectors/gelato/main.sol @@ -0,0 +1,167 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { IGelatoInterface, Task, Provider, TaskSpec, TaskReceipt } from "./interface.sol"; +import { Events } from "./events.sol"; + +abstract contract GelatoResolver is DSMath, Basic, Events { + + IGelatoInterface internal constant gelato = IGelatoInterface(0x1d681d76ce96E4d70a88A00EBbcfc1E47808d0b8); + + // ===== Gelato ENTRY APIs ====== + + /** + * @dev Enables first time users to pre-fund eth, whitelist an executor & register the + * ProviderModuleDSA.sol to be able to use Gelato + * @param _executor address of single execot node or gelato'S decentralized execution market + * @param _taskSpecs enables external providers to whitelist TaskSpecs on gelato + * @param _modules address of ProviderModuleDSA + * @param _ethToDeposit amount of eth to deposit on Gelato, only for self-providers + */ + function multiProvide( + address _executor, + TaskSpec[] calldata _taskSpecs, + address[] calldata _modules, + uint256 _ethToDeposit, + uint256 _getId, + uint256 _setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint256 ethToDeposit = getUint(_getId, _ethToDeposit); + ethToDeposit = ethToDeposit == uint(-1) ? address(this).balance : ethToDeposit; + + gelato.multiProvide{value: ethToDeposit}( + _executor, + _taskSpecs, + _modules + ); + + setUint(_setId, ethToDeposit); + + _eventName = "LogMultiProvide(address,TaskSpec[],address[],uint256,uint256,uint256)"; + _eventParam = abi.encode(_executor, _taskSpecs, _modules, ethToDeposit, _getId, _setId); + } + + /** + * @dev Submits a single, one-time task to Gelato + * @param _provider Consists of proxy module address (DSA) and provider address () + * who will pay for the transaction execution + * @param _task Task specifying the condition and the action connectors + * @param _expiryDate Default 0, othweise timestamp after which the task expires + */ + function submitTask( + Provider calldata _provider, + Task calldata _task, + uint256 _expiryDate + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + gelato.submitTask(_provider, _task, _expiryDate); + + _eventName = "LogSubmitTask(Provider,Task,uint256)"; + _eventParam = abi.encode(_provider, _task, _expiryDate); + } + + /** + * @dev Submits single or mulitple Task Sequences to Gelato + * @param _provider Consists of proxy module address (DSA) and provider address () + * who will pay for the transaction execution + * @param _tasks A sequence of Tasks, can be a single or multiples + * @param _expiryDate Default 0, othweise timestamp after which the task expires + * @param _cycles How often the Task List should be executed, e.g. 5 times + */ + function submitTaskCycle( + Provider calldata _provider, + Task[] calldata _tasks, + uint256 _expiryDate, + uint256 _cycles + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + gelato.submitTaskCycle( + _provider, + _tasks, + _expiryDate, + _cycles + ); + + _eventName = "LogSubmitTaskCycle(Provider,Task[],uint256)"; + _eventParam = abi.encode(_provider, _tasks, _expiryDate); + } + + /** + * @dev Submits single or mulitple Task Chains to Gelato + * @param _provider Consists of proxy module address (DSA) and provider address () + * who will pay for the transaction execution + * @param _tasks A sequence of Tasks, can be a single or multiples + * @param _expiryDate Default 0, othweise timestamp after which the task expires + * @param _sumOfRequestedTaskSubmits The TOTAL number of Task auto-submits + * that should have occured once the cycle is complete + */ + function submitTaskChain( + Provider calldata _provider, + Task[] calldata _tasks, + uint256 _expiryDate, + uint256 _sumOfRequestedTaskSubmits + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + gelato.submitTaskChain( + _provider, + _tasks, + _expiryDate, + _sumOfRequestedTaskSubmits + ); + + _eventName = "LogSubmitTaskChain(Provider,Task[],uint256)"; + _eventParam = abi.encode(_provider, _tasks, _expiryDate); + } + + // ===== Gelato EXIT APIs ====== + + /** + * @dev Withdraws funds from Gelato, de-whitelists TaskSpecs and Provider Modules + * in one tx + * @param _withdrawAmount Amount of ETH to withdraw from Gelato + * @param _taskSpecs List of Task Specs to de-whitelist, default empty [] + * @param _modules List of Provider Modules to de-whitelist, default empty [] + */ + function multiUnprovide( + uint256 _withdrawAmount, + TaskSpec[] calldata _taskSpecs, + address[] calldata _modules, + uint256 _getId, + uint256 _setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint256 withdrawAmount = getUint(_getId, _withdrawAmount); + uint256 balanceBefore = address(this).balance; + + gelato.multiUnprovide( + withdrawAmount, + _taskSpecs, + _modules + ); + + uint256 actualWithdrawAmount = sub(address(this).balance, balanceBefore); + + setUint(_setId, actualWithdrawAmount); + + _eventName = "LogMultiUnprovide(TaskSpec[],address[],uint256,uint256,uint256)"; + _eventParam = abi.encode(_taskSpecs, _modules, actualWithdrawAmount, _getId, _setId); + } + + /** + * @dev Cancels outstanding Tasks + * @param _taskReceipts List of Task Receipts to cancel + */ + function multiCancelTasks(TaskReceipt[] calldata _taskReceipts) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + gelato.multiCancelTasks(_taskReceipts); + + _eventName = "LogMultiCancelTasks(TaskReceipt[])"; + _eventParam = abi.encode(_taskReceipts); + } + +} + +contract ConnectGelato is GelatoResolver { + string public name = "Gelato-v1.0"; +} diff --git a/contracts/connectors/instapool/events.sol b/contracts/connectors/instapool/events.sol new file mode 100644 index 00000000..d9fc1a52 --- /dev/null +++ b/contracts/connectors/instapool/events.sol @@ -0,0 +1,53 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositLiquidity(address indexed token, uint256 tokenAmt, uint256 getId, uint256 setId); + event LogWithdrawLiquidity(address indexed token, uint256 tokenAmt, uint256 getId, uint256 setId); + event LogFlashBorrow( + address indexed token, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogFlashPayback( + address indexed token, + uint256 tokenAmt, + uint256 feeCollected, + uint256 getId, + uint256 setId + ); + + event LogFlashPaybackOrigin( + address indexed origin, + address indexed token, + uint256 tokenAmt, + uint256 feeCollected, + uint256 originFeeAmt, + uint256 getId, + uint256 setId + ); + + event LogMultiBorrow( + address[] tokens, + uint256[] amts, + uint256[] getId, + uint256[] setId + ); + + event LogMultiPayback( + address[] tokens, + uint256[] getId, + uint256[] setId + ); + + event LogMultiPaybackOrigin( + address indexed origin, + address[] tokens, + // uint256[] amts, + // uint256[] feeCollected, + // uint256[] originFeeAmt, + uint256[] getId, + uint256[] setId + ); +} diff --git a/contracts/connectors/instapool/helpers.sol b/contracts/connectors/instapool/helpers.sol new file mode 100644 index 00000000..bd63b68f --- /dev/null +++ b/contracts/connectors/instapool/helpers.sol @@ -0,0 +1,81 @@ +pragma solidity ^0.7.0; + +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { InstaPoolFeeInterface, LiqudityInterface } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + + using SafeERC20 for IERC20; + + /** + * @dev Instapool Helper + */ + LiqudityInterface internal constant instaPool = LiqudityInterface(0x06cB7C24990cBE6b9F99982f975f9147c000fec6); + + /** + * @dev Instapool Fee + */ + InstaPoolFeeInterface internal constant instaPoolFee = InstaPoolFeeInterface(0xAaA91046C1D1a210017e36394C83bD5070dadDa5); + + function _transfer(address payable to, IERC20 token, uint _amt) internal { + address(token) == ethAddr ? + to.transfer(_amt) : + token.safeTransfer(to, _amt); + } + + function _getBalance(IERC20 token) internal view returns (uint256) { + return address(token) == ethAddr ? + address(this).balance : + token.balanceOf(address(this)); + } + + function calculateTotalFeeAmt(IERC20 token, uint amt) internal view returns (uint totalAmt) { + uint fee = instaPoolFee.fee(); + uint flashAmt = instaPool.borrowedToken(address(token)); + if (fee == 0) { + totalAmt = amt; + } else { + uint feeAmt = wmul(flashAmt, fee); + totalAmt = add(amt, feeAmt); + } + } + + function calculateFeeAmt(IERC20 token, uint amt) internal view returns (address feeCollector, uint feeAmt) { + uint fee = instaPoolFee.fee(); + feeCollector = instaPoolFee.feeCollector(); + if (fee == 0) { + feeAmt = 0; + } else { + feeAmt = wmul(amt, fee); + uint totalAmt = add(amt, feeAmt); + + uint totalBal = _getBalance(token); + require(totalBal >= totalAmt - 10, "Not-enough-balance"); + feeAmt = totalBal > totalAmt ? feeAmt : sub(totalBal, amt); + } + } + + function calculateFeeAmtOrigin(IERC20 token, uint amt) + internal + view + returns ( + address feeCollector, + uint poolFeeAmt, + uint originFee + ) + { + uint feeAmt; + (feeCollector, feeAmt) = calculateFeeAmt(token, amt); + if (feeAmt == 0) { + poolFeeAmt = 0; + originFee = 0; + } else { + originFee = wmul(feeAmt, 20 * 10 ** 16); // 20% + poolFeeAmt = sub(feeAmt, originFee); + } + } +} diff --git a/contracts/connectors/instapool/interface.sol b/contracts/connectors/instapool/interface.sol new file mode 100644 index 00000000..b69ad0bb --- /dev/null +++ b/contracts/connectors/instapool/interface.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.7.0; + +interface LiqudityInterface { + function deposit(address, uint) external payable; + function withdraw(address, uint) external; + + function accessLiquidity(address[] calldata, uint[] calldata) external; + function returnLiquidity(address[] calldata) external payable; + + function isTknAllowed(address) external view returns(bool); + function tknToCTkn(address) external view returns(address); + function liquidityBalance(address, address) external view returns(uint); + + function borrowedToken(address) external view returns(uint); +} + +interface InstaPoolFeeInterface { + function fee() external view returns(uint); + function feeCollector() external view returns(address); +} + +interface CTokenInterface { + function borrowBalanceCurrent(address account) external returns (uint); + function balanceOf(address owner) external view returns (uint256 balance); + function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); // For ERC20 +} + +interface CETHInterface { + function borrowBalanceCurrent(address account) external returns (uint); + function repayBorrowBehalf(address borrower) external payable; +} diff --git a/contracts/connectors/instapool/main.sol b/contracts/connectors/instapool/main.sol new file mode 100644 index 00000000..3cd4092c --- /dev/null +++ b/contracts/connectors/instapool/main.sol @@ -0,0 +1,278 @@ +pragma solidity ^0.7.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract LiquidityManage is Helpers, Events { + /** + * @dev Deposit Liquidity in InstaPool. + * @param token token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function deposit( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + + uint ethAmt; + if (token == ethAddr) { + _amt = _amt == uint(-1) ? address(this).balance : _amt; + ethAmt = _amt; + } else { + IERC20 tokenContract = IERC20(token); + _amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt; + tokenContract.approve(address(instaPool), _amt); + } + + instaPool.deposit{value: ethAmt}(token, _amt); + setUint(setId, _amt); + + emit LogDepositLiquidity(token, _amt, getId, setId); + _eventName = "LogDepositLiquidity(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Withdraw Liquidity in InstaPool. + * @param token token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function withdraw( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + + instaPool.withdraw(token, _amt); + setUint(setId, _amt); + + _eventName = "LogWithdrawLiquidity(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } +} + +abstract contract LiquidityAccessHelper is LiquidityManage { + /** + * @dev Add Fee Amount to borrowed flashloan/ + * @param amt Get token amount at this ID from `InstaMemory` Contract. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function addFeeAmount(address token, uint amt, uint getId, uint setId) external payable { + uint _amt = getUint(getId, amt); + require(_amt != 0, "amt-is-0"); + uint totalFee = calculateTotalFeeAmt(IERC20(token), _amt); + + setUint(setId, totalFee); + } +} + +contract LiquidityAccess is LiquidityAccessHelper { + /** + * @dev Access Token Liquidity from InstaPool. + * @param token token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt token amount. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function flashBorrow( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + + address[] memory _tknAddrs = new address[](1); + _tknAddrs[0] = token; + uint[] memory _amts = new uint[](1); + _amts[0] = _amt; + + instaPool.accessLiquidity(_tknAddrs, _amts); + + setUint(setId, _amt); + + _eventName = "LogFlashBorrow(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Return Token Liquidity from InstaPool. + * @param token token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function flashPayback( + address token, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = instaPool.borrowedToken(token); + IERC20 tokenContract = IERC20(token); + + (address feeCollector, uint feeAmt) = calculateFeeAmt(tokenContract, _amt); + + address[] memory _tknAddrs = new address[](1); + _tknAddrs[0] = token; + + _transfer(payable(address(instaPool)), tokenContract, _amt); + instaPool.returnLiquidity(_tknAddrs); + + if (feeAmt > 0) _transfer(payable(feeCollector), tokenContract, feeAmt); + + setUint(setId, _amt); + + _eventName = "LogFlashPayback(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, feeAmt, getId, setId); + } + + /** + * @dev Return Token Liquidity from InstaPool and Transfer 20% of Collected Fee to `origin`. + * @param origin origin address to transfer 20% of the collected fee. + * @param token token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function flashPaybackOrigin( + address origin, + address token, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + require(origin != address(0), "origin-is-address(0)"); + uint _amt = instaPool.borrowedToken(token); + IERC20 tokenContract = IERC20(token); + + (address feeCollector, uint poolFeeAmt, uint originFeeAmt) = calculateFeeAmtOrigin(tokenContract, _amt); + + address[] memory _tknAddrs = new address[](1); + _tknAddrs[0] = token; + + _transfer(payable(address(instaPool)), tokenContract, _amt); + instaPool.returnLiquidity(_tknAddrs); + + if (poolFeeAmt > 0) { + _transfer(payable(feeCollector), tokenContract, poolFeeAmt); + _transfer(payable(origin), tokenContract, originFeeAmt); + } + + setUint(setId, _amt); + + _eventName = "LogFlashPaybackOrigin(address,address,uint256,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(origin, token, _amt, poolFeeAmt, originFeeAmt, getId, setId); + } +} + +contract LiquidityAccessMulti is LiquidityAccess { + /** + * @dev Access Multiple Token liquidity from InstaPool. + * @param tokens Array of token addresses.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amts Array of token amount. + * @param getId get token amounts at this IDs from `InstaMemory` Contract. + * @param setId set token amounts at this IDs in `InstaMemory` Contract. + */ + function flashMultiBorrow( + address[] calldata tokens, + uint[] calldata amts, + uint[] calldata getId, + uint[] calldata setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _length = tokens.length; + uint[] memory _amts = new uint[](_length); + for (uint i = 0; i < _length; i++) { + _amts[i] = getUint(getId[i], amts[i]); + } + + instaPool.accessLiquidity(tokens, _amts); + + for (uint i = 0; i < _length; i++) { + setUint(setId[i], _amts[i]); + } + + _eventName = "LogMultiBorrow(address[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode(tokens, amts, getId, setId); + } + + /** + * @dev Return Multiple token liquidity from InstaPool. + * @param tokens Array of token addresses.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param getId get token amounts at this IDs from `InstaMemory` Contract. + * @param setId set token amounts at this IDs in `InstaMemory` Contract. + */ + function flashMultiPayback( + address[] calldata tokens, + uint[] calldata getId, + uint[] calldata setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _length = tokens.length; + + for (uint i = 0; i < _length; i++) { + uint _amt = instaPool.borrowedToken(tokens[i]); + IERC20 tokenContract = IERC20(tokens[i]); + (address feeCollector, uint feeAmt) = calculateFeeAmt(tokenContract, _amt); + + _transfer(payable(address(instaPool)), tokenContract, _amt); + + if (feeAmt > 0) _transfer(payable(feeCollector), tokenContract, feeAmt); + + setUint(setId[i], _amt); + } + + instaPool.returnLiquidity(tokens); + + _eventName = "LogMultiPayback(address[],uint256[],uint256[])"; + _eventParam = abi.encode(tokens, getId, setId); + } + + // TODO - Fix stack too deep + // /** + // * @dev Return Multiple token liquidity from InstaPool and Tranfer 20% of the Fee to Origin. + // * @param tokens Array of token addresses.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + // * @param getId get token amounts at this IDs from `InstaMemory` Contract. + // * @param setId set token amounts at this IDs in `InstaMemory` Contract. + // */ + // function flashMultiPaybackOrigin( + // address origin, + // address[] calldata tokens, + // uint[] calldata getId, + // uint[] calldata setId + // ) external payable returns (string memory _eventName, bytes memory _eventParam) { + // uint _length = tokens.length; + + // for (uint i = 0; i < _length; i++) { + // uint _amt = instaPool.borrowedToken(tokens[i]); + // IERC20 tokenContract = IERC20(tokens[i]); + + // (address feeCollector, uint poolFeeAmt, uint originFeeAmt) = calculateFeeAmtOrigin(tokenContract, _amt); + + // _transfer(payable(address(instaPool)), tokenContract, _amt); + + // if (poolFeeAmt > 0) { + // _transfer(payable(feeCollector), tokenContract, poolFeeAmt); + // _transfer(payable(origin), tokenContract, originFeeAmt); + // } + + // setUint(setId[i], _amt); + // } + // instaPool.returnLiquidity(tokens); + + // _eventName = "LogMultiPaybackOrigin(address,address[],uint256[],uint256[])"; + // _eventParam = abi.encode(origin, tokens, getId, setId); + // } +} + +contract ConnectInstaPool is LiquidityAccessMulti { + string public name = "InstaPool-v2.1"; +}