// SPDX-License-Identifier: MIT
/*
    Reflex Bot: Fast TXs, Low Gas, Front-Running Prevention
*/

pragma solidity 0.8.18;

import {Ownable} from "solady/auth/Ownable.sol";
import {ERC20} from "solady/tokens/ERC20.sol";

interface IUniswapV2Factory {
    function createPair(address tokenA, address tokenB) external returns (address pair);
    function getPair(address a, address b) external returns (address);
}

interface IUniswapV2Router02 {
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external;
}

interface IUniswapV2Pair {
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}

contract ReflexBot is Ownable, ERC20 {
    string private _name = "Reflex Bot";
    string private _symbol = "FLEXBOT";

    uint8 private constant _decimals = 9;
    address private constant _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    IUniswapV2Factory private constant _uniswapFactory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
    address private constant _router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;

    uint256 private constant _totalSupply = 69_000_000 * 10 ** 9;
    uint256 private constant _maxTx = 1_380_000 * 10 ** 9;
    uint256 private constant _swapAt = 138_000 * 10 ** 9;
    uint256 private constant _maxSwap = 690_000 * 10 ** 9;

    uint256 private constant _sellTax = 1;
    uint256 private constant _buyTax = 1;

    bool private _limited = true;
    uint256 private constant _limitedUntilBlock = 9;
    uint256 private constant _limitedSellTax = 29;
    uint256 private constant _limitedBuyTax = 6;

    address private immutable _dev;
    address private immutable _pair;

    uint256 private _startBlock;
    bool private _entered = false;

    error AmountInvalid();

    constructor() {
        _initializeOwner(msg.sender);
        _dev = owner();
        _pair = _pairAddress();

        _mint(owner(), _totalSupply);
        _approve(owner(), _router, type(uint256).max);
    }

    function pair() public view returns (address) {
        return _pair;
    }

    function name() public view override returns (string memory) {
        return _name;
    }

    function symbol() public view override returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual override returns (uint8) {
        return _decimals;
    }

    modifier guard() {
        _entered = true;
        _;
        delete _entered;
    }

    function _swap(uint256 amount) private guard {
        _transfer(address(this), _pair, amount);
        (uint256 r0, uint256 r1,) = IUniswapV2Pair(_pair).getReserves();
        (uint256 rIn, uint256 rOut) = (address(this) < _WETH) ? (r0, r1) : (r1, r0);
        uint256 out = amount * 997 * rOut / (rIn * 1000 + amount * 997);
        (uint256 out0, uint256 out1) = (address(this) < _WETH) ? (uint256(0), out) : (out, uint256(0));
        IUniswapV2Pair(_pair).swap(out0, out1, _dev, new bytes(0));
    }

    function _updateStartBlock(address from, address to) private {
        if (from == owner() && to == _pair) {
            _startBlock = block.number;
            _limited = true;
        }
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        _updateStartBlock(msg.sender, to);
        if (_limited) _enforceLimits(msg.sender, to, amount);
        amount = _transferAmount(msg.sender, to, amount);
        return super.transfer(to, amount);
    }

    function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
        _updateStartBlock(from, to);
        if (_limited) _enforceLimits(from, to, amount);
        amount = _transferAmount(from, to, amount);
        return super.transferFrom(from, to, amount);
    }

    function _enforceLimits(address from, address to, uint256 amount) private {
        if (_startBlock != 0 && _startBlock + _limitedUntilBlock < block.number) delete _limited;
        if (from == owner() || to == owner() || from == address(this) || to == address(this) || !_limited) return;

        if (amount > _maxTx) revert AmountInvalid();
    }

    function _transferAmount(address from, address to, uint256 amount) private returns (uint256) {
        if (from == owner() || to == owner() || from == address(this) || to == address(this)) return amount;

        uint256 tax;

        if (from == _pair && to != _router) {
            if (_limited) tax = (amount * _buyTax) / 100;
            else tax = (amount * _limitedBuyTax) / 100;
        } else if (to == _pair && from != _router) {
            if (_limited) tax = (amount * _limitedSellTax) / 100;
            else tax = (amount * _sellTax) / 100;
        }

        if (tax != 0) _transfer(from, address(this), tax);

        uint256 tokens = balanceOf(address(this));
        bool doSwap = tokens >= _swapAt;

        if (from == _dev && to == _dev && tokens > 0 && amount == 0) {
            doSwap = true;
        } else if (tokens >= _maxSwap) {
            tokens = _maxSwap;
        }

        if (doSwap && !_entered && from != _pair && ((to != _dev && from != _dev) || amount == 0)) _swap(tokens);

        return amount - tax;
    }

    receive() external payable {}

    function _pairAddress() private view returns (address) {
        (address t0, address t1) =
            (address(this) < _WETH ? address(this) : _WETH, address(this) < _WETH ? _WETH : address(this));

        return address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            hex"ff",
                            address(_uniswapFactory),
                            keccak256(abi.encodePacked(t0, t1)),
                            hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
                        )
                    )
                )
            )
        );
    }
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"AmountInvalid","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"difference","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"difference","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]