contract oCurveFlashLiquidate is ReentrancyGuard, Ownable, Structs {
  using SafeERC20 for IERC20;
  using Address for address;
  using SafeMath for uint256;

  address public dydx;
  address public weth;
  address payable public _oToken;
  address payable public _vault;
  uint256 public _amount;

  constructor () public {
    dydx = address(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
    weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
  }

  function liquidate(uint256 amount, address payable vault, address payable oToken) public {
    _vault = vault;
    _amount = amount;
    _oToken = oToken;

    Info[] memory infos = new Info[](1);
    ActionArgs[] memory args = new ActionArgs[](3);

    infos[0] = Info(address(this), 0);

    AssetAmount memory wamt = AssetAmount(false, AssetDenomination.Wei, AssetReference.Delta, amount);
    ActionArgs memory withdraw;
    withdraw.actionType = ActionType.Withdraw;
    withdraw.accountId = 0;
    withdraw.amount = wamt;
    withdraw.primaryMarketId = 0;
    withdraw.otherAddress = address(this);

    args[0] = withdraw;

    ActionArgs memory call;
    call.actionType = ActionType.Call;
    call.accountId = 0;
    call.otherAddress = address(this);

    args[1] = call;

    ActionArgs memory deposit;
    AssetAmount memory damt = AssetAmount(true, AssetDenomination.Wei, AssetReference.Delta, amount.add(1));
    deposit.actionType = ActionType.Deposit;
    deposit.accountId = 0;
    deposit.amount = damt;
    deposit.primaryMarketId = 0;
    deposit.otherAddress = address(this);

    args[2] = deposit;

    DyDx(dydx).operate(infos, args);
  }

  function callFunction(
      address sender,
      Info memory accountInfo,
      bytes memory data
  ) public {
    OptionsContract oToken = OptionsContract(_oToken);
    require(oToken.isUnsafe(_vault), 'cannot liquidate a safe vault');

    WETH(weth).withdraw(_amount);

    uint256 tokensPerETH = oToken.maxOTokensIssuable(1e18);
    uint256 maxToLiquidate = oToken.maxOTokensLiquidatable(_vault); //100 * 1e15
    uint256 ethRequired = maxToLiquidate.mul(1e18).div(tokensPerETH);
    if (oToken.hasVault(address(this))) {
      oToken.addETHCollateralOption.value(ethRequired)(maxToLiquidate, address(this));
    } else {
      oToken.createETHCollateralOption.value(ethRequired)(maxToLiquidate, address(this));
    }
    oToken.liquidate(_vault, maxToLiquidate);

    WETH(weth).deposit.value(_amount)();
  }

  function() external payable {

  }

  // incase of half-way error
  function inCaseTokenGetsStuck(IERC20 _TokenAddress) onlyOwner public {
      uint qty = _TokenAddress.balanceOf(address(this));
      _TokenAddress.safeTransfer(msg.sender, qty);
  }

  // incase of half-way error
  function inCaseETHGetsStuck() onlyOwner public{
      (bool result, ) = msg.sender.call.value(address(this).balance)("");
      require(result, "transfer of ETH failed");
  }
}
function inCaseETHGetsStuck() onlyOwner public{
    (bool result, ) = msg.sender.call.value(address(this).balance)("");
    require(result, "transfer of ETH failed");
}

// incase of half-way error