Skip to content

Dydxflashloan #143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions contracts/Misc/FlashLoanDydx/FlashLoan.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "./interfaces/DydxFlashLoanBase.sol";
import "./interfaces/ICallee.sol";

contract TestDyDxSoloMargin is ICallee, DydxFlashloanBase {
address private constant SOLO = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;

address public flashUser;

struct MyCustomData {
address token;
uint repayAmount;
}

function initiateFlashLoan(address _token, uint _amount) external {
ISoloMargin solo = ISoloMargin(SOLO);

// Get marketId from token address
/*
0 WETH
1 SAI
2 USDC
3 DAI
*/
uint marketId = _getMarketIdFromTokenAddress(SOLO, _token);

// Calculate repay amount (_amount + (2 wei))
uint repayAmount = _getRepaymentAmountInternal(_amount);
IERC20(_token).approve(SOLO, repayAmount);

/*
1. Withdraw
2. Call callFunction()
3. Deposit back
*/

Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](3);

operations[0] = _getWithdrawAction(marketId, _amount);
operations[1] = _getCallAction(
abi.encode(MyCustomData({token: _token, repayAmount: repayAmount}))
);
operations[2] = _getDepositAction(marketId, repayAmount);

Account.Info[] memory accountInfos = new Account.Info[](1);
accountInfos[0] = _getAccountInfo();

solo.operate(accountInfos, operations);

}

// THis is a callback function called by dydx flash loan contract. It passes the data from above function
function callFunction(
address sender,
Account.Info memory account,
bytes memory data
) public override {
require(msg.sender == SOLO, "!solo");
require(sender == address(this), "!this contract");

MyCustomData memory mcd = abi.decode(data, (MyCustomData));
uint repayAmount = mcd.repayAmount;

uint bal = IERC20(mcd.token).balanceOf(address(this));
// Got money
// Now use it and repay

require(bal >= repayAmount, "bal < repay");

// More code here...
flashUser = sender;
}
}
// Solo margin contract mainnet - 0x1e0447b19bb6ecfdae1e4ae1694b0c3659614e4e
113 changes: 113 additions & 0 deletions contracts/Misc/FlashLoanDydx/interfaces/DydxFlashLoanBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "./ISoloMargin.sol";

contract DydxFlashloanBase {
using SafeMath for uint;

// -- Internal Helper functions -- //

function _getMarketIdFromTokenAddress(address _solo, address token)
internal
view
returns (uint)
{
ISoloMargin solo = ISoloMargin(_solo);

uint numMarkets = solo.getNumMarkets();

address curToken;
for (uint i = 0; i < numMarkets; i++) {
curToken = solo.getMarketTokenAddress(i);

if (curToken == token) {
return i;
}
}

revert("No marketId found for provided token");
}

function _getRepaymentAmountInternal(uint amount) internal pure returns (uint) {
// Needs to be overcollateralize
// Needs to provide +2 wei to be safe
return amount.add(2);
}

function _getAccountInfo() internal view returns (Account.Info memory) {
return Account.Info({owner: address(this), number: 1});
}

function _getWithdrawAction(uint marketId, uint 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, uint 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: ""
});
}
}
27 changes: 27 additions & 0 deletions contracts/Misc/FlashLoanDydx/interfaces/ICallee.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.7;

import {Account} from "./ISoloMargin.sol";

/**
* @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;
}
Loading