Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
pragma solidity =0.5.17;
pragma experimental ABIEncoderV2;
import "./DydxFlashloanBase.sol";
contract FlashSwap is DydxFlashloanBase {
using SafeMath for uint;
address internal constant _solo = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address internal constant router = 0x72e5B0cb1099E664e044A7fD4185d3aF8965E095;
function flashSwap(uint value, address payable currency, address token) external payable returns(uint amount) {
require(value > 2, "require value > 2");
if(currency == WETH && msg.value >= value)
WETH9(currency).deposit.value(msg.value)();
else
TransferHelper.safeTransferFrom(currency, msg.sender, address(this), value);
uint _amount = value.mul(10000-199).sub(10000*2).div(199);
// Get marketId from token address
uint256 marketId = _getMarketIdFromTokenAddress(_solo, currency);
// Calculate repay amount (_amount + (2 wei))
// Approve transfer from
uint256 repayAmount = _getRepaymentAmountInternal(_amount);
IERC20(currency).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(currency, token, repayAmount));
operations[2] = _getDepositAction(marketId, repayAmount);
Account.Info[] memory accountInfos = new Account.Info[](1);
accountInfos[0] = _getAccountInfo();
ISoloMargin(_solo).operate(accountInfos, operations);
address limitedToken = IQFRouter(router).buyback(token);
address quotaToken = IQFRouter(router).quotaTokenOf(limitedToken);
IQFRouter(router).claim(limitedToken, currency);
amount = IERC20(quotaToken).balanceOf(address(this));
IERC20(quotaToken).transfer(msg.sender, amount);
}
function callFunction(address sender, Account.Info calldata account, bytes calldata data) external {
require(msg.sender == _solo && sender == address(this) && account.owner == address(this) && account.number == 1, "callFunction param check fail");
address[] memory path = new address[](2);
uint repayAmount;
(path[0], path[1], repayAmount) = abi.decode(data, (address, address, uint));
uint amount = IERC20(path[0]).balanceOf(address(this));
TransferHelper.safeApprove(path[0], router, amount);
amount = IQFRouter(router).swapExactTokensForTokens(amount, 0, path, address(this), now)[1];
(path[0], path[1]) = (path[1], path[0]);
TransferHelper.safeApprove(path[0], router, amount);
amount = IQFRouter(router).swapExactTokensForTokens(amount, 0, path, address(this), now)[1];
require(amount >= repayAmount, "Not enough funds to repay dydx loan!");
}
}
interface IQFRouter {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function quotaTokenOf(address) external view returns(address);
function buyback(address) external view returns(address);
function claim(address limitedToken, address taxToken) external;
}
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
interface WETH9 {
function deposit() external payable;
function withdraw(uint wad) external;
}
// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
library SafeMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x, "add-overflow");
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, "sub-underflow");
}
function sub0(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : 0;
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x, "mul-overflow");
}
function div(uint x, uint y) internal pure returns (uint z) {
return x / y;
}
function div1(uint256 a, uint256 b) internal pure returns (uint256) {
return b == 0 ? a : a/b;
}
}
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED");
}
function safeTransfer(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
}
function safeTransferETH(address to, uint value) internal {
(bool success,) = to.call.value(value)(new bytes(0)); // (bool success,) = to.call{value:value}(new bytes(0));
require(success, "ETH_TRANSFER_FAILED");
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
pragma solidity =0.5.17;
pragma experimental ABIEncoderV2;
//import "@openzeppelin/contracts/math/SafeMath.sol";
//import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ISoloMargin.sol";
contract DydxFlashloanBase {
//using SafeMath for uint256;
// -- Internal Helper functions -- //
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 _getRepaymentAmountInternal(uint256 amount)
internal
pure
returns (uint256)
{
// Needs to be overcollateralize
// Needs to provide +2 wei to be safe
require(amount + 2 > amount, "+2 overflow");
return amount + 2;
}
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: ""
});
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
pragma solidity =0.5.17;
pragma experimental ABIEncoderV2;
library Account {
enum Status {Normal, Liquid, Vapor}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Storage {
mapping(uint256 => Types.Par) balances; // Mapping from marketId to principal
Status status;
}
}
library 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
}
enum AccountLayout {OnePrimary, TwoPrimary, PrimaryAndSecondary}
enum MarketLayout {ZeroMarkets, OneMarket, TwoMarkets}
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 TransferArgs {
Types.AssetAmount amount;
Account.Info accountOne;
Account.Info accountTwo;
uint256 market;
}
struct BuyArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 makerMarket;
uint256 takerMarket;
address exchangeWrapper;
bytes orderData;
}
struct SellArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 takerMarket;
uint256 makerMarket;
address exchangeWrapper;
bytes orderData;
}
struct TradeArgs {
Types.AssetAmount amount;
Account.Info takerAccount;
Account.Info makerAccount;
uint256 inputMarket;
uint256 outputMarket;
address autoTrader;
bytes tradeData;
}
struct LiquidateArgs {
Types.AssetAmount amount;
Account.Info solidAccount;
Account.Info liquidAccount;
uint256 owedMarket;
uint256 heldMarket;
}
struct VaporizeArgs {
Types.AssetAmount amount;
Account.Info solidAccount;
Account.Info vaporAccount;
uint256 owedMarket;
uint256 heldMarket;
}
struct CallArgs {
Account.Info account;
address callee;
bytes data;
}
}
library Decimal {
struct D256 {
uint256 value;
}
}
library Interest {
struct Rate {
uint256 value;
}
struct Index {
uint96 borrow;
uint96 supply;
uint32 lastUpdate;
}
}
library Monetary {
struct Price {
uint256 value;
}
struct Value {
uint256 value;
}
}
library Storage {
// All information necessary for tracking a market
struct Market {
// Contract address of the associated ERC20 token
address token;
// Total aggregated supply and borrow amount of the entire market
Types.TotalPar totalPar;
// Interest index of the market
Interest.Index index;
// Contract address of the price oracle for this market
address priceOracle;
// Contract address of the interest setter for this market
address interestSetter;
// Multiplier on the marginRatio for this market
Decimal.D256 marginPremium;
// Multiplier on the liquidationSpread for this market
Decimal.D256 spreadPremium;
// Whether additional borrows are allowed for this market
bool isClosing;
}
// The global risk parameters that govern the health and security of the system
struct RiskParams {
// Required ratio of over-collateralization
Decimal.D256 marginRatio;
// Percentage penalty incurred by liquidated accounts
Decimal.D256 liquidationSpread;
// Percentage of the borrower's interest fee that gets passed to the suppliers
Decimal.D256 earningsRate;
// The minimum absolute borrow value of an account
// There must be sufficient incentivize to liquidate undercollateralized accounts
Monetary.Value minBorrowedValue;
}
// The maximum RiskParam values that can be set
struct RiskLimits {
uint64 marginRatioMax;
uint64 liquidationSpreadMax;
uint64 earningsRateMax;
uint64 marginPremiumMax;
uint64 spreadPremiumMax;
uint128 minBorrowedValueMax;
}
// The entire storage state of Solo
struct State {
// number of markets
uint256 numMarkets;
// marketId => Market
mapping(uint256 => Market) markets;
// owner => account number => Account
mapping(address => mapping(uint256 => Account.Storage)) accounts;
// Addresses that can control other users accounts
mapping(address => mapping(address => bool)) operators;
// Addresses that can control all users accounts
mapping(address => bool) globalOperators;
// mutable risk parameters of the system
RiskParams riskParams;
// immutable risk limits of the system
RiskLimits riskLimits;
}
}
library 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 TotalPar {
uint128 borrow;
uint128 supply;
}
struct Par {
bool sign; // true if positive
uint128 value;
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
interface ISoloMargin { //
struct OperatorArg {
address operator;
bool trusted;
}
function ownerSetSpreadPremium(
uint256 marketId,
Decimal.D256 calldata spreadPremium
) external;
function getIsGlobalOperator(address operator) external view returns (bool);
function getMarketTokenAddress(uint256 marketId)
external
view
returns (address);
function ownerSetInterestSetter(uint256 marketId, address interestSetter)
external;
function getAccountValues(Account.Info calldata account)
external
view
returns (Monetary.Value memory, Monetary.Value memory);
function getMarketPriceOracle(uint256 marketId)
external
view
returns (address);
function getMarketInterestSetter(uint256 marketId)
external
view
returns (address);
function getMarketSpreadPremium(uint256 marketId)
external
view
returns (Decimal.D256 memory);
function getNumMarkets() external view returns (uint256);
function ownerWithdrawUnsupportedTokens(address token, address recipient)
external
returns (uint256);
function ownerSetMinBorrowedValue(Monetary.Value calldata minBorrowedValue)
external;
function ownerSetLiquidationSpread(Decimal.D256 calldata spread) external;
function ownerSetEarningsRate(Decimal.D256 calldata earningsRate) external;
function getIsLocalOperator(address owner, address operator)
external
view
returns (bool);
function getAccountPar(Account.Info calldata account, uint256 marketId)
external
view
returns (Types.Par memory);
function ownerSetMarginPremium(
uint256 marketId,
Decimal.D256 calldata marginPremium
) external;
function getMarginRatio() external view returns (Decimal.D256 memory);
function getMarketCurrentIndex(uint256 marketId)
external
view
returns (Interest.Index memory);
function getMarketIsClosing(uint256 marketId) external view returns (bool);
function getRiskParams() external view returns (Storage.RiskParams memory);
function getAccountBalances(Account.Info calldata account)
external
view
returns (address[] memory, Types.Par[] memory, Types.Wei[] memory);
function renounceOwnership() external;
function getMinBorrowedValue() external view returns (Monetary.Value memory);
function setOperators(OperatorArg[] calldata args) external;
function getMarketPrice(uint256 marketId) external view returns (address);
function owner() external view returns (address);
function isOwner() external view returns (bool);
function ownerWithdrawExcessTokens(uint256 marketId, address recipient)
external
returns (uint256);
function ownerAddMarket(
address token,
address priceOracle,
address interestSetter,
Decimal.D256 calldata marginPremium,
Decimal.D256 calldata spreadPremium
) external;
function operate(
Account.Info[] calldata accounts,
Actions.ActionArgs[] calldata actions
) external;
function getMarketWithInfo(uint256 marketId)
external
view
returns (
Storage.Market memory,
Interest.Index memory,
Monetary.Price memory,
Interest.Rate memory
);
function ownerSetMarginRatio(Decimal.D256 calldata ratio) external;
function getLiquidationSpread() external view returns (Decimal.D256 memory);
function getAccountWei(Account.Info calldata account, uint256 marketId)
external
view
returns (Types.Wei memory);
function getMarketTotalPar(uint256 marketId)
external
view
returns (Types.TotalPar memory);
function getLiquidationSpreadForPair(
uint256 heldMarketId,
uint256 owedMarketId
) external view returns (Decimal.D256 memory);
function getNumExcessTokens(uint256 marketId)
external
view
returns (Types.Wei memory);
function getMarketCachedIndex(uint256 marketId)
external
view
returns (Interest.Index memory);
function getAccountStatus(Account.Info calldata account)
external
view
returns (uint8);
function getEarningsRate() external view returns (Decimal.D256 memory);
function ownerSetPriceOracle(uint256 marketId, address priceOracle) external;
function getRiskLimits() external view returns (Storage.RiskLimits memory);
function getMarket(uint256 marketId)
external
view
returns (Storage.Market memory);
function ownerSetIsClosing(uint256 marketId, bool isClosing) external;
function ownerSetGlobalOperator(address operator, bool approved) external;
function transferOwnership(address newOwner) external;
function getAdjustedAccountValues(Account.Info calldata account)
external
view
returns (Monetary.Value memory, Monetary.Value memory);
function getMarketMarginPremium(uint256 marketId)
external
view
returns (Decimal.D256 memory);
function getMarketInterestRate(uint256 marketId)
external
view
returns (Interest.Rate memory);
}