2021-04-09 01:03:40 +00:00
|
|
|
pragma solidity ^0.7.0;
|
|
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
2021-04-14 23:04:22 +00:00
|
|
|
import "@openzeppelin/contracts/utils/Address.sol";
|
2021-04-09 01:03:40 +00:00
|
|
|
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;
|
2021-04-16 19:00:13 +00:00
|
|
|
MigrationInterface public constant migrationAddr = MigrationInterface(0xA0557234eB7b3c503388202D3768Cfa2f1AE9Dc2);
|
2021-04-09 01:03:40 +00:00
|
|
|
|
|
|
|
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");
|
|
|
|
|
2021-04-14 23:04:22 +00:00
|
|
|
(bytes memory callData, uint ethAmt) = abi.decode(
|
2021-04-09 01:03:40 +00:00
|
|
|
data,
|
2021-04-14 23:04:22 +00:00
|
|
|
(bytes, uint)
|
2021-04-09 01:03:40 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
wethContract.transfer(address(migrationAddr), ethAmt);
|
|
|
|
|
2021-04-16 16:35:31 +00:00
|
|
|
Address.functionCall(address(migrationAddr), callData);
|
2021-04-09 01:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
2021-04-15 16:33:16 +00:00
|
|
|
wethContract.approve(soloAddr, ethAmt + 2);
|
|
|
|
|
2021-04-09 01:03:40 +00:00
|
|
|
solo.operate(accountInfos, operations);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
contract InstaPool is DydxFlashloaner {
|
|
|
|
constructor() public {
|
|
|
|
wethContract.approve(wethAddr, uint(-1));
|
|
|
|
}
|
|
|
|
|
|
|
|
receive() external payable {}
|
2021-04-16 16:34:49 +00:00
|
|
|
}
|