// File: Bitxchange.sol



// SPDX-License-Identifier: MIT
//
// No-gas required token swapping to ETH.
// https://xc.bitx.cx
//
// Keeps gas cost and fee
// 90% fee sent to $bitx stakers
// $Bitx: 0xD150e07f602bf3239BE3DE4341E10BE1678a3f8b
//
// Buy & Stake: https://token.bitx.cx
//
// Developed by @Rotwang9000 for https://Bitx.cx
// https://t.me/BitXcx
//
// Register for Airdrop: https://t.me/BitxLiveBot

pragma solidity ^0.8.20;


import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

interface IRewardPot {
    function addToRewardPot() external payable;
}

contract Bitxchange is Ownable {
    IUniswapV2Router02 public uniswapRouter;
    IRewardPot public rewardPot;
    uint256 public feePercentage = 750; // 0.75% fee, scaled by 10^4
    uint256 public estimatedGasForSwap = 250000;
    address public relayer;

    event FeeUpdated(uint256 newFee);
    event RewardPotUpdated(address newRewardPot);
    event SwapSuccessful(address indexed from, uint256 receivedETH);

    constructor(
        address _uniswapRouter,
        address _rewardPot,
        address _relayer
    ) Ownable(msg.sender) {
        uniswapRouter = IUniswapV2Router02(_uniswapRouter);
        rewardPot = IRewardPot(_rewardPot);
        relayer = _relayer;
    }

    // Payable function
    function receiveFunds() public payable {}

    receive() external payable {}

    // Payable fallback function (Solidity 0.6.0 and later)
    fallback() external payable {}

    // Owner deposits ETH into the contract
    function depositETH() external payable {}

    modifier onlyRelayer() {
        require(
            msg.sender == relayer || msg.sender == owner(),
            "Not the relayer"
        );
        _;
    }

	function uniswapAddress() public view returns(address){
		return address(uniswapRouter);
	}

    // Owner can withdraw ETH
    function withdrawETH(uint256 amount) external onlyOwner {
        payable(owner()).transfer(amount);
    }

    function setRelayer(address _newRelayer) external onlyOwner {
        relayer = _newRelayer;
    }

    function setFeePercentage(uint256 _newFee) external onlyOwner {
        feePercentage = _newFee;
        emit FeeUpdated(_newFee);
    }

    function setEstimatedGasForSwap(
        uint256 _newEstimatedGasForSwap
    ) external onlyOwner {
        estimatedGasForSwap = _newEstimatedGasForSwap;
    }

    function setUniswapRouter(address _newRouter) external onlyOwner {
        require(_newRouter != address(0), "Invalid address");
        uniswapRouter = IUniswapV2Router02(_newRouter);
    }

    function setRewardPot(address _newRewardPot) external onlyOwner {
        require(_newRewardPot != address(0), "Invalid address");
        rewardPot = IRewardPot(_newRewardPot);
        emit RewardPotUpdated(_newRewardPot);
    }

    function rescueTokens(
        address token,
        address to,
        uint256 amount
    ) external onlyRelayer {
        uint256 balance = IERC20(token).balanceOf(address(this));
        require(amount <= balance, "Not enough tokens to rescue");
        require(IERC20(token).transfer(to, amount), "Transfer failed");
    }

    function tryGetAmountsOut(
        uint amountIn,
        address[] memory path
    ) internal view returns (bool success, uint256 amountOut) {
        try uniswapRouter.getAmountsOut(amountIn, path) returns (
            uint256[] memory amounts
        ) {
            return (true, amounts[1]);
        } catch {
            return (false, 0);
        }
    }

    // Function to check if a swap is possible
    function canSwap(
        address token,
        uint256 amountIn,
        uint256 userGasPrice
    ) public view returns (bool) {
        address[] memory path = new address[](2);
        path[0] = token;
        path[1] = uniswapRouter.WETH();

        // Check if there's enough liquidity for the swap
        (bool success, uint256 amountOut) = tryGetAmountsOut(amountIn, path);
        if (!success || amountOut == 0) {
            return false;
        }

        // Estimate gas cost and check against the minimum output amount
        uint256 estimatedGasCost = estimatedGasForSwap * userGasPrice;
        if (estimatedGasCost >= amountOut) {
            return false;
        }

        return true;
    }

    function getSwapQuote(
        address token,
        uint256 amount,
        uint256 slippage,
        uint256 userGasPrice
    )
        public
        view
        returns (uint256 minAmountOut, uint256 estimatedGasCost, bool canDoSwap)
    {
        address[] memory path = new address[](2);
        path[0] = token;
        path[1] = uniswapRouter.WETH();

        // Get the expected output amount based on the input amount and path
        (bool success, uint256 expectedAmountOut) = tryGetAmountsOut(
            amount,
            path
        );
        require(success, "No liquidity for this token");

        // Apply slippage
        minAmountOut = (expectedAmountOut * (10000 - slippage)) / 10000;

        // Benchmark gas for Uniswap swap: 184,523 (from Ethereum.org)
        // Adding a 20% buffer for other operations and fluctuations: ~221,428
        //uint256 estimatedGasForSwap = 221428;

        // Calculate estimated gas cost based on user-provided gas price
        estimatedGasCost = estimatedGasForSwap * userGasPrice;

        return (
            minAmountOut,
            estimatedGasCost,
            canSwap(token, amount, userGasPrice)
        );
    }

    function swapTokensWithPermit(
        address token,
        address from,
        address to,
        uint256 amount,
        uint256 minAmountOut,
        uint expectedAmountOut,
        uint unpaidFees,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public payable onlyRelayer {
        IERC20Permit(token).permit(
            from,
            address(this),
            amount,
            deadline,
            v,
            r,
            s
        );

        // Perform the swap
        return
            swapTokensWithTransfer(
                token,
                from,
                to,
                amount,
                minAmountOut,
                expectedAmountOut,
                unpaidFees
            );
    }

    function swapTokensWithTransfer(
        address token,
        address from,
        address to,
        uint256 amount,
        uint256 minAmountOut,
        uint expectedAmountOut,
        uint unpaidFees
    ) public payable onlyRelayer {
        require(
            IERC20(token).transferFrom(from, address(this), amount),
            "Transfer Failed"
        );

        return
            swapTokensForETH(
                token,
                to,
                amount,
                minAmountOut,
                expectedAmountOut,
                unpaidFees
            );
    }

    function swapTokensForETH(
        address token,
        address to,
        uint256 amount,
        uint256 minAmountOut,
        uint expectedAmountOut,
        uint unpaidFees
    ) public payable onlyRelayer {
        uint256 gasCost = gasleft();

        // Estimate gas cost and check against minAmountOut
        uint256 estimatedGasCost = estimatedGasForSwap * tx.gasprice;
        require(
            estimatedGasCost + unpaidFees < minAmountOut,
            "Estimated gas cost exceeds minAmountOut"
        );
        require(
            expectedAmountOut >= minAmountOut,
            "Expected amount less than minimum requested"
        );

        address[] memory path = new address[](2);
        path[0] = token;
        path[1] = uniswapRouter.WETH();

        // Check tokens are in the contract
        require(
            IERC20(token).balanceOf(address(this)) >= amount,
            "Token not in contract"
        );

        // Perform the swap
        IERC20(token).approve(address(uniswapRouter), amount);

        uint256[] memory amounts = uniswapRouter.swapExactTokensForETH(
            amount,
            minAmountOut,
            path,
            address(this),
            block.timestamp
        );
        gasCost = ((gasCost - gasleft()) + 100000) * tx.gasprice; // including extra for the transfers after this
        gasCost = gasCost + unpaidFees;
        uint receivedETH = amounts[1];
        if (gasCost > receivedETH) {
            payable(msg.sender).transfer(gasCost);
        } else {
            payable(msg.sender).transfer(gasCost);

            receivedETH = amounts[1] - gasCost;
            // Calculate the fee-free zone and the excess amount
            uint256 feeFreeZone = expectedAmountOut > minAmountOut
                ? expectedAmountOut - minAmountOut
                : 0;
            uint256 excessAmount = receivedETH > expectedAmountOut
                ? receivedETH - expectedAmountOut
                : 0;

            // Calculate and send the fee
            uint256 totalFee = (((receivedETH - feeFreeZone - excessAmount) *
                feePercentage) / 10000) + (excessAmount * 40) / 100; // 40% of the excess amount

            // Send 90% of the total fee to the reward pot
            rewardPot.addToRewardPot{value: (totalFee * 90) / 100}();

            // The remaining fee stays in the contract

            // Send the remaining ETH back to the original sender
            payable(to).transfer(receivedETH - totalFee);
        }

        emit SwapSuccessful(to, receivedETH);
    }

    function swapTokensForETH_UserPaysGas_withPermit(
        address token,
        uint256 amount,
        uint256 minAmountOut,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable {
		if(deadline > block.timestamp){
			IERC20Permit(token).permit(
				msg.sender,
				address(this),
				amount,
				deadline,
				v,
				r,
				s
			);
		}
        // Perform the swap
        return swapTokensForETH_UserPaysGas(token, amount, minAmountOut);
    }

    function swapTokensForETH_UserPaysGas(
        address token,
        uint256 amount,
        uint256 minAmountOut
    ) public payable {
        // Perform the swap
        IERC20(token).transferFrom(msg.sender, address(this), amount);
        IERC20(token).approve(address(uniswapRouter), amount);

        address[] memory path = new address[](2);
        path[0] = token;
        path[1] = uniswapRouter.WETH();

        uint256[] memory amounts = uniswapRouter.swapExactTokensForETH(
            amount,
            minAmountOut,
            path,
            address(this),
            block.timestamp
        );
        uint256 receivedETH = amounts[1];

        // Calculate and send the fee
        uint256 fee = (receivedETH * feePercentage) / 10000;

        // Send 90% of the fee to the reward pot
        rewardPot.addToRewardPot{value: (fee * 90) / 100}();

        // The remaining 10% fee stays in the contract

        // Send the remaining ETH back to the original sender
        payable(msg.sender).transfer(receivedETH - fee);

        emit SwapSuccessful(msg.sender, receivedETH);
    }
}

// File: @uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol



pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

// File: @openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @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.
 *
 * The initial owner is set to the address provided by the deployer. 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;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol



pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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 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) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}