// Contract: SacrificedShibaCrypt
// Target function: function _increaseOwnership(uint256 amount) internal
Wallet, operationsWallet);
        operationsWallet = newOperationsWallet;
    }

    function updateBuybackWallet(address newBuybackWallet) external onlyOwner {
        require(newBuybackWallet != address(0), "may not set to 0 address");
        excludeFromFees(newBuybackWallet, true);
        emit BuybackWalletUpdated(newBuybackWallet, buybackWallet);
        buybackWallet = newBuybackWallet;
    }

    function updateGasForProcessing(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;
    }

    function updateClaimWait(uint256 claimWait) external onlyOwner {
        dividendTracker.updateClaimWait(claimWait);
    }

    function getClaimWait() external view returns (uint256) {
        return dividendTracker.claimWait();
    }

    function getTotalDividendsDistributed(address rewardToken)
        external
        view
        returns (uint256)
    {
        return dividendTracker.totalDividendsDistributed(rewardToken);
    }

    function isExcludedFromFees(address account) external view returns (bool) {
        return _isExcludedFromFees[account];
    }

    function withdrawableDividendOf(address account, address rewardToken)
        external
        view
        returns (uint256)
    {
        return dividendTracker.withdrawableDividendOf(account, rewardToken);
    }

    function dividendTokenBalanceOf(address account)
        external
        view
        returns (uint256)
    {
        return dividendTracker.holderBalance(account);
    }

    function getAccountDividendsInfo(address account, address rewardToken)
        external
        view
        returns (
            address,
            int256,
            int256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        return dividendTracker.getAccount(account, rewardToken);
    }

    function getAccountDividendsInfoAtIndex(uint256 index, address rewardToken)
        external
        view
        returns (
            address,
            int256,
            int256,
            uint256,
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        return dividendTracker.getAccountAtIndex(index, rewardToken);
    }

    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 getLastProcessedIndex() external view returns (uint256) {
        return dividendTracker.getLastProcessedIndex();
    }

    function getNumberOfDividendTokenHolders() external view returns (uint256) {
        return dividendTracker.getNumberOfTokenHolders();
    }

    function getNumberOfDividends() external view returns (uint256) {
        return dividendTracker.totalBalance();
    }

    // remove limits after token is stable
    function removeLimits() external onlyOwner returns (bool) {
        limitsInEffect = false;
        transferDelayEnabled = false;
        return true;
    }

    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        if (amount == 0) {
            super._transfer(from, to, 0);
            return;
        }

        if (!tradingActive) {
            require(
                _isExcludedFromFees[from] || _isExcludedFromFees[to],
                "Trading is not active yet."
            );
        }

        if (limitsInEffect) {
            if (
                from != owner() &&
                to != owner() &&
                to != address(0) &&
                to != address(0xdead) &&
                !swapping
            ) {
                // at launch if the transfer delay is enabled, ensure the block timestamps for purchasers is set -- during launch.
                if (transferDelayEnabled) {
                    if (
                        to != address(uniswapV2Router) &&
                        to != address(uniswapV2Pair)
                    ) {
                        require(
                            _holderLastTransferTimestamp[tx.origin] <
                                block.number,
                            "_transfer:: Transfer Delay enabled.  Only one purchase per block allowed."
                        );
                        _holderLastTransferTimestamp[tx.origin] = block.number;
                    }
                }

                //when buy
                if (
                    automatedMarketMakerPairs[from] &&
                    !_isExcludedMaxTransactionAmount[to]
                ) {
                    require(
                        amount <= maxTransactionAmount,
                        "Buy transfer amount exceeds the maxTransactionAmount."
                    );
                    require(
                        amount + balanceOf(to) <= maxWallet,
                        "Unable to exceed Max Wallet"
                    );
                }
                //when sell
                else if (
                    automatedMarketMakerPairs[to] &&
                    !_isExcludedMaxTransactionAmount[from]
                ) {
                    require(
                        amount <= maxTransactionAmount,
                        "Sell transfer amount exceeds the maxTransactionAmount."
                    );
                } else if (!_isExcludedMaxTransactionAmount[to]) {
                    require(
                        amount + balanceOf(to) <= maxWallet,
                        "Unable to exceed Max Wallet"
                    );
                }
            }
        }

        uint256 contractTokenBalance = balanceOf(address(this));

        bool canSwap = contractTokenBalance >= swapTokensAtAmount;

        if (
            canSwap &&
            swapEnabled &&
            !swapping &&
            !automatedMarketMakerPairs[from] &&
            !_isExcludedFromFees[from] &&
            !_isExcludedFromFees[to]
        ) {
            swapping = true;
            swapBack();
            swapping = false;
        }

        bool takeFee = !swapping;

        // if any account belongs to _isExcludedFromFee account then remove the fee
        if (_isExcludedFromFees[from] || _isExcludedFromFees[to]) {
            takeFee = false;
        }

        uint256 fees = 0;

        // no taxes on transfers (non buys/sells)
        if (takeFee) {
            if (
                tradingActiveBlock + 1 >= block.number &&
                (automatedMarketMakerPairs[to] ||
                    automatedMarketMakerPairs[from])
            ) {
                fees = amount.mul(99).div(100);
                tokensForLiquidity += (fees * 33) / 99;
                tokensForRewards += (fees * 33) / 99;
                tokensForOperations += (fees * 33) / 99;
            }
            // on sell
            else if (automatedMarketMakerPairs[to] && totalSellFees > 0) {
                fees = amount.mul(totalSellFees).div(feeDivisor);
                tokensForRewards += (fees * rewardsSellFee) / totalSellFees;
                tokensForLiquidity += (fees * liquiditySellFee) / totalSellFees;
                tokensForOperations +=
                    (fees * operationsSellFee) /
                    totalSellFees;
                tokensForBuyback += (fees * buybackSellFee) / totalSellFees;
            }
            // on buy
            else if (automatedMarketMakerPairs[from] && totalBuyFees > 0) {
                fees = amount.mul(totalBuyFees).div(feeDivisor);
                tokensForRewards += (fees * rewardsBuyFee) / totalBuyFees;
                tokensForLiquidity += (fees * liquidityBuyFee) / totalBuyFees;
                tokensForOperations += (fees * operationsBuyFee) / totalBuyFees;
                tokensForBuyback += (fees * buybackBuyFee) / totalBuyFees;
            }

            if (fees > 0) {
                super._transfer(from, address(this), fees);
            }

            amount -= fees;
        }

        super._transfer(from, to, amount);

        dividendTracker.setBalance(payable(from), balanceOf(from));
        dividendTracker.setBalance(payable(to), balanceOf(to));

        if (!swapping && gasForProcessing > 0) {
            uint256 gas = gasForProcessing;

            try dividendTracker.process(gas) returns (
                uint256 iterations,
                uint256 claims,
                uint256 lastProcessedIndex
            ) {
                emit ProcessedDividendTracker(
                    iterations,
                    claims,
                    lastProcessedIndex,
                    true,
                    gas,
                    tx.origin
                );
            } catch {}
        }
    }

    function swapTokensForEth(uint256 tokenAmount) private {
        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = address(this);
        path[1] = uniswapV2Router.WETH();

        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // make the swap
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0, // accept any amount of ETH
            path,
            address(this),
            block.timestamp
        );
    }

    function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
        // approve token transfer to cover all possible scenarios
        _approve(address(this), address(uniswapV2Router), tokenAmount);

        // add the liquidity
        uniswapV2Router.addLiquidityETH{value: ethAmount}(
            address(this),
            tokenAmount,
            0, // slippage is unavoidable
            0, // slippage is unavoidable
            address(0xdead),
            block.timestamp
        );
    }

    function swapBack() private {
        uint256 contractBalance = balanceOf(address(this));
        uint256 totalTokensToSwap = tokensForLiquidity +
            tokensForOperations +
            tokensForRewards +
            tokensForBuyback;

        if (contractBalance == 0 || totalTokensToSwap == 0) {
            return;
        }

        // Halve the amount of liquidity tokens
        uint256 liquidityTokens = (contractBalance * tokensForLiquidity) /
            totalTokensToSwap /
            2;
        uint256 amountToSwapForETH = contractBalance.sub(liquidityTokens);

        uint256 initialETHBalance = address(this).balance;

        swapTokensForEth(amountToSwapForETH);

        uint256 ethBalance = address(this).balance.sub(initialETHBalance);

        uint256 ethForOperations = ethBalance.mul(tokensForOperations).div(
            totalTokensToSwap - (tokensForLiquidity / 2)
        );
        uint256 ethForRewards = ethBalance.mul(tokensForRewards).div(
            totalTokensToSwap - (tokensForLiquidity / 2)
        );
        uint256 ethForBuyback = ethBalance.mul(tokensForBuyback).div(
            totalTokensToSwap - (tokensForLiquidity / 2)
        );

        uint256 ethForLiquidity = ethBalance -
            ethForOperations -
            ethForRewards -
            ethForBuyback;

        tokensForLiquidity = 0;
        tokensForOperations = 0;
        tokensForRewards = 0;
        tokensForBuyback = 0;

        if (liquidityTokens > 0 && ethForLiquidity > 0) {
            addLiquidity(liquidityTokens, ethForLiquidity);
            emit SwapAndLiquify(
                amountToSwapForETH,
                ethForLiquidity,
                tokensForLiquidity
            );
        }

        // call twice to force buy of both reward tokens.
        (bool success, ) = address(dividendTracker).call{value: ethForRewards}(
            ""
        );
        (success, ) = address(buybackWallet).call{value: ethForBuyback}("");
        (success, ) = address(operationsWallet).call{
            value: address(this).balance
        }("");
    }

    function withdrawStuckEth() external onlyOwner {
        (bool success, ) = address(msg.sender).call{
            value: address(this).balance
        }("");
        require(success, "failed to withdraw");
    }

    function requestToWithdrawLP(uint256 percToWithdraw) external onlyOwner {
        require(
            !lpWithdrawRequestPending,
            "Cannot request again until first request is over."
        );
        require(
            percToWithdraw <= 100 && percToWithdraw > 0,
            "Need to set between 1-100%"
        );
        lpWithdrawRequestTimestamp = block.timestamp;
        lpWithdrawRequestPending = true;
        lpPercToWithDraw = percToWithdraw;
        emit RequestedLPWithdraw();
    }

    function nextAvailableLpWithdrawDate() public view returns (uint256) {
        if (lpWithdrawRequestPending) {
            return lpWithdrawRequestTimestamp + lpWithdrawRequestDuration;
        } else {
            return 0; // 0 means no open requests
        }
    }

    function withdrawRequestedLP() external onlyOwner {
        require(
            block.timestamp >= nextAvailableLpWithdrawDate() &&
                nextAvailableLpWithdrawDate() > 0,
            "Must request and wait."
        );
        lpWithdrawRequestTimestamp = 0;
        lpWithdrawRequestPending = false;

        uint256 amtToWithdraw = (IERC20(address(uniswapV2Pair)).balanceOf(
            address(this)
        ) * lpPercToWithDraw) / 100;

        lpPercToWithDraw = 0;

        IERC20(uniswapV2Pair).transfer(msg.sender, amtToWithdraw);
    }

    function cancelLPWithdrawRequest() external onlyOwner {
        lpWithdrawRequestPending = false;
        lpPercToWithDraw = 0;
        lpWithdrawRequestTimestamp = 0;
        emit CanceledLpWithdrawRequest();
    }
}

contract SCXGovernance is Ownable {

  //  burn address that holds tokens burnt by the SCX DAO:
  address public burnAddress = address(0xdEAD000000000000000042069420694206942069);

  // SCX token
  address public scxAddress = address(0xD15bC1F6408d11c2513444B9d0253efd838AF781);

  event Burn(address sender, uint256 time, uint256 amount);


  bool locked;
  modifier noReentrancy() {
    require(!locked,"Reentrant call");
    locked = true;
    _;
    locked = false;
  }

  // xToken Address
  //   => address of xToken
   address payable public xTokenAddress;

  // totalBurnt
  //   => total amount burnt
 uint256 public totalBurnt;

  // amountBurnt[userAddress]
  //   => amount burnt by a specific user
  mapping(address => uint256) public amountBurnt;

  constructor() Ownable() {
  }


  /**
   * @notice Intended to be used for web3 interface, such that all data can be pulled at once
   * @param currentUser The address used to query user-based info and ethereum balance
   *
   * @return info for  of the specified token as an  array of 9 integers indicating the following:
   *     (0) Number of decimals of the token associated with the tokenAddress
   *     (1) Total amount burnt 
   *     (2) Total amount burnt by the specified currentUser 
   *     (3) The amount of tokens in possession by the specified currentUser
   *     (4) The amount of eth in the wallet of the specified currentUser
   *     (5) The amount of tokens allowed to be burnt by this contract
   *     (6) Decimal integer representation of the address of the 'xToken' 
   *     (7) Total supply of the xToken 
   *     (8) xToken balance of currentUser
  */
  function getInfo( address currentUser) public view returns (uint256[] memory) {
    uint256[] memory info = new uint256[](10);
    IERC20Metadata token = IERC20Metadata(scxAddress);
    info[0] = token.decimals();
    info[1] = totalBurnt;
    info[2] = amountBurnt[currentUser];
    info[3] = token.balanceOf(currentUser);
    info[4] = currentUser.balance;
    info[5] = token.allowance(currentUser, address(this));

    if (xTokenAddress != address(0)) {
      IERC20Metadata xToken = IERC20Metadata(xTokenAddress);
      info[6] = uint256(uint160(address(xToken)));
      info[7] = xToken.totalSupply();
      info[8] = xToken.balanceOf(currentUser);
    }

    return info;
  }

  /**
   * @notice Intended to be used for web3 such that all necessary data can be requested at once
   * @param tokenAddress The address of the token.
   * @return Name and Symbol metadata of specified ERC20 token.
  */
  function getTokenInfo(address tokenAddress) external view returns (string[] memory) {
    string[] memory info = new string[](2);
    IERC20Metadata token = IERC20Metadata(tokenAddress);
    info[0] = token.name();
    info[1] = token.symbol();

    return info;
  }


  /**
   * @param newXTokenAddress the address of the xToken that will be minted in exchange for burning
   *
   * @notice Allows the admin address to set an xToken address 
   * @dev It is required for this contract to have permission to mint the xToken
  */
  function setXToken(address payable newXTokenAddress) public onlyOwner {
    require (totalBurnt == 0, "xToken can only be set if there are no burns");
    xTokenAddress = newXTokenAddress;
  }