// Contract: TAROTCULT
// Target function: function accumulativeDividendOf(address _owner, address _rewardToken) public view override returns(uint256)
:
        *
        * - The divisor cannot be zero.
        */
       function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
       }

       /**
        * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
        * Reverts with custom message when dividing by zero.
        *
        * Counterpart to Solidity's `%` operator. This function uses a `revert`
        * opcode (which leaves remaining gas untouched) while Solidity uses an
        * invalid opcode to revert (consuming all remaining gas).
        *
        * Requirements:
        *
        * - The divisor cannot be zero.
        */
       function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
       }
}

contract Ownable is Context {
       address private _owner;

       event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
       
       /**
        * @dev Initializes the contract setting the deployer as the initial owner.
        */
       constructor () {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
       }

       /**
        * @dev Returns the address of the current owner.
        */
       function owner() public view returns (address) {
              return _owner;
       }

       /**
        * @dev Throws if called by any account other than the owner.
        */
       modifier onlyOwner() {
              require(_owner == _msgSender(), "Ownable: caller is not the owner");
              _;
       }

       /**
        * @dev Leaves the contract without owner. It will not be possible to call
        * `onlyOwner` functions anymore. Can only be called by the current owner.
        *
        * NOTE: Renouncing ownership will leave the contract without an owner,
        * thereby removing any functionality that is only available to the owner.
        */
       function renounceOwnership() public virtual onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = address(0);
       }

       /**
        * @dev Transfers ownership of the contract to a new account (`newOwner`).
        * Can only be called by the current owner.
        */
       function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
       }
}



library SafeMathInt {
       int256 private constant MIN_INT256 = int256(1) << 255;
       int256 private constant MAX_INT256 = ~(int256(1) << 255);

       /**
        * @dev Multiplies two int256 variables and fails on overflow.
        */
       function mul(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a * b;

              //Detect overflow when multiplying MIN_INT256 with -1
              require(c != MIN_INT256 || (a & MIN_INT256) != (b & MIN_INT256));
              require((b == 0) || (c / b == a));
              return c;
       }

       /**
        * @dev Division of two int256 variables and fails on overflow.
        */
       function div(int256 a, int256 b) internal pure returns (int256) {
              //Prevent overflow when dividing MIN_INT256 by -1
              require(b != -1 || a != MIN_INT256);

              //Solidity already throws when dividing by 0.
              return a / b;
       }

       /**
        * @dev Subtracts two int256 variables and fails on overflow.
        */
       function sub(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a - b;
              require((b >= 0 && c <= a) || (b < 0 && c > a));
              return c;
       }

       /**
        * @dev Adds two int256 variables and fails on overflow.
        */
       function add(int256 a, int256 b) internal pure returns (int256) {
              int256 c = a + b;
              require((b >= 0 && c >= a) || (b < 0 && c < a));
              return c;
       }

       /**
        * @dev Converts to absolute value, and fails on overflow.
        */
       function abs(int256 a) internal pure returns (int256) {
              require(a != MIN_INT256);
              return a < 0 ? -a : a;
       }


       function toUint256Safe(int256 a) internal pure returns (uint256) {
              require(a >= 0);
              return uint256(a);
       }
}

library SafeMathUint {
  function toInt256Safe(uint256 a) internal pure returns (int256) {
       int256 b = int256(a);
       require(b >= 0);
       return b;
  }
}


interface IUniswapV2Router01 {
       function factory() external pure returns (address);
       function WETH() external pure returns (address);

       function addLiquidity(
              address tokenA,
              address tokenB,
              uint amountADesired,
              uint amountBDesired,
              uint amountAMin,
              uint amountBMin,
              address to,
              uint deadline
       ) external returns (uint amountA, uint amountB, uint liquidity);
       function addLiquidityETH(
              address token,
              uint amountTokenDesired,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline
       ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
       function removeLiquidity(
              address tokenA,
              address tokenB,
              uint liquidity,
              uint amountAMin,
              uint amountBMin,
              address to,
              uint deadline
       ) external returns (uint amountA, uint amountB);
       function removeLiquidityETH(
              address token,
              uint liquidity,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline
       ) external returns (uint amountToken, uint amountETH);
       function removeLiquidityWithPermit(
              address tokenA,
              address tokenB,
              uint liquidity,
              uint amountAMin,
              uint amountBMin,
              address to,
              uint deadline,
              bool approveMax, uint8 v, bytes32 r, bytes32 s
       ) external returns (uint amountA, uint amountB);
       function removeLiquidityETHWithPermit(
              address token,
              uint liquidity,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline,
              bool approveMax, uint8 v, bytes32 r, bytes32 s
       ) external returns (uint amountToken, uint amountETH);
       function swapExactTokensForTokens(
              uint amountIn,
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline
       ) external returns (uint[] memory amounts);
       function swapTokensForExactTokens(
              uint amountOut,
              uint amountInMax,
              address[] calldata path,
              address to,
              uint deadline
       ) external returns (uint[] memory amounts);
       function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
              external
              payable
              returns (uint[] memory amounts);
       function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
              external
              returns (uint[] memory amounts);
       function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
              external
              returns (uint[] memory amounts);
       function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
              external
              payable
              returns (uint[] memory amounts);

       function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
       function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
       function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
       function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
       function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

interface IUniswapV2Router02 is IUniswapV2Router01 {
       function removeLiquidityETHSupportingFeeOnTransferTokens(
              address token,
              uint liquidity,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline
       ) external returns (uint amountETH);
       function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
              address token,
              uint liquidity,
              uint amountTokenMin,
              uint amountETHMin,
              address to,
              uint deadline,
              bool approveMax, uint8 v, bytes32 r, bytes32 s
       ) external returns (uint amountETH);

       function swapExactTokensForTokensSupportingFeeOnTransferTokens(
              uint amountIn,
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline
       ) external;
       function swapExactETHForTokensSupportingFeeOnTransferTokens(
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline
       ) external payable;
       function swapExactTokensForETHSupportingFeeOnTransferTokens(
              uint amountIn,
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline
       ) external;
}

contract DividendPayingToken is DividendPayingTokenInterface, DividendPayingTokenOptionalInterface, Ownable {
  using SafeMath for uint256;
  using SafeMathUint for uint256;
  using SafeMathInt for int256;

  //With `magnitude`, we can properly distribute dividends even if the amount of received ether is small.
  //For more discussion about choosing the value of `magnitude`,
  // see https://github.com/ethereum/EIPs/issues/1726#issuecomment-472352728
  uint256 constant internal magnitude = 2**128;

  mapping(address => uint256) internal magnifiedDividendPerShare;
  address[] public rewardTokens;
  address public nextRewardToken;
  uint256 public rewardTokenCounter;
  
  IUniswapV2Router02 public immutable uniswapV2Router;
  
  
  //About dividendCorrection:
  //If the token balance of a `_user` is never changed, the dividend of `_user` can be computed with:
  //  `dividendOf(_user) = dividendPerShare * balanceOf(_user)`.
  //When `balanceOf(_user)` is changed (via minting/burning/transferring tokens),
  //  `dividendOf(_user)` should not be changed,
  //  but the computed value of `dividendPerShare * balanceOf(_user)` is changed.
  //To keep the `dividendOf(_user)` unchanged, we add a correction term:
  //  `dividendOf(_user) = dividendPerShare * balanceOf(_user) + dividendCorrectionOf(_user)`,
  //  where `dividendCorrectionOf(_user)` is updated whenever `balanceOf(_user)` is changed:
  //  `dividendCorrectionOf(_user) = dividendPerShare * (old balanceOf(_user)) - (new balanceOf(_user))`.
  //So now `dividendOf(_user)` returns the same value before and after `balanceOf(_user)` is changed.
  mapping(address => mapping(address => int256)) internal magnifiedDividendCorrections;
  mapping(address => mapping(address => uint256)) internal withdrawnDividends;
  
  mapping (address => uint256) public holderBalance;
  uint256 public totalBalance;

  mapping(address => uint256) public totalDividendsDistributed;
  
  constructor(){
         IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); //router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
         uniswapV2Router = _uniswapV2Router; 
         

         rewardTokens.push(address(0xf0f9D895aCa5c8678f706FB8216fa22957685A13)); //CULT - 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
         
         nextRewardToken = rewardTokens[0];
  }

  

  ///@dev Distributes dividends whenever ether is paid to this contract.
  receive() external payable {
       distributeDividends();
  }

  ///@notice Distributes ether to token holders as dividends.
  ///@dev It reverts if the total supply of tokens is 0.
  ///It emits the `DividendsDistributed` event if the amount of received ether is greater than 0.
  ///About undistributed ether:
  ///  In each distribution, there is a small amount of ether not distributed,
  ///       the magnified amount of which is
  ///       `(msg.value * magnitude) % totalSupply()`.
  ///  With a well-chosen `magnitude`, the amount of undistributed ether
  ///       (de-magnified) in a distribution can be less than 1 wei.
  ///  We can actually keep track of the undistributed ether in a distribution
  ///       and try to distribute it in the next distribution,
  ///       but keeping track of such data on-chain costs much more than
  ///       the saved ether, so we don't do that.
       
  function distributeDividends() public override payable { 
       require(totalBalance > 0);
       uint256 initialBalance = IERC20(nextRewardToken).balanceOf(address(this));
       buyTokens(msg.value, nextRewardToken);
       uint256 newBalance = IERC20(nextRewardToken).balanceOf(address(this)).sub(initialBalance);
       if (newBalance > 0) {
         magnifiedDividendPerShare[nextRewardToken] = magnifiedDividendPerShare[nextRewardToken].add(
              (newBalance).mul(magnitude) / totalBalance
         );
         emit DividendsDistributed(msg.sender, newBalance);

         totalDividendsDistributed[nextRewardToken] = totalDividendsDistributed[nextRewardToken].add(newBalance);
       }
       rewardTokenCounter = rewardTokenCounter == rewardTokens.length - 1 ? 0 : rewardTokenCounter + 1;
       nextRewardToken = rewardTokens[rewardTokenCounter];
  }
  
  //useful for buybacks or to reclaim any BNB on the contract in a way that helps holders.
       function buyTokens(uint256 bnbAmountInWei, address rewardToken) internal {
              //generate the uniswap pair path of weth -> eth
              address[] memory path = new address[](2);
              path[0] = uniswapV2Router.WETH();
              path[1] = rewardToken;

              //make the swap
              uniswapV2Router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: bnbAmountInWei}(
                     0, //accept any amount of Ethereum
                     path,
                     address(this),
                     block.timestamp
              );
       }
  
  ///@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 withdrawDividend(address _rewardToken) external virtual override {
       _withdrawDividendOfUser(payable(msg.sender), _rewardToken);
  }

  ///@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, address _rewardToken) internal returns (uint256) {
       uint256 _withdrawableDividend = withdrawableDividendOf(user, _rewardToken);
       if (_withdrawableDividend > 0) {
         withdrawnDividends[user][_rewardToken] = withdrawnDividends[user][_rewardToken].add(_withdrawableDividend);
         emit DividendWithdrawn(user, _withdrawableDividend);
         IERC20(_rewardToken).transfer(user, _withdrawableDividend);
         return _withdrawableDividend;
       }

       return 0;
  }


  ///@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, address _rewardToken) external view override returns(uint256) {
       return withdrawableDividendOf(_owner, _rewardToken);
  }

  ///@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, 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];
  }