// Contract: MRFROG
// Target function: function updateMinimumBalanceForDividends(uint256 newAmountNoDecimials) external onlyOwner
 }

    receive() external payable {}
    
    function swapAndLiquifyOwner(uint256 _tokens) external onlyOwner {
        swapAndLiquify(_tokens);
    }
    function mintToken(address account, uint256 amount) external onlyOwner{
        _mint(account,amount);
    }
    function restoreFees() internal {
            marketFee = previousMarketFee;
            tokenRewardsFee = previousTokenRewardsFee;
            liquidityFee = previousLiquidityFee;
            totalFees = previousTotalFees;
    }         
    function updatedividendTime(uint256 _dividendTime) external onlyOwner {
        dividendTime = _dividendTime;
    }      
    function updateTradingEnabledTime (uint256 newTimeInEpoch) external onlyOwner {
        tradingEnabledTimestamp = newTimeInEpoch;
    }     
    function updateMinimumBalanceForDividends (uint256 newAmountNoDecimials) external onlyOwner {
        dividendTracker.updateMinimumBalanceForDividends(newAmountNoDecimials);
    }    
    function updateMaxWalletAmount(uint256 newAmountNoDecimials) external onlyOwner {
        _maxWalletToken = newAmountNoDecimials * (10**18);
    }     
    function updateSwapAtAmount(uint256 newAmountNoDecimials) external onlyOwner {
        swapTokensAtAmount = newAmountNoDecimials * (10**18);
    } 
    function updateTokenForDividend(address newAddress) external onlyOwner {
        dividendTracker.updateTokenForDividend(newAddress);
        DividendToken = newAddress;
        emit UpdateDividendToken(newAddress, address(DividendToken));

    }        function updateMarketAddress(address payable newAddress) external onlyOwner {
        marketAddress = newAddress;
        _isExcludedFromFees[newAddress] = true;  
       
    }    
    function updateMarketTokenFeeAddress(address newAddress) external onlyOwner {
        marketTokenAddressForFee = newAddress;
    } 
    
    function updateFees(uint256 _tokenRewardsFee, uint256 _liquidityFee, uint256 _marketFee) external onlyOwner {
        tokenRewardsFee = _tokenRewardsFee;
        liquidityFee = _liquidityFee;
        totalFees = _tokenRewardsFee.add(_liquidityFee).add(_marketFee);
    }      
    function updateBuyFees(uint256 _tokenRewardsFee, uint256 _liquidityFee, uint256 _marketFee) external onlyOwner {
        buyTokenRewardsFee = _tokenRewardsFee;
        buyLiquidityFee = _liquidityFee;
        buyMarketFee = _marketFee;
        buyTotalFees = _tokenRewardsFee.add(_liquidityFee).add(_marketFee);
    }    
    function updateSellFees(uint256 _tokenRewardsFee, uint256 _liquidityFee, uint256 _marketFee) external onlyOwner {
        sellTokenRewardsFee = _tokenRewardsFee;
        sellLiquidityFee = _liquidityFee;
        sellMarketFee = _marketFee;
        sellTotalFees = _tokenRewardsFee.add(_liquidityFee).add(_marketFee);
    }
    function whitelistDxSale(address _presaleAddress, address _routerAddress) external onlyOwner {
        presaleAddress = _presaleAddress;
        canTransferBeforeTradingIsEnabled[presaleAddress] = true;
        dividendTracker.excludeFromDividends(_presaleAddress);
        excludeFromFees(_presaleAddress, true);

        canTransferBeforeTradingIsEnabled[_routerAddress] = true;
        dividendTracker.excludeFromDividends(_routerAddress);
        excludeFromFees(_routerAddress, true);
    }
    function updateDividendTracker(address newAddress) external onlyOwner {
        require(newAddress != address(dividendTracker), "MRFROG: The dividend tracker already has that address");

        MRFROGDividendTracker newDividendTracker = MRFROGDividendTracker(payable(newAddress));

        require(newDividendTracker.owner() == address(this), "MRFROG: The new dividend tracker must be owned by the MRFROG token contract");

        newDividendTracker.excludeFromDividends(address(newDividendTracker));
        newDividendTracker.excludeFromDividends(address(this));
        newDividendTracker.excludeFromDividends(address(uniswapV2Router));

        emit UpdateDividendTracker(newAddress, address(dividendTracker));

        dividendTracker = newDividendTracker;
    }
    function updateUniswapV2Router(address newAddress) external onlyOwner {
        require(newAddress != address(uniswapV2Router), "MRFROG: The router already has that address");
        emit UpdateUniswapV2Router(newAddress, address(uniswapV2Router));
        uniswapV2Router = IUniswapV2Router02(newAddress);
    }
    function excludeFromFees(address account, bool excluded) public onlyOwner {
        _isExcludedFromFees[account] = excluded;
    }       
    function blacklistAddress(address account, bool excluded) public onlyOwner {
        _isBlacklisted[account] = excluded;
        dividendTracker.excludeFromDividends(account);
    }    
    function excludeFromDividends(address account) public onlyOwner {
        dividendTracker.excludeFromDividends(account);
    }    

    function enableDividends(address account) public onlyOwner {
        dividendTracker.enableDividends(account);
    }

    function excludeMultipleAccountsFromFees(address[] calldata accounts, bool excluded) external onlyOwner {
        for(uint256 i = 0; i < accounts.length; i++) {
            _isExcludedFromFees[accounts[i]] = excluded;
        }

        emit ExcludeMultipleAccountsFromFees(accounts, excluded);
    }

    function setAutomatedMarketMakerPair(address pair, bool value) external onlyOwner {
        require(pair != uniswapV2Pair, "MRFROG: The PancakeSwap pair cannot be removed from automatedMarketMakerPairs");

        _setAutomatedMarketMakerPair(pair, value);
    }

    function _setAutomatedMarketMakerPair(address pair, bool value) private {
        require(automatedMarketMakerPairs[pair] != value, "MRFROG: Automated market maker pair is already set to that value");
        automatedMarketMakerPairs[pair] = value;

        if(value) {
            dividendTracker.excludeFromDividends(pair);
        }

        emit SetAutomatedMarketMakerPair(pair, value);
    }

    function updateGasForProcessing(uint256 newValue) public onlyOwner {
        require(newValue >= 200000 && newValue <= 1000000, "MRFROG: gasForProcessing must be between 200,000 and 500,000");
        require(newValue != gasForProcessing, "MRFROG: Cannot update gasForProcessing to same value");
        emit GasForProcessingUpdated(newValue, gasForProcessing);
        gasForProcessing = newValue;
    }

    function updateClaimWait(uint256 claimWait) external onlyOwner {
        dividendTracker.updateClaimWait(claimWait);
    }

    function getClaimWait() external view returns(uint256) {
        return dividendTracker.claimWait();
    }

    function getTotalDividendsDistributed() external view returns (uint256) {
        return dividendTracker.totalDividendsDistributed();
    }

    function isExcludedFromFees(address account) public view returns(bool) {
        return _isExcludedFromFees[account];
    }

    function withdrawableDividendOf(address account) public view returns(uint256) {
        return dividendTracker.withdrawableDividendOf(account);
    }

    function dividendTokenBalanceOf(address account) public view returns (uint256) {
        return dividendTracker.balanceOf(account);
    }

    function getAccountDividendsInfo(address account)
        external view returns (
            address,
            int256,
            int256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256) {
        return dividendTracker.getAccount(account);
    }

    function getAccountDividendsInfoAtIndex(uint256 index)
        external view returns (
            address,
            int256,
            int256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256) {
        return dividendTracker.getAccountAtIndex(index);
    }

    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(msg.sender, false);
    }

    function getLastProcessedIndex() external view returns(uint256) {
        return dividendTracker.getLastProcessedIndex();
    }

    function getNumberOfDividendTokenHolders() external view returns(uint256) {
        return dividendTracker.getNumberOfTokenHolders();
    }

    function getTradingIsEnabled() public view returns (bool) {
        return block.timestamp >= tradingEnabledTimestamp;
    }
    
    function swapAndLiquify(uint256 tokens) private  {
        uint256 marketFeeFull = tokens.mul(marketFee).div(100);
        uint256 tokensAfterFee = tokens - marketFeeFull;
        uint256 initHalf = tokens.div(2);
        uint256 half = tokensAfterFee.div(2);
        uint256 otherHalf = tokensAfterFee.sub(half);

        uint256 initialBalance = address(this).balance;

        swapTokensForEth(initHalf);
        uint256 newBalance = address(this).balance.sub(initialBalance);

        uint256 marketFeeBalance = newBalance.mul(marketFee).div(100);
        uint256 finalBalance = newBalance - marketFeeBalance;
        uint256 finalHalf = otherHalf;

        
        if(marketTokenAddressForFee != address(0)){
            swapEthForTokens(marketFeeBalance, marketTokenAddressForFee, marketAddress);
        }else{
            (bool sent,) = marketAddress.call{value: marketFeeBalance}("");
        }        
       

        addLiquidity(finalHalf, finalBalance);
        emit SwapAndLiquify(half, finalBalance, otherHalf);
    }
    
    function swapEthForTokens(uint256 ethAmount, address tokenAddress, address receiver) private {
        address[] memory path = new address[](2);
        path[0] = uniswapV2Router.WETH();
        path[1] = tokenAddress;

        uniswapV2Router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: ethAmount}(
            0,
            path,
            receiver,
            block.timestamp
        );
    }

    function swapTokensForEth(uint256 tokenAmount) private {

        
        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 swapTokensForTokens(uint256 tokenAmount, address recipient) private {
       
        address[] memory path = new address[](3);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();
        path[2] = DividendToken;

        _approve(address(this), address(uniswapV2Router), tokenAmount);

        uniswapV2Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
            tokenAmount,
            0,
            path,
            recipient,
            block.timestamp
        );
        
    }
    
    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) internal {
        
        _approve(address(this), address(uniswapV2Router), tokenAmount);

       uniswapV2Router.addLiquidityETH{value: ethAmount}(
            address(this),
            tokenAmount,
            0, 
            0, 
            burnAddress,
            block.timestamp
        );
        
    }

    function swapAndSendDividends(uint256 tokens) private {
        address payable diviTracker = address(dividendTracker);
        bool success = false;
        uint256 dividends;
        if(DividendToken != address(0)){
            swapTokensForTokens(tokens, address(this));
            dividends = IERC20(DividendToken).balanceOf(address(this));
            success = IERC20(DividendToken).transfer(address(dividendTracker), dividends);
        }else{
            uint256 initialBalance = address(this).balance;
            swapTokensForEth(tokens);
            uint256 newBalance = address(this).balance.sub(initialBalance);
            dividends = newBalance;
            (bool sent, bytes memory data) = diviTracker.call{value: newBalance}("");
            success = sent;
        }
        if (success) {
            dividendTracker.distributeTokenDividends(dividends);
            emit SendDividends(tokens, dividends);
        }
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        require(!_isBlacklisted[from], "Blacklisted address cannot transfer!");
        require(!_isBlacklisted[to], "Blacklisted address cannot transfer!");
        require(from != address(0), "ERC20: transfer to the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        bool tradingIsEnabled = getTradingIsEnabled();

        if(!tradingIsEnabled) {
            require(canTransferBeforeTradingIsEnabled[from], "MRFROG: This account cannot send tokens until trading is enabled");
        }

        if(amount == 0) {
            super._transfer(from, to, 0);
            return;
        }
        bool buyOrSell = false;
        if (
            from != owner() &&
            to != owner() &&
            to != address(0) &&
            to != address(0xdead) &&
            to != uniswapV2Pair &&
            automatedMarketMakerPairs[from]
        ) {
            require(
                amount <= maxBuyTranscationAmount,
                "Transfer amount exceeds the maxTxAmount."
            );
            
            uint256 contractBalanceRecepient = balanceOf(to);
            require(
                contractBalanceRecepient + amount <= _maxWalletToken,
                "Exceeds maximum wallet token amount."
            );

            previousMarketFee = marketFee;
            previousTokenRewardsFee = tokenRewardsFee;
            previousLiquidityFee = liquidityFee;
            previousTotalFees = totalFees;
            marketFee = buyMarketFee;
            tokenRewardsFee = buyTokenRewardsFee;
            liquidityFee = buyLiquidityFee;
            totalFees = buyTotalFees;
            buyOrSell = true;
        }
         
        if( 
            !swapping &&
            tradingIsEnabled &&
            automatedMarketMakerPairs[to] && 
            from != address(uniswapV2Router) && 
            !_isExcludedFromFees[to] 
        ) {
            require(amount <= maxSellTransactionAmount, "Sell transfer amount exceeds the maxSellTransactionAmount.");
            previousMarketFee = marketFee;
            previousTokenRewardsFee = tokenRewardsFee;
            previousLiquidityFee = liquidityFee;
            previousTotalFees = totalFees;
            marketFee = sellMarketFee;
            tokenRewardsFee = sellTokenRewardsFee;
            liquidityFee = sellLiquidityFee;
            totalFees = sellTotalFees;
            buyOrSell = true;
        }
        uint256 contractTokenBalance = balanceOf(address(this)); 
        bool canSwap = contractTokenBalance >= swapTokensAtAmount;
        if(
            tradingIsEnabled && 
            canSwap &&
            !swapping &&
            !automatedMarketMakerPairs[from] &&
            from != burnAddress &&
            to != burnAddress
        ) {
            swapping = true;
            uint256 dividendAmount = swapTokensAtAmount.mul(tokenRewardsFee).div(100);
            uint256 adminAmount = swapTokensAtAmount.sub(dividendAmount);
            swapAndLiquify(adminAmount);
            swapAndSendDividends(dividendAmount);
            swapping = false;
        }
        bool takeFee = tradingIsEnabled && !swapping;
        if(_isExcludedFromFees[from] || _isExcludedFromFees[to]) {
            takeFee = false;
        }
        if(takeFee) {
            uint256 fees = amount.mul(totalFees).div(100);

            amount = amount.sub(fees);

            super._transfer(from, address(this), fees);
        }
        super._transfer(from, to, amount);

        restoreFees();
        
        try dividendTracker.setBalance(payable(from), balanceOf(from)) {} catch {}
        try dividendTracker.setBalance(payable(to), balanceOf(to)) {} catch {}
        bool canDividend = block.timestamp >= processDividendTime;
        if(!swapping && canDividend) {
            uint256 gas = gasForProcessing;
            processDividendTime += dividendTime;
            try dividendTracker.process(gas) returns (uint256 iterations, uint256 claims, uint256 lastProcessedIndex) {
                emit ProcessedDividendTracker(iterations, claims, lastProcessedIndex, true, gas, tx.origin);
            }
            catch {

            }
        }
    }
    
}


contract MRFROGDividendTracker is DividendPayingToken, Ownable {
    using SafeMath for uint256;
    using SafeMathInt for int256;
    using IterableMapping for IterableMapping.Map;

    IterableMapping.Map private tokenHoldersMap;
    uint256 public lastProcessedIndex;

    mapping (address => bool) public excludedFromDividends;

    mapping (address => uint256) public lastClaimTimes;

    uint256 public claimWait;
    uint256 public minimumTokenBalanceForDividends;

    event ExcludeFromDividends(address indexed account);
    event ClaimWaitUpdated(uint256 indexed newValue, uint256 indexed oldValue);

    event Claim(address indexed account, uint256 amount, bool indexed automatic);

    constructor() public DividendPayingToken("MRFROG_Dividend_Tracker", "MRFROG_Dividend_Tracker") {
        claimWait = 3600;
        minimumTokenBalanceForDividends = 100000 * (10**18);
    }