// Contract: TRIBUTE
// Target function: function wwexcludeFromDividends(address account) external onlyOwner
   /// @param _owner The address of a token holder.
   /// @return The amount of dividend in wei that `_owner` can withdraw.
  function withdrawableDividendOf(address _owner, address _rewardToken) public view override returns(uint256) {
    return accumulativeDividendOf(_owner,_rewardToken).sub(withdrawnDividends[_owner][_rewardToken]);
  }

   /// @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, address _rewardToken) external view override returns(uint256) {
    return withdrawnDividends[_owner][_rewardToken];
  }


   /// @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, address _rewardToken) public view override returns(uint256) {
    return magnifiedDividendPerShare[_rewardToken].mul(holderBalance[_owner]).toInt256Safe()
      .add(magnifiedDividendCorrections[_rewardToken][_owner]).toUint256Safe() / magnitude;
  }

   /// @dev Internal function that increases tokens to an account.
   /// 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 _increase(address account, uint256 value) internal {
    for (uint256 i; i < rewardTokens.length; i++){
        magnifiedDividendCorrections[rewardTokens[i]][account] = magnifiedDividendCorrections[rewardTokens[i]][account]
          .sub((magnifiedDividendPerShare[rewardTokens[i]].mul(value)).toInt256Safe());
    }
  }

   /// @dev Internal function that reduces 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 _reduce(address account, uint256 value) internal {
      for (uint256 i; i < rewardTokens.length; i++){
        magnifiedDividendCorrections[rewardTokens[i]][account] = magnifiedDividendCorrections[rewardTokens[i]][account]
          .add( (magnifiedDividendPerShare[rewardTokens[i]].mul(value)).toInt256Safe() );
      }
  }

  function _setBalance(address account, uint256 newBalance) internal {
    uint256 currentBalance = holderBalance[account];
    holderBalance[account] = newBalance;
    if(newBalance > currentBalance) {
      uint256 increaseAmount = newBalance.sub(currentBalance);
      _increase(account, increaseAmount);
      totalBalance += increaseAmount;
    } else if(newBalance < currentBalance) {
      uint256 reduceAmount = currentBalance.sub(newBalance);
      _reduce(account, reduceAmount);
      totalBalance -= reduceAmount;
    }
  }
}

contract DividendTracker is DividendPayingToken {
    using SafeMath for uint256;
    using SafeMathInt for int256;

    struct Map {
        address[] keys;
        mapping(address => uint) values;
        mapping(address => uint) indexOf;
        mapping(address => bool) inserted;
    }

    function get(address key) private view returns (uint) {
        return tokenHoldersMap.values[key];
    }

    function getIndexOfKey(address key) private view returns (int) {
        if(!tokenHoldersMap.inserted[key]) {
            return -1;
        }
        return int(tokenHoldersMap.indexOf[key]);
    }

    function getKeyAtIndex(uint index) private view returns (address) {
        return tokenHoldersMap.keys[index];
    }



     function size() private view returns (uint) {
         return tokenHoldersMap.keys.length;
     }
 
     function set(address key, uint val) private {
         if (tokenHoldersMap.inserted[key]) {
             tokenHoldersMap.values[key] = val;
         } else {
             tokenHoldersMap.inserted[key] = true;
             tokenHoldersMap.values[key] = val;
             tokenHoldersMap.indexOf[key] = tokenHoldersMap.keys.length;
             tokenHoldersMap.keys.push(key);
         }
     }

    function remove(address key) private {
        if (!tokenHoldersMap.inserted[key]) {
            return;
        }

        delete tokenHoldersMap.inserted[key];
        delete tokenHoldersMap.values[key];

        uint index = tokenHoldersMap.indexOf[key];
        uint lastIndex = tokenHoldersMap.keys.length - 1;
        address lastKey = tokenHoldersMap.keys[lastIndex];

        tokenHoldersMap.indexOf[lastKey] = index;
        delete tokenHoldersMap.indexOf[key];

        tokenHoldersMap.keys[index] = lastKey;
        tokenHoldersMap.keys.pop();
    }

    Map private tokenHoldersMap;
    uint256 public lastProcessedIndex;

    mapping (address => bool) public excludedFromDividends;

    mapping (address => uint256) public lastClaimTimes;

    uint256 public claimWait;
    uint256 public immutable minimumTokenBalanceForDividends;

     event ExcludeFromDividends(address indexed account);
     event IncludeInDividends(address indexed account);
     event ClaimWaitUpdated(uint256 indexed newValue, uint256 indexed oldValue);
 
     event Claim(address indexed account, uint256 amount, bool indexed automatic);

    constructor() {
    	claimWait = 1200;
        minimumTokenBalanceForDividends = 1000 * (10**18);
    }

    function excludeFromDividends(address account) external onlyOwner {
    	excludedFromDividends[account] = true;

    	_setBalance(account, 0);
    	remove(account);

    	emit ExcludeFromDividends(account);
    }
    
    function includeInDividends(address account) external onlyOwner {
    	require(excludedFromDividends[account]);
    	excludedFromDividends[account] = false;

    	emit IncludeInDividends(account);
    }

    function updateClaimWait(uint256 newClaimWait) external onlyOwner {
        require(newClaimWait >= 1200 && newClaimWait <= 86400, "Dividend_Tracker: claimWait must be updated to between 1 and 24 hours");
        require(newClaimWait != claimWait, "Dividend_Tracker: Cannot update claimWait to same value");
        emit ClaimWaitUpdated(newClaimWait, claimWait);
        claimWait = newClaimWait;
    }

    function getLastProcessedIndex() external view returns(uint256) {
    	return lastProcessedIndex;
    }

    function getNumberOfTokenHolders() external view returns(uint256) {
        return tokenHoldersMap.keys.length;
    }

     // Check to see if I really made this contract or if it is a clone!

     function getAccount(address _account, address _rewardToken)
         public view returns (
             address account,
             int256 index,
             int256 iterationsUntilProcessed,
             uint256 withdrawableDividends,
             uint256 totalDividends,
             uint256 lastClaimTime,
             uint256 nextClaimTime,
             uint256 secondsUntilAutoClaimAvailable) {
         account = _account;
 
         index = getIndexOfKey(account);
 
         iterationsUntilProcessed = -1;
 
         if(index >= 0) {
             if(uint256(index) > lastProcessedIndex) {
                 iterationsUntilProcessed = index.sub(int256(lastProcessedIndex));
             }
             else {
                 uint256 processesUntilEndOfArray = tokenHoldersMap.keys.length > lastProcessedIndex ?
                                                         tokenHoldersMap.keys.length.sub(lastProcessedIndex) :
                                                         0;
 
 
                 iterationsUntilProcessed = index.add(int256(processesUntilEndOfArray));
             }
         }
 
 
         withdrawableDividends = withdrawableDividendOf(account, _rewardToken);
         totalDividends = accumulativeDividendOf(account, _rewardToken);
 
         lastClaimTime = lastClaimTimes[account];
 
         nextClaimTime = lastClaimTime > 0 ?
                                     lastClaimTime.add(claimWait) :
                                     0;
 
         secondsUntilAutoClaimAvailable = nextClaimTime > block.timestamp ?
                                                     nextClaimTime.sub(block.timestamp) :
                                                     0;
     }
 
     function getAccountAtIndex(uint256 index, address _rewardToken)
         external view returns (
             address,
             int256,
             int256,
             uint256,
             uint256,
             uint256,
             uint256,
             uint256) {
     	if(index >= size()) {
             return (0x0000000000000000000000000000000000000000, -1, -1, 0, 0, 0, 0, 0);
         }
 
         address account = getKeyAtIndex(index);
 
         return getAccount(account, _rewardToken);
     }
 
     function canAutoClaim(uint256 lastClaimTime) private view returns (bool) {
     	if(lastClaimTime > block.timestamp)  {
     		return false;
     	}
 
     	return block.timestamp.sub(lastClaimTime) >= claimWait;
     }

    function setBalance(address payable account, uint256 newBalance) external onlyOwner {
    	if(excludedFromDividends[account]) {
    		return;
    	}

    	if(newBalance >= minimumTokenBalanceForDividends) {
            _setBalance(account, newBalance);
    		set(account, newBalance);
    	}
    	else {
            _setBalance(account, 0);
    		remove(account);
    	}

    	processAccount(account, true);
    }
    
    function process(uint256 gas) external returns (uint256, uint256, uint256) {
    	uint256 numberOfTokenHolders = tokenHoldersMap.keys.length;

    	if(numberOfTokenHolders == 0) {
    		return (0, 0, lastProcessedIndex);
    	}

    	uint256 _lastProcessedIndex = lastProcessedIndex;

    	uint256 gasUsed = 0;

    	uint256 gasLeft = gasleft();

    	uint256 iterations = 0;
    	uint256 claims = 0;

    	while(gasUsed < gas && iterations < numberOfTokenHolders) {
    		_lastProcessedIndex++;

    		if(_lastProcessedIndex >= tokenHoldersMap.keys.length) {
    			_lastProcessedIndex = 0;
    		}

    		address account = tokenHoldersMap.keys[_lastProcessedIndex];

    		if(canAutoClaim(lastClaimTimes[account])) {
    			if(processAccount(payable(account), true)) {
    				claims++;
    			}
    		}

    		iterations++;

    		uint256 newGasLeft = gasleft();

    		if(gasLeft > newGasLeft) {
    			gasUsed = gasUsed.add(gasLeft.sub(newGasLeft));
    		}
    		gasLeft = newGasLeft;
    	}

    	lastProcessedIndex = _lastProcessedIndex;

    	return (iterations, claims, lastProcessedIndex);
    }

    function processAccount(address payable account, bool automatic) public onlyOwner returns (bool) {
        uint256 amount;
        bool paid;
        for (uint256 i; i < rewardTokens.length; i++){
            amount = _withdrawDividendOfUser(account, rewardTokens[i]);
            if(amount > 0) {
        		lastClaimTimes[account] = block.timestamp;
                emit Claim(account, amount, automatic);
                paid = true;
    	    }
        }
        return paid;
    }
}

contract TRIBUTE is ERC20, Ownable {
    using SafeMath for uint256;

    IUniswapV2Router02 public immutable uniswapV2Router;
    address public immutable uniswapV2Pair;

     bool private swapping;

    DividendTracker public dividendTracker;

    address public operationsWallet;
    
     uint256 public maxTransactionAmount;
     uint256 public swapTokensAtAmount;
     uint256 public maxWallet;
     
     uint256 public liquidityActiveBlock = 0;  // 0 means liquidity is not active yet
     uint256 public tradingActiveBlock = 0;  // 0 means trading is not active
     uint256 public earlyBuyPenaltyEnd;  // determines when snipers/bots can sell without extra penalty
     
     bool public limitsInEffect = true;
     bool public tradingActive = false;
     bool public swapEnabled = false;
     
       // Anti-bot and anti-whale mappings and variables
     mapping(address => uint256) private _holderLastTransferTimestamp;  // to hold last Transfers temporarily during launch
     bool public transferDelayEnabled = true;
    
    uint256 public constant feeDivisor = 1000;

    uint256 public totalSellFees;
    uint256 public lerewardsSellFee;
    uint256 public operationsSellFee;
    uint256 public liquiditySellFee;
    
    uint256 public totalBuyFees;
    uint256 public rewardsBuyFee;
    uint256 public operationsBuyFee;
    uint256 public _liquidityBuyFee;
    
    uint256 public tokensForRewards;
    uint256 public tokensForOperations;
    uint256 public tokensForLiquidity;
    
    uint256 public gasForProcessing = 0;



     // exlcude from fees and max transaction amount
    mapping (address => bool) private _isExcludedFromFees;

    mapping (address => bool) public _isExcludedMaxTransactionAmount;

     // store addresses that a automatic market maker pairs. Any transfer *to* these addresses
     // could be subject to a maximum transfer amount
    mapping (address => bool) public automatedMarketMakerPairs;

    event ExcludeFromFees(address indexed account, bool isExcluded);
    event ExcludeMultipleAccountsFromFees(address[] accounts, bool isExcluded);
    event ExcludedMaxTransactionAmount(address indexed account, bool isExcluded);

    event SetAutomatedMarketMakerPair(address indexed pair, bool indexed value);

    event OperationsWalletUpdated(address indexed newWallet, address indexed oldWallet);

    event DevWalletUpdated(address indexed newWallet, address indexed oldWallet);

    event GasForProcessingUpdated(uint256 indexed newValue, uint256 indexed oldValue);
    
    event SwapAndLiquify(
        uint256 tokensSwapped,
        uint256 ethReceived,
        uint256 tokensIntoLiqudity
    );

    event SendDividends(
    	uint256 tokensSwapped,
    	uint256 amount
    );

    event ProcessedDividendTracker(
    	uint256 iterations,
    	uint256 claims,
        uint256 lastProcessedIndex,
    	bool indexed automatic,
    	uint256 gas,
    	address indexed processor
    );

    event RequestedLPWithdraw();
    
    event WithdrewLPForMigration();

    event CanceledLpWithdrawRequest();

    constructor() ERC20("9.11.2001", "TRIBUTE") {

         uint256 totalSupply = 1000 * 1e6 * 1e18;
         
         maxTransactionAmount = totalSupply * 10 / 1000;  // 1.0% maxTransactionAmountTxn
         swapTokensAtAmount = totalSupply * 15 / 10000;  // 1.5% swap tokens amount
         maxWallet = totalSupply * 30 / 1000;  // 3% Max wallet
 
         rewardsBuyFee = 0;
         operationsBuyFee = 0;
         _liquidityBuyFee = 40;
         totalBuyFees = rewardsBuyFee + operationsBuyFee + _liquidityBuyFee;
         
         lerewardsSellFee = 0;
         operationsSellFee = 0;
         liquiditySellFee = 40;
         totalSellFees = lerewardsSellFee + operationsSellFee + liquiditySellFee;
 
    	 dividendTracker = new DividendTracker();
    	 
    	 operationsWallet = address(msg.sender);  // set as operations wallet
 
    	 IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
    	 
           // Create a uniswap pair for this new token
         address _uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
             .createPair(address(this), _uniswapV2Router.WETH());
 
         uniswapV2Router = _uniswapV2Router;
         uniswapV2Pair = _uniswapV2Pair;
 
         _setAutomatedMarketMakerPair(_uniswapV2Pair, true);

         // exclude from paying fees or having max transaction amount
        excludeFromFees(owner(), true);
        excludeFromFees(address(this), true);
        excludeFromFees(address(0xdead), true);
        excludeFromMaxTransaction(owner(), true);
        excludeFromMaxTransaction(address(this), true);
        excludeFromMaxTransaction(address(dividendTracker), true);
        excludeFromMaxTransaction(address(_uniswapV2Router), true);
        excludeFromMaxTransaction(address(0xdead), true);

         // exclude from receiving dividends
        dividendTracker.excludeFromDividends(address(dividendTracker));
        dividendTracker.excludeFromDividends(address(this));
        dividendTracker.excludeFromDividends(owner());
        dividendTracker.excludeFromDividends(address(_uniswapV2Router));
        dividendTracker.excludeFromDividends(address(0xdead));
        

        _createInitialSupply(address(owner()), totalSupply);
    }

    receive() external payable {

  	}

      // disable Transfer delay - cannot be reenabled
    function qqdisableTransferDelay() external onlyOwner returns (bool){
        transferDelayEnabled = false;
        return true;
    }