// Contract: VeloxSwapV2
// Target function: function sellTokensForExactTokens(
_AMOUNT_CALCULATION_FAILED,
        LIQUIDATE_NEW_BORROW_INDEX_CALCULATION_FAILED_BORROWED_ASSET,
        LIQUIDATE_NEW_BORROW_INDEX_CALCULATION_FAILED_COLLATERAL_ASSET,
        LIQUIDATE_NEW_BORROW_RATE_CALCULATION_FAILED_BORROWED_ASSET,
        LIQUIDATE_NEW_SUPPLY_INDEX_CALCULATION_FAILED_BORROWED_ASSET,
        LIQUIDATE_NEW_SUPPLY_INDEX_CALCULATION_FAILED_COLLATERAL_ASSET,
        LIQUIDATE_NEW_SUPPLY_RATE_CALCULATION_FAILED_BORROWED_ASSET,
        LIQUIDATE_NEW_TOTAL_BORROW_CALCULATION_FAILED_BORROWED_ASSET,
        LIQUIDATE_NEW_TOTAL_CASH_CALCULATION_FAILED_BORROWED_ASSET,
        LIQUIDATE_NEW_TOTAL_SUPPLY_BALANCE_CALCULATION_FAILED_BORROWER_COLLATERAL_ASSET,
        LIQUIDATE_NEW_TOTAL_SUPPLY_BALANCE_CALCULATION_FAILED_LIQUIDATOR_COLLATERAL_ASSET,
        LIQUIDATE_FETCH_ASSET_PRICE_FAILED,
        LIQUIDATE_TRANSFER_IN_FAILED,
        LIQUIDATE_TRANSFER_IN_NOT_POSSIBLE,
        REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_CONTRACT_PAUSED,
        REPAY_BORROW_NEW_BORROW_INDEX_CALCULATION_FAILED,
        REPAY_BORROW_NEW_BORROW_RATE_CALCULATION_FAILED,
        REPAY_BORROW_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
        REPAY_BORROW_NEW_SUPPLY_RATE_CALCULATION_FAILED,
        REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_NEW_TOTAL_BORROW_CALCULATION_FAILED,
        REPAY_BORROW_NEW_TOTAL_CASH_CALCULATION_FAILED,
        REPAY_BORROW_TRANSFER_IN_FAILED,
        REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
        SET_ASSET_PRICE_CHECK_ORACLE,
        SET_MARKET_INTEREST_RATE_MODEL_OWNER_CHECK,
        SET_ORACLE_OWNER_CHECK,
        SET_ORIGINATION_FEE_OWNER_CHECK,
        SET_PAUSED_OWNER_CHECK,
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_RISK_PARAMETERS_OWNER_CHECK,
        SET_RISK_PARAMETERS_VALIDATION,
        SUPPLY_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        SUPPLY_CONTRACT_PAUSED,
        SUPPLY_MARKET_NOT_SUPPORTED,
        SUPPLY_NEW_BORROW_INDEX_CALCULATION_FAILED,
        SUPPLY_NEW_BORROW_RATE_CALCULATION_FAILED,
        SUPPLY_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
        SUPPLY_NEW_SUPPLY_RATE_CALCULATION_FAILED,
        SUPPLY_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        SUPPLY_NEW_TOTAL_CASH_CALCULATION_FAILED,
        SUPPLY_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
        SUPPLY_TRANSFER_IN_FAILED,
        SUPPLY_TRANSFER_IN_NOT_POSSIBLE,
        SUPPORT_MARKET_FETCH_PRICE_FAILED,
        SUPPORT_MARKET_OWNER_CHECK,
        SUPPORT_MARKET_PRICE_CHECK,
        SUSPEND_MARKET_OWNER_CHECK,
        WITHDRAW_ACCOUNT_LIQUIDITY_CALCULATION_FAILED,
        WITHDRAW_ACCOUNT_SHORTFALL_PRESENT,
        WITHDRAW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        WITHDRAW_AMOUNT_LIQUIDITY_SHORTFALL,
        WITHDRAW_AMOUNT_VALUE_CALCULATION_FAILED,
        WITHDRAW_CAPACITY_CALCULATION_FAILED,
        WITHDRAW_CONTRACT_PAUSED,
        WITHDRAW_NEW_BORROW_INDEX_CALCULATION_FAILED,
        WITHDRAW_NEW_BORROW_RATE_CALCULATION_FAILED,
        WITHDRAW_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
        WITHDRAW_NEW_SUPPLY_RATE_CALCULATION_FAILED,
        WITHDRAW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        WITHDRAW_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
        WITHDRAW_TRANSFER_OUT_FAILED,
        WITHDRAW_TRANSFER_OUT_NOT_POSSIBLE
    }

    /**
      * @dev report a known exception
      */
    function raiseException(Exception exception, Reason reason) internal returns (uint) {
        emit SwapException(uint(exception), uint(reason), 0);
        return uint(exception);
    }

    /**
      * @dev report an opaque error from an upgradeable collaborator contract
      */
    function raiseGenericException(Reason reason, uint genericException) internal returns (uint) {
        emit SwapException(uint(Exception.GENERIC_ERROR), uint(reason), genericException);
        return uint(Exception.GENERIC_ERROR);
    }

}

pragma solidity 0.8.0;




/**
  * @title Swappable Interface
  */
contract Swappable is SwapExceptions {
    /**
      * @dev Checks whether or not there is sufficient allowance for this contract to move amount from `from` and
      *      whether or not `from` has a balance of at least `amount`. Does NOT do a transfer.
      */
    function checkTransferIn(address asset, address from, uint amount) internal view returns (Exception) {

        IERC20 token = IERC20(asset);

        if (token.allowance(from, address(this)) < amount) {
            return Exception.TOKEN_INSUFFICIENT_ALLOWANCE;
        }

        if (token.balanceOf(from) < amount) {
            return Exception.TOKEN_INSUFFICIENT_BALANCE;
        }

        return Exception.NO_ERROR;
    }

    /**
      *  @dev This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
      *  See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
      */
    function doTransferIn(address asset, address from, uint amount) internal returns (Exception) {
        IERC20NONStandard token = IERC20NONStandard(asset);
        bool result;
        // Should we use Helper.safeTransferFrom?
        require(token.allowance(from, address(this)) >= amount, 'Not enough allowance from client');
        token.transferFrom(from, address(this), amount);

        assembly {
            switch returndatasize()
                case 0 {                      // This is a non-standard ERC-20
                    result := not(0)          // set result to true
                }
                case 32 {                     // This is a complaint ERC-20
                    returndatacopy(0, 0, 32)
                    result := mload(0)        // Set `result = returndata` of external call
                }
                default {                     // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }

        if (!result) {
            return Exception.TOKEN_TRANSFER_FAILED;
        }

        return Exception.NO_ERROR;
    }

    /**
      * @dev Checks balance of this contract in asset
      */
    function getCash(address asset) internal view returns (uint) {
        IERC20 token = IERC20(asset);
        return token.balanceOf(address(this));
    }

    /**
      * @dev Checks balance of `from` in `asset`
      */
    function getBalanceOf(address asset, address from) internal view returns (uint) {
        IERC20 token = IERC20(asset);
        return token.balanceOf(from);
    }

    /**
      * @dev Similar to EIP20 transfer, except it handles a False result from `transfer` and returns an explanatory
      *      error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
      *      insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
      *      it is >= amount, this should not revert in normal conditions.
      *
      *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
      *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
      */
    function doTransferOut(address asset, address to, uint amount) internal returns (Exception) {
        IERC20NONStandard token = IERC20NONStandard(asset);
        bool result;
        token.transfer(to, amount);

        assembly {
            switch returndatasize()
                case 0 {                      // This is a non-standard ERC-20
                    result := not(0)          // set result to true
                }
                case 32 {                     // This is a complaint ERC-20
                    returndatacopy(0, 0, 32)
                    result := mload(0)        // Set `result = returndata` of external call
                }
                default {                     // This is an excessively non-compliant ERC-20, revert.
                    revert(0, 0)
                }
        }

        if (!result) {
            return Exception.TOKEN_TRANSFER_OUT_FAILED;
        }

        return Exception.NO_ERROR;
    }
}

pragma solidity 0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return payable(msg.sender);
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

pragma solidity >=0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

pragma solidity >=0.8.0;

interface IVeloxSwap {

    function withdrawToken(address token, uint256 amount) external;
    
    function withdrawETH(uint256 amount) external;

    function sellExactTokensForTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 tokenInAmount,
        uint256 minTokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline) external returns (uint256 amountOut);

    function sellExactTokensForTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 tokenInAmount,
        uint256 minTokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline,
        uint estimatedGasFundingCost) external returns (uint256 amountOut);

    function sellTokensForExactTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 maxTokenInAmount,
        uint256 tokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline) external;

    function sellTokensForExactTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 maxTokenInAmount,
        uint256 tokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline,
        uint estimatedGasFundingCost) external;

    function fundGasCost(address seller, uint256 wethAmount) external;

}

pragma solidity >=0.8.0;

abstract contract BackingStore {
    address public MAIN_CONTRACT;
    address public UNISWAP_FACTORY_ADDRESS = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    address public UNISWAP_ROUTER_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address public ADMIN_ADDRESS;
}

pragma solidity 0.8.0;












/**
* @title VeloxSwap based on algorithmic conditional trading exeuctions
*/
contract VeloxSwapV2 is BackingStore, Ownable, Swappable, IVeloxSwap {

    /**
        * @dev Throws if called by any account other than the admin.
        */
    modifier onlyAdmin() {
        require(ADMIN_ADDRESS == msg.sender, "VELOXSWAP: NOT_ADMIN");
        _;
    }

    struct SwapInput {
        address seller;
        address tokenInAddress;
        address tokenOutAddress;
        uint256 tokenInAmount;
        uint256 tokenOutAmount;
        uint16 feeFactor;
        bool takeFeeFromInput;
        uint256 deadline;
    }

    uint constant FEE_SCALE = 10000;
    uint constant GAS_FUNDING_ESTIMATED_GAS = 26233;

    IUniswapV2Router02 public immutable router;

    constructor() {
        router = IUniswapV2Router02(UNISWAP_ROUTER_ADDRESS);
    }

    event ValueSwapped(address indexed seller, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut);
    event GasFunded(address indexed seller, uint256 gasCost);

    function withdrawToken(address token, uint256 amount) onlyOwner override external {
        VeloxTransferHelper.safeTransfer(token, msg.sender, amount);
    }

    function withdrawETH(uint256 amount) onlyOwner override external {
        VeloxTransferHelper.safeTransferETH(msg.sender, amount);
    }

    function fundGasCost(address seller, uint256 wethAmount) onlyAdmin override public {
        VeloxTransferHelper.safeTransferFrom(router.WETH(), seller, ADMIN_ADDRESS, wethAmount);
        
        emit GasFunded(seller, wethAmount);
    }

    /**
    *   @dev This function should ONLY be executed when algorithmic conditons are met
    *   function sellExactTokensForTokens
    *   @param seller               address
    *   @param tokenInAddress       address
    *   @param tokenOutAddress      address
    *   @param tokenInAmount        uint256
    *   @param minTokenOutAmount    uint256
    *   @param feeFactor            uint - 1/10000 fraction of the amount, i.e. feeFactor of 1 means 0.01% fee
    *   @param takeFeeFromInput     bool
    *   @param deadline             uint256 - UNIX timestamp
    *   @param estimatedGasFundingCost uint - estimated gas for gas funding transaction
    */
    function sellExactTokensForTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 tokenInAmount,
        uint256 minTokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline,
        uint estimatedGasFundingCost
    ) override onlyAdmin public returns (uint256 amountOut) {
        uint256 initialGas = gasleft();

        SwapInput memory input = SwapInput({ 
            seller: seller, tokenInAddress: tokenInAddress,
            tokenOutAddress: tokenOutAddress,
            tokenInAmount: tokenInAmount,
            tokenOutAmount: minTokenOutAmount,
            feeFactor: feeFactor,
            takeFeeFromInput: takeFeeFromInput,
            deadline: deadline });
        
        amountOut = swapTokens(input, true);
        
        uint256 gasCost = (initialGas - gasleft() + estimatedGasFundingCost) * tx.gasprice;

        fundGasCost(seller, gasCost);
    }

    /**
    *   @dev This function should ONLY be executed when algorithmic conditons are met
    *   function sellExactTokensForTokens
    *   @param seller               address
    *   @param tokenInAddress       address
    *   @param tokenOutAddress      address
    *   @param tokenInAmount        uint256
    *   @param minTokenOutAmount    uint256
    *   @param feeFactor            uint - 1/10000 fraction of the amount, i.e. feeFactor of 1 means 0.01% fee
    *   @param takeFeeFromInput     bool
    *   @param deadline             uint256 - UNIX timestamp
    */
    function sellExactTokensForTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 tokenInAmount,
        uint256 minTokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline
    ) override onlyAdmin public returns (uint256 amountOut) {
        amountOut = sellExactTokensForTokens(seller, tokenInAddress, tokenOutAddress, tokenInAmount, minTokenOutAmount, feeFactor, takeFeeFromInput, deadline, GAS_FUNDING_ESTIMATED_GAS);
    }