// Contract: VeloxSwapV2
// Target function: function adjustInputBasedOnFee(bool takeFeeFromInput, uint16 feeFactor, uint256 amountIn, uint256 amountOut, address sellerAddress) private view
 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);
    }

    /**
    *   @dev This function should ONLY be executed when algorithmic conditons are met
    *   function sellTokensForExactTokens
    *   @param seller               address
    *   @param tokenInAddress       address
    *   @param tokenOutAddress      address
    *   @param maxTokenInAmount     uint256
    *   @param tokenOutAmount       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 sellTokensForExactTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 maxTokenInAmount,
        uint256 tokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline,
        uint estimatedGasFundingCost) override onlyAdmin public {

        uint256 initialGas = gasleft();

        SwapInput memory input = SwapInput({ 
            seller: seller, tokenInAddress: tokenInAddress,
            tokenOutAddress: tokenOutAddress,
            tokenInAmount: maxTokenInAmount,
            tokenOutAmount: tokenOutAmount,
            feeFactor: feeFactor,
            takeFeeFromInput: takeFeeFromInput,
            deadline: deadline });
        
        swapTokens(input, false);
        
        uint256 gasCost = (initialGas - gasleft() + estimatedGasFundingCost) * tx.gasprice;

        fundGasCost(seller, gasCost);
    }

    /**
    *   @dev This function should ONLY be executed when algorithmic conditons are met
    *   function sellTokensForExactTokens
    *   @param seller               address
    *   @param tokenInAddress       address
    *   @param tokenOutAddress      address
    *   @param maxTokenInAmount     uint256
    *   @param tokenOutAmount       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 sellTokensForExactTokens(
        address seller,
        address tokenInAddress,
        address tokenOutAddress,
        uint256 maxTokenInAmount,
        uint256 tokenOutAmount,
        uint16 feeFactor,
        bool takeFeeFromInput,
        uint256 deadline) override onlyAdmin public {
        sellTokensForExactTokens(seller, tokenInAddress, tokenOutAddress, maxTokenInAmount, tokenOutAmount, feeFactor, takeFeeFromInput, deadline, GAS_FUNDING_ESTIMATED_GAS);
    }
    
    function swapTokens(SwapInput memory input, bool exactIn) public returns (uint256 amountOut) {
        uint256 amountInForSwap;
        uint256 amountOutForSwap;
        address swapTargetAddress;

        (amountInForSwap, amountOutForSwap, swapTargetAddress) 
            = prepareSwap(input);

        // Execute the swap
        doSwap(input.tokenInAddress, input.tokenOutAddress, amountInForSwap, amountOutForSwap, swapTargetAddress, input.deadline, exactIn);

        amountOut = amountOutForSwap;

        // Take the fee from the output if not taken from the input
        if (!input.takeFeeFromInput) {
            amountOut = takeOutputFee(amountOutForSwap, input.feeFactor, input.tokenOutAddress, input.seller);
        }

        emit ValueSwapped(input.seller, input.tokenInAddress, input.tokenOutAddress, input.tokenInAmount, amountOut);
    }

    function prepareSwap(SwapInput memory input) private returns (uint256 amountInForSwap, uint256 amountOurForSwap, address targetAddress) {

        // Sanity checks
        validateInput(input.seller, input.tokenInAddress, input.tokenOutAddress, input.tokenInAmount, input.tokenOutAmount, input.feeFactor, input.deadline);

        // Be 100% sure there's available allowance in this token contract
        Exception exception = doTransferIn(input.tokenInAddress, input.seller, input.tokenInAmount);
        require(exception == Exception.NO_ERROR, 'VELOXSWAP: ALLOWANCE_TOO_LOW');

        // Checking In/Out reserves
        checkLiquidity(input.tokenInAddress, input.tokenOutAddress, input.tokenOutAmount);

        // Fee
        (amountInForSwap, amountOurForSwap, targetAddress) = adjustInputBasedOnFee(input.takeFeeFromInput, input.feeFactor, input.tokenInAmount, input.tokenOutAmount, input.seller);
    }

    function validateInput(address seller, address tokenInAddress, address tokenOutAddress, uint256 tokenInAmount, uint256 tokenOutAmount, uint16 feeFactor, uint256 deadline) private view {
        require(deadline >= block.timestamp, 'VELOXSWAP: EXPIRED');
        require(feeFactor <= 30, 'VELOXSWAP: FEE_OVER_03_PERCENT');
        require(address(router) != address(0), 'VELOXSWAP: ROUTER_NOT_INSTANTIATED');

        address tokenWETH = router.WETH();
        require(tokenWETH != address(0), 'VELOXSWAP: WETH_ADDRESS_NOT_FOUND');

        require (seller != address(0) &&
                tokenInAddress != address(0) &&
                tokenOutAddress != address(0) &&
                tokenInAmount > 0 &&
                tokenOutAmount > 0,
        'VELOXSWAP: ZERO_DETECTED');

        // For now we only work with WETH/TOKEN pairs
        require(tokenInAddress == tokenWETH || tokenOutAddress == tokenWETH, 'VELOXSWAP: INVALID_PATH');
    }