pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.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(0xA0557234eB7b3c503388202D3768Cfa2f1AE9Dc2); 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"); (bytes memory callData, uint ethAmt) = abi.decode( data, (bytes, uint) ); wethContract.transfer(address(migrationAddr), ethAmt); Address.functionCall(address(migrationAddr), callData); } 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(); wethContract.approve(soloAddr, ethAmt + 2); solo.operate(accountInfos, operations); } } contract InstaPool is DydxFlashloaner { constructor() public { wethContract.approve(wethAddr, uint(-1)); } receive() external payable {} }