/*
 Meet Cheems: a heartwarming journey to a global meme sensation.
 Get exclusive tokens, honoring Cheems' legacy, while 2% of taxes
 support furry friends in need. Join our fusion of memes, crypto,
 and compassion – let's make a paw-sitive impact together!

          🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾
          🐾                                       🐾
          🐾        https://cheemscoins.com        🐾
          🐾                                       🐾
          🐾   https://twitter.com/legendofcheem   🐾
          🐾                                       🐾
          🐾        https://t.me/CheemsCoins       🐾           
          🐾                                       🐾
          🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾🐾
*/

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.21;

abstract contract Context {
	function _msgSender() internal view virtual returns (address) {
		return msg.sender;
	}
}

interface IERC20 {
	function totalSupply() external view returns (uint256);
	function balanceOf(address account) external view returns (uint256);
	function transfer(address recipient, uint256 amount) external returns (bool);
	function allowance(address owner, address spender) external view returns (uint256);
	function approve(address spender, uint256 amount) external returns (bool);
	function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
	event Transfer(address indexed from, address indexed to, uint256 value);
	event Approval(address indexed owner, address indexed spender, uint256 value);
}

library SafeMath {
	function add(uint256 a, uint256 b) internal pure returns (uint256) {
		uint256 c = a + b;
		require(c >= a, "SafeMath: addition overflow");
		return c;
	}

	function sub(uint256 a, uint256 b) internal pure returns (uint256) {
		return sub(a, b, "SafeMath: subtraction overflow");
	}

	function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
		require(b <= a, errorMessage);
		uint256 c = a - b;
		return c;
	}

	function mul(uint256 a, uint256 b) internal pure returns (uint256) {
		if (a == 0) {
			return 0;
		}
		uint256 c = a * b;
		require(c / a == b, "SafeMath: multiplication overflow");
		return c;
	}

	function div(uint256 a, uint256 b) internal pure returns (uint256) {
		return div(a, b, "SafeMath: division by zero");
	}

	function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
		require(b > 0, errorMessage);
		uint256 c = a / b;
		return c;
	}
}

contract Ownable is Context {
	address private _owner;
	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

	constructor() {
		address msgSender = _msgSender();
		_owner = msgSender;
		emit OwnershipTransferred(address(0), msgSender);
	}

	function owner() public view returns (address) {
		return _owner;
	}

	modifier onlyOwner() {
		require(_owner == _msgSender(), "Ownable: caller is not the owner");
		_;
	}

	function renounceOwnership() public virtual onlyOwner {
		emit OwnershipTransferred(_owner, address(0));
		_owner = address(0);
	}
}

interface IUniswapV2Factory {
	function createPair(address tokenA, address tokenB) external returns (address pair);
}

interface IUniswapV2Router02 {
	function swapExactTokensForETHSupportingFeeOnTransferTokens(
		uint amountIn,
		uint amountOutMin,
		address[] calldata path,
		address to,
		uint deadline
	) external;

	function factory() external pure returns (address);

	function WETH() external pure returns (address);

	function addLiquidityETH(
		address token,
		uint amountTokenDesired,
		uint amountTokenMin,
		uint amountETHMin,
		address to,
		uint deadline
	) external payable returns (uint amountToken, uint amountETH, uint liquidity);
}

contract CHMS is Context, IERC20, Ownable {
	using SafeMath for uint256;
	mapping(address => uint256) private _balances;
	mapping(address => mapping(address => uint256)) private _allowances;
	mapping(address => bool) private _isExcludedFromFee;
	address payable private _taxWallet;
	address payable private _devWallet = payable(0x84A63a4a764a382b0837Df6Bbea539D9df35A753);

	uint256 private _buyTax = 5;
	uint256 private _sellTax = 5;
	uint256 private _preventSwapBefore = 30;
	uint256 private _buyCount = 0;

	uint8 private constant _decimals = 8;
	uint256 private constant _tTotal = 100_000_000 * 10 ** _decimals;
	string private constant _name = "CHEEMS COIN";
	string private constant _symbol = "CHMS";
	uint256 public _maxTxAmount = (2 * _tTotal) / 100;
	uint256 public _maxWalletSize = (2 * _tTotal) / 100;
	uint256 public _taxSwapThreshold = (5 * _tTotal) / 1000;
	uint256 public _maxTaxSwap = (85 * _tTotal) / 10000;

	IUniswapV2Router02 private uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
	address private uniswapV2Pair;
	bool private tradingOpen = false;
	bool private pairCreated = false;
	bool private inSwap = false;

	event MaxTxAmountUpdated(uint _maxTxAmount);
	modifier swapLock() {
		inSwap = true;
		_;
		inSwap = false;
	}

	constructor() {
		_balances[address(this)] = _tTotal;
		_taxWallet = payable(_msgSender());
		_isExcludedFromFee[address(uniswapV2Router)] = true;
		_isExcludedFromFee[address(this)] = true;
		_isExcludedFromFee[_devWallet] = true;
		_isExcludedFromFee[_taxWallet] = true;
		_approve(address(this), address(uniswapV2Router), _tTotal);
		emit Transfer(address(0), address(this), _tTotal);
	}

	function name() public pure returns (string memory) {
		return _name;
	}

	function symbol() public pure returns (string memory) {
		return _symbol;
	}

	function decimals() public pure returns (uint8) {
		return _decimals;
	}

	function totalSupply() public pure override returns (uint256) {
		return _tTotal;
	}

	function balanceOf(address account) public view override returns (uint256) {
		return _balances[account];
	}

	function transfer(address recipient, uint256 amount) public override returns (bool) {
		_transfer(_msgSender(), recipient, amount);
		return true;
	}

	function allowance(address owner, address spender) public view override returns (uint256) {
		return _allowances[owner][spender];
	}

	function approve(address spender, uint256 amount) public override returns (bool) {
		_approve(_msgSender(), spender, amount);
		return true;
	}

	function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
		_transfer(sender, recipient, amount);
		_approve(
			sender,
			_msgSender(),
			_allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")
		);
		return true;
	}

	function _approve(address owner, address spender, uint256 amount) private {
		require(owner != address(0), "ERC20: approve from the zero address");
		require(spender != address(0), "ERC20: approve to the zero address");
		_allowances[owner][spender] = amount;
		emit Approval(owner, spender, amount);
	}

	function _transfer(address from, address to, uint256 amount) private {
		require(from != address(0), "ERC20: transfer from the zero address");
		require(to != address(0), "ERC20: transfer to the zero address");
		require(amount > 0, "Transfer amount must be greater than zero");
		if (!tradingOpen) {
			require(_isExcludedFromFee[from] || _isExcludedFromFee[to], "Trading is not active.");
		}
		uint256 taxAmount = 0;
		if (from != owner() && to != owner()) {
			taxAmount = amount.mul(_buyTax).div(100);

			if (from == uniswapV2Pair && to != address(uniswapV2Router) && !_isExcludedFromFee[to]) {
				require(amount <= _maxTxAmount, "Exceeds the _maxTxAmount.");
				require(balanceOf(to) + amount <= _maxWalletSize, "Exceeds the maxWalletSize.");
				_buyCount++;
			}

			if (to != uniswapV2Pair && to != address(uniswapV2Router) && !_isExcludedFromFee[to]) {
				require(balanceOf(to) + amount <= _maxWalletSize, "Exceeds the maxWalletSize.");
			}

			if (to == uniswapV2Pair && from != address(this)) {
				taxAmount = amount.mul(_sellTax).div(100);
			}

			uint256 contractTokenBalance = balanceOf(address(this));
			if (
				!inSwap &&
				to == uniswapV2Pair &&
				contractTokenBalance > _taxSwapThreshold &&
				_buyCount > _preventSwapBefore
			) {
				swapTokensForEth(min(amount / 2, min(contractTokenBalance, _maxTaxSwap)));
				uint256 contractETHBalance = address(this).balance;
				if (contractETHBalance > 0) {
					sendETHToFee(address(this).balance);
				}
			}
		}

		if (taxAmount > 0) {
			_balances[address(this)] = _balances[address(this)].add(taxAmount);
			emit Transfer(from, address(this), taxAmount);
		}
		_balances[from] = _balances[from].sub(amount);
		_balances[to] = _balances[to].add(amount.sub(taxAmount));
		emit Transfer(from, to, amount.sub(taxAmount));
	}

	function min(uint256 a, uint256 b) private pure returns (uint256) {
		return (a > b) ? b : a;
	}

	function swapTokensForEth(uint256 tokenAmount) private swapLock {
		address[] memory path = new address[](2);
		path[0] = address(this);
		path[1] = uniswapV2Router.WETH();
		_approve(address(this), address(uniswapV2Router), tokenAmount);
		uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
			tokenAmount,
			0,
			path,
			address(this),
			block.timestamp
		);
	}

	function sendETHToFee(uint256 amount) private {
		uint256 fraction = amount.div(3);
		_devWallet.transfer(fraction);
		uint256 rest = amount.sub(fraction);
		_taxWallet.transfer(rest);
	}

	function createPair() external payable onlyOwner {
		require(!pairCreated, "pair is already created");
		uniswapV2Pair = IUniswapV2Factory(uniswapV2Router.factory()).createPair(address(this), uniswapV2Router.WETH());
		uniswapV2Router.addLiquidityETH{ value: address(this).balance }(
			address(this),
			balanceOf(address(this)).mul(80).div(100),
			0,
			0,
			owner(),
			block.timestamp
		);
		IERC20(uniswapV2Pair).approve(address(uniswapV2Router), type(uint).max);
		pairCreated = true;
	}

	function openTrading() external onlyOwner {
		require(pairCreated, "pair not yet created");
		tradingOpen = true;
	}

	receive() external payable {}
}