From 449f8aeefeba15cb3c8ce66d787932249cf66790 Mon Sep 17 00:00:00 2001 From: Samyak Jain <34437877+KaymasJain@users.noreply.github.com> Date: Fri, 9 Apr 2021 06:33:40 +0530 Subject: [PATCH] added flashloan contract --- contracts/liquidity.sol | 315 ++++++++++++++++++++ contracts/senders/aave-v2-migrator/main.sol | 7 + 2 files changed, 322 insertions(+) create mode 100644 contracts/liquidity.sol diff --git a/contracts/liquidity.sol b/contracts/liquidity.sol new file mode 100644 index 0000000..b904d93 --- /dev/null +++ b/contracts/liquidity.sol @@ -0,0 +1,315 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import { DSMath } from "./common/math.sol"; + +interface Account { + struct Info { + address owner; // The address that owns the account + uint256 number; // A nonce that allows a single address to control many accounts + } +} + +interface ListInterface { + function accountID(address) external view returns (uint64); +} + +interface Actions { + enum ActionType { + Deposit, // supply tokens + Withdraw, // borrow tokens + Transfer, // transfer balance between accounts + Buy, // buy an amount of some token (publicly) + Sell, // sell an amount of some token (publicly) + Trade, // trade tokens against another account + Liquidate, // liquidate an undercollateralized or expiring account + Vaporize, // use excess tokens to zero-out a completely negative account + Call // send arbitrary data to an address + } + + struct ActionArgs { + ActionType actionType; + uint256 accountId; + Types.AssetAmount amount; + uint256 primaryMarketId; + uint256 secondaryMarketId; + address otherAddress; + uint256 otherAccountId; + bytes data; + } + + struct DepositArgs { + Types.AssetAmount amount; + Account.Info account; + uint256 market; + address from; + } + + struct WithdrawArgs { + Types.AssetAmount amount; + Account.Info account; + uint256 market; + address to; + } + + struct CallArgs { + Account.Info account; + address callee; + bytes data; + } +} + +interface Types { + enum AssetDenomination { + Wei, // the amount is denominated in wei + Par // the amount is denominated in par + } + + enum AssetReference { + Delta, // the amount is given as a delta from the current value + Target // the amount is given as an exact number to end up at + } + + struct AssetAmount { + bool sign; // true if positive + AssetDenomination denomination; + AssetReference ref; + uint256 value; + } + + struct Wei { + bool sign; // true if positive + uint256 value; + } +} + + +interface ISoloMargin { + struct OperatorArg { + address operator; + bool trusted; + } + + function getMarketTokenAddress(uint256 marketId) + external + view + returns (address); + + function getNumMarkets() external view returns (uint256); + + + function operate( + Account.Info[] calldata accounts, + Actions.ActionArgs[] calldata actions + ) external; + + function getAccountWei(Account.Info calldata account, uint256 marketId) + external + view + returns (Types.Wei memory); +} + +contract DydxFlashloanBase { + function _getMarketIdFromTokenAddress(address _solo, address token) + internal + view + returns (uint256) + { + ISoloMargin solo = ISoloMargin(_solo); + + uint256 numMarkets = solo.getNumMarkets(); + + address curToken; + for (uint256 i = 0; i < numMarkets; i++) { + curToken = solo.getMarketTokenAddress(i); + + if (curToken == token) { + return i; + } + } + + revert("No marketId found for provided token"); + } + + function _getAccountInfo() internal view returns (Account.Info memory) { + return Account.Info({owner: address(this), number: 1}); + } + + function _getWithdrawAction(uint marketId, uint256 amount) + internal + view + returns (Actions.ActionArgs memory) + { + return + Actions.ActionArgs({ + actionType: Actions.ActionType.Withdraw, + accountId: 0, + amount: Types.AssetAmount({ + sign: false, + denomination: Types.AssetDenomination.Wei, + ref: Types.AssetReference.Delta, + value: amount + }), + primaryMarketId: marketId, + secondaryMarketId: 0, + otherAddress: address(this), + otherAccountId: 0, + data: "" + }); + } + + function _getCallAction(bytes memory data) + internal + view + returns (Actions.ActionArgs memory) + { + return + Actions.ActionArgs({ + actionType: Actions.ActionType.Call, + accountId: 0, + amount: Types.AssetAmount({ + sign: false, + denomination: Types.AssetDenomination.Wei, + ref: Types.AssetReference.Delta, + value: 0 + }), + primaryMarketId: 0, + secondaryMarketId: 0, + otherAddress: address(this), + otherAccountId: 0, + data: data + }); + } + + function _getDepositAction(uint marketId, uint256 amount) + internal + view + returns (Actions.ActionArgs memory) + { + return + Actions.ActionArgs({ + actionType: Actions.ActionType.Deposit, + accountId: 0, + amount: Types.AssetAmount({ + sign: true, + denomination: Types.AssetDenomination.Wei, + ref: Types.AssetReference.Delta, + value: amount + }), + primaryMarketId: marketId, + secondaryMarketId: 0, + otherAddress: address(this), + otherAccountId: 0, + data: "" + }); + } +} + +/** + * @title ICallee + * @author dYdX + * + * Interface that Callees for Solo must implement in order to ingest data. + */ +interface ICallee { + + // ============ Public Functions ============ + + /** + * Allows users to send this contract arbitrary data. + * + * @param sender The msg.sender to Solo + * @param accountInfo The account from which the data is being sent + * @param data Arbitrary data given by the sender + */ + function callFunction( + address sender, + Account.Info calldata accountInfo, + bytes calldata data + ) + external; +} +interface IndexInterface { + function master() external view returns (address); +} + +interface TokenInterface { + function approve(address, uint256) external; + function transfer(address, uint) external; + function transferFrom(address, address, uint) external; + function deposit() external payable; + function withdraw(uint) external; + function balanceOf(address) external view returns (uint); + function decimals() external view returns (uint); +} + +struct AaveDataRaw { + address targetDsa; + uint[] supplyAmts; + uint[] variableBorrowAmts; + uint[] stableBorrowAmts; + address[] supplyTokens; + address[] borrowTokens; +} + +interface MigrationInterface { + function migrateFlashCallback(AaveDataRaw calldata _data, address dsa, uint ethAmt) external; +} + +contract Setup { + address public constant soloAddr = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e; + address public constant wethAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + MigrationInterface public constant migrationAddr = MigrationInterface(address(0)); // TODO: Migration address + + TokenInterface wethContract = TokenInterface(wethAddr); + ISoloMargin solo = ISoloMargin(soloAddr); + +} + +contract DydxFlashloaner is Setup, ICallee, DydxFlashloanBase, DSMath { + using SafeERC20 for IERC20; + + function callFunction( + address sender, + Account.Info memory account, + bytes memory data + ) public override { + require(sender == address(this), "not-same-sender"); + require(msg.sender == soloAddr, "not-solo-dydx-sender"); + + (AaveDataRaw memory _data, address dsa, uint ethAmt) = abi.decode( + data, + (AaveDataRaw, address, uint) + ); + + wethContract.transfer(address(migrationAddr), ethAmt); + + migrationAddr.migrateFlashCallback(_data, dsa, ethAmt); + } + + function initiateFlashLoan(bytes memory data, uint ethAmt) external { + require(msg.sender == address(migrationAddr), "not-migration-contract"); + uint marketId = _getMarketIdFromTokenAddress(soloAddr, wethAddr); // TODO: set Static market ID? + + Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](3); + + operations[0] = _getWithdrawAction(marketId, ethAmt); + operations[1] = _getCallAction(data); + operations[2] = _getDepositAction(marketId, ethAmt + 2); + + Account.Info[] memory accountInfos = new Account.Info[](1); + accountInfos[0] = _getAccountInfo(); + + solo.operate(accountInfos, operations); + } +} + +contract InstaPool is DydxFlashloaner { + constructor() public { + wethContract.approve(wethAddr, uint(-1)); + } + + receive() external payable {} +} \ No newline at end of file diff --git a/contracts/senders/aave-v2-migrator/main.sol b/contracts/senders/aave-v2-migrator/main.sol index c8ff2af..26111d6 100644 --- a/contracts/senders/aave-v2-migrator/main.sol +++ b/contracts/senders/aave-v2-migrator/main.sol @@ -228,6 +228,13 @@ contract MigrateResolver is LiquidityResolver { _migrate(_data, msg.sender); } + function migrateFlashCallback(AaveDataRaw calldata _data, address dsa, uint ethAmt) external { + require(false); // TODO: flash loan contract + // TODO: deposit ETH in Aave + _migrate(_data, dsa); + // TODO: withdraw ETH from Aave + } + function migrateWithFlash(AaveDataRaw calldata _data, uint ethAmt) external { bytes memory data = abi.encode(_data, msg.sender, ethAmt);