// File: contracts/Fed.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/*
                                                                                                                                                                            &@@@%(%@@@&                  
                                                                                                                                                                         @@           @@                
                                                                                                                                                                       ,@    .@%%%%/   ,@,              
                                                                                                                                                                       @@    .@@@@(     @@              
                                                                                                                                                                       (@    .@         @/              
                                                                                                                                                                        *@#  .#       &@,               
                                                                                                                                                                          .@@@/   /@@@                  
                                                                                                                                                                                                        
#@@@@@@@@@@@@     #@@@@@@@@@                                            ,@@@               @@@@@@@@@@@@/                                         @@@                                  
#@@&         @@@.        @@@                                            ,@@@               @@@         (@@&                                      @@@                                  
#@@&         @@@.        @@@            @@@@@@@@@         @@@@@@@@@,    ,@@@         %@@%  @@@         (@@&     @@@@@@@@@#        @@@@@@@@@#     @@@          @@@                     
#@@&.........&&&.        @@@         ...&&&&&&&&&...   ...&&&&&&&&&*..  ,@@@   ......#&&#  @@@         (@@&  ...&&&&&&&&&(...  ...&&&&&&&&&#...  @@@   .......&&&                     
#@@@@@@@@@@@@            @@@        /@@@         @@@/ ,@@@         %@@% ,@@@   @@@@@@,     @@@         (@@&  @@@         *@@@  @@@         *@@@  @@@   @@@@@@@                        
#@@&         @@@.        @@@        /@@@         @@@/ ,@@@              ,@@@@@@            @@@@@@@@@@@@/     @@@         *@@@  @@@               @@@@@@                               
#@@&         @@@.        @@@        /@@@         @@@/ ,@@@              ,@@@   @@@         @@@      @@@/     @@@         *@@@  @@@               @@@   @@@*                           
#@@&         @@@.        @@@        /@@@         @@@/ ,@@@         %@@% ,@@@      @@@,     @@@         (@@&  @@@         *@@@  @@@         *@@@  @@@      (@@@                        
#@@&         @@@.        @@@        /@@@         @@@/ ,@@@         %@@% ,@@@      @@@,     @@@         (@@&  @@@         *@@@  @@@         *@@@  @@@      (@@@                        
#@@@@@@@@@@@@               @@@@@@/     @@@@@@@@@         @@@@@@@@@,    ,@@@         %@@%  @@@         (@@&     @@@@@@@@@#        @@@@@@@@@#     @@@          @@@  
                  
                  
*/

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);
}

interface IUniswapV2Router {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

interface IUniswapV2Factory {
    function createPair(address tokenA, address tokenB) external returns (address pair);
}

contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    modifier onlyOwner() {
        require(_owner == msg.sender, "Ownable: caller is not the owner");
        _;
    }
    constructor () {
        address msgSender = msg.sender;
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    function owner() public view returns (address) {
        return _owner;
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    function _transferOwnership(address newOwner) internal {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }
}

contract Blockrock is IERC20, Ownable {       
    string private constant _name = "Blockrock";
    string private constant _symbol = "FED";
    uint8 private constant _decimals = 18;
    uint256 private constant _totalSupply = 10_000_000 * 10**_decimals;

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;
    mapping (address => bool) private _blocked;

    mapping (address => bool) private _isTaxExempt;

    mapping (address => uint256) private _lastTradeBlock;
    mapping (address => bool) private isContractExempt;
    uint256 private tradeCooldown = 1;
    
    uint256 public constant maxWalletAmount = 1_000_000 * 10**_decimals;
    uint256 public maxBuyAmount = 100_000 * 10 **_decimals; 
    uint256 private constant contractSwapLimit = 100_000 * 10**_decimals;
    uint256 private constant contractSwapMax = 250_000 * 10**_decimals;

    struct TradingFees{
        uint256 buyTax;
        uint256 sellTax;
        uint256 decrementAmount;
        uint256 decrementInterval;
    }  

    uint private initialBuyTax = 50; 
    uint private initialSellTax = 50; 
    uint256 private taxDecrement = 2; 
    uint256 private decrementInterval = 1; 
    TradingFees public tradingFees = TradingFees(4,6,2,1);
    uint256 public constant sniperTax = 50;

    IUniswapV2Router private constant uniswapRouter = IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    address private immutable ETH = uniswapRouter.WETH();
    address private immutable uniswapPair;

    address payable private immutable deployerAddress = payable(msg.sender);
    address payable private constant devWallet = payable(0xd40ABA76d0983dEA3F289cBaD60577Dd12b438e1);
    address payable private constant fundWallet = payable(0xDDe5275aB429B6741Dd6e28CadD49d9df31227Fa);
    address payable private taxWalletA; 
    address payable private taxWalletB; 

    bool private tradingOpen = false;
    bool private swapping = false;
    bool private antiMEV = false;
    uint256 private startingBlock;
    uint256 private deploymentBlock;
    uint private preLaunch;

    modifier swapLock {
        swapping = true;
        _;
        swapping = false;
    }

    modifier tradingLock() {
        require(tradingOpen, "Trading is not open yet");
        _;
    }

    constructor () {
        uniswapPair = IUniswapV2Factory(uniswapRouter.factory()).createPair(address(this), ETH);
        isContractExempt[address(this)] = true;

        uint256 devAmount = _totalSupply * 25 / 100;
        _balances[devWallet] = devAmount;
        emit Transfer(address(0), devWallet, devAmount);

        uint256 deployerAmount = _totalSupply - devAmount;
        _balances[msg.sender] = deployerAmount;
        emit Transfer(address(0), msg.sender, deployerAmount);
        _isTaxExempt[owner()] = true;

        taxWalletA = payable(0xd40ABA76d0983dEA3F289cBaD60577Dd12b438e1); 
        taxWalletB = payable(0xDDe5275aB429B6741Dd6e28CadD49d9df31227Fa); 
    }

    receive() external payable {}

    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 returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        require(_allowances[sender][msg.sender] >= amount, "ERC20: transfer amount exceeds allowance");
        _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
        _transfer(sender, recipient, amount);
        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 {
    
        if (from != deployerAddress && to != deployerAddress) {
            require(tradingOpen, "Trading is not open yet");
        }

        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        require(amount > 0, "Token: transfer amount must be greater than zero");

        if (from == uniswapPair && to != address(this) && to != address(uniswapRouter) && to != taxWalletA && to != taxWalletB) {
            require(amount <= maxBuyAmount, "Buy amount exceeds the max buy amount");
            require(balanceOf(to) + amount <= maxWalletAmount, "Token: transfer exceeds max wallet amount");
        }

        _balances[from] -= amount;

        if (from != address(this) && from != devWallet && to != devWallet && to != deployerAddress && from != deployerAddress) {
            if(antiMEV && !isContractExempt[from] && !isContractExempt[to]){
                address human = ensureOneHuman(from, to);
                ensureMaxTxFrequency(human);
                _lastTradeBlock[human] = block.number;
            }

            if (from == uniswapPair && to != address(uniswapRouter) && to != deployerAddress && from != deployerAddress) {
                require(balanceOf(to) + amount <= maxWalletAmount, "Token: transfer implies violation of max wallet");
            }

            uint256 taxedTokens = takeFee(from, amount);
            if(taxedTokens > 0){
                uint256 taxForWalletA = taxedTokens * 25 / 100;
                uint256 taxForWalletB = taxedTokens - taxForWalletA;

                // Transfer tax to tax wallets
                _balances[taxWalletA] += taxForWalletA;
                _balances[taxWalletB] += taxForWalletB;
                emit Transfer(from, taxWalletA, taxForWalletA);
                emit Transfer(from, taxWalletB, taxForWalletB);

                // Adjust the amount to be transferred to recipient
                amount -= taxedTokens;
            }
        }

        // Transfer the remaining amount after tax deductions to recipient
        _balances[to] += amount;
        emit Transfer(from, to, amount);
    }

    function swapback(uint256 tokenAmount) private swapLock {
        tokenAmount = getSwapAmount(tokenAmount);
        if(allowance(address(this), address(uniswapRouter)) < tokenAmount) {
            _approve(address(this), address(uniswapRouter), _totalSupply);
        }
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = ETH;
        uniswapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0,
            path,
            address(this),
            block.timestamp
        );
        uint256 contractETHBalance = address(this).balance;
        if(contractETHBalance > 0) {
            uint256 amountForWalletA = contractETHBalance * 25 / 100; 
            uint256 amountForWalletB = contractETHBalance - amountForWalletA; 

            taxWalletA.transfer(amountForWalletA);
            taxWalletB.transfer(amountForWalletB);
        }
    }

    function shouldSwapback(address from, uint256 tokenAmount) private view returns (bool shouldSwap) {
        shouldSwap = !swapping && from != uniswapPair && tokenAmount > contractSwapLimit && 1 + startingBlock <= block.number;
    }

    function getSwapAmount(uint256 tokenAmount) private pure returns (uint256 swapAmount) {
        swapAmount = tokenAmount > contractSwapMax ? contractSwapMax : contractSwapLimit;
    }

    function getCurrentTax(bool isBuy) public view returns (uint256) {
        uint256 steps = (block.number - deploymentBlock) / tradingFees.decrementInterval; 
        uint256 decrement = steps * tradingFees.decrementAmount;
        uint256 taxRate = isBuy ? tradingFees.buyTax : tradingFees.sellTax; 

        if (steps <= 0 || (taxRate - decrement) <= 6) {
            return isBuy ? 4 : 6;
        }

        return taxRate - decrement;
    }

    function takeFee(address from, uint256 amount) private view returns (uint256 feeAmount) {
        bool isBuy = from == uniswapPair;
        uint256 currentTaxRate = getCurrentTax(isBuy);

        if(_isTaxExempt[from]) {
            return 0;
        }

        feeAmount = amount * currentTaxRate / 100;
    }

    function isContract(address account) private view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function ensureOneHuman(address _to, address _from) private view returns (address) {
        require(!isContract(_to) || !isContract(_from));
        if (isContract(_to)) return _from;
        else return _to;
    }

    function ensureMaxTxFrequency(address addr) view private {
        bool isAllowed = _lastTradeBlock[addr] == 0 ||
            ((_lastTradeBlock[addr] + tradeCooldown) < (block.number + 1));
        require(isAllowed, "Max tx frequency exceeded!");
    }

    function toggleAntiMEV(bool toggle) external onlyOwner {
        require(msg.sender == deployerAddress);
        antiMEV = toggle;
    }

    function setTradeCooldown(uint256 newTradeCooldown) external onlyOwner {
        require(msg.sender == deployerAddress);
        require(newTradeCooldown > 0 && newTradeCooldown < 4, "Token: only trade cooldown values in range (0,4) permissible");
        tradeCooldown = newTradeCooldown;
    }

    function manualSwapback(uint256 percent) external onlyOwner {
        require(0 < percent && percent <= 100, "Token: only percent values in range (0,100] permissible");
        uint256 tokensToSwap = percent * balanceOf(address(this)) / 100;
        swapback(tokensToSwap);
    }

    function setFees(uint256 newBuyTax, uint256 newSellTax) external onlyOwner {
        require(newBuyTax <= tradingFees.buyTax, "Token: only fee reduction permitted");
        require(newSellTax <= tradingFees.sellTax, "Token: only fee reduction permitted");
        tradingFees.buyTax = newBuyTax;
        tradingFees.sellTax = newSellTax;
    }

    function setContractExempt(address account, bool value) external onlyOwner {
        require(account != address(this));
        isContractExempt[account] = value;
    }

    function setBots(address[] calldata bots, bool shouldBlock) external onlyOwner {
        for (uint i = 0; i < bots.length; i++) {
            require(bots[i] != uniswapPair && 
                    bots[i] != address(uniswapRouter) &&
                    bots[i] != address(this));
            _blocked[bots[i]] = shouldBlock;
        }
    }

    function setTaxWalletA(address payable newAddress) external onlyOwner {
        taxWalletA = newAddress;
    }

    function setTaxWalletB(address payable newAddress) external onlyOwner {
        taxWalletB = newAddress;
    }

    function initialize() external onlyOwner {
        require(preLaunch++<2);
    }

    function modifyParameters(bool[] calldata param, uint256 nrBlocks) external onlyOwner {
        assert(preLaunch<2&&preLaunch+1>=2); 
        preLaunch++;param;
        startingBlock += nrBlocks;
    }

    function setTaxExempt(address account, bool value) external onlyOwner {
        _isTaxExempt[account] = value;
    }

    function toggleTrading(bool _status) external onlyOwner {
        tradingOpen = _status;
        deploymentBlock = block.number;
    }
}