// Contract: OPTICS
// Target function: function distributeDividends() public override payable
(address _rewardToken) external;

  ///@dev This event MUST emit when ether is distributed to token holders.
  ///@param from The address which sends ether to this contract.
  ///@param weiAmount The amount of distributed ether in wei.
  event DividendsDistributed(
     address indexed from,
     uint256 weiAmount
  );

  ///@dev This event MUST emit when an address withdraws their dividend.
  ///@param to The address which withdraws ether from this contract.
  ///@param weiAmount The amount of withdrawn ether in wei.
  event DividendWithdrawn(
     address indexed to,
     uint256 weiAmount
  );
}

library SafeMath {
     /**
      * @dev Returns the addition of two unsigned integers, reverting on
      * overflow.
      *
      * Counterpart to Solidity's `+` operator.
      *
      * Requirements:
      *
      * - Addition cannot overflow.
      */
     function add(uint256 a, uint256 b) internal pure returns (uint256) {
          uint256 c = a + b;
          require(c >= a, "SafeMath: addition overflow");

          return c;
     }

     /**
      * @dev Returns the subtraction of two unsigned integers, reverting on
      * overflow (when the result is negative).
      *
      * Counterpart to Solidity's `-` operator.
      *
      * Requirements:
      *
      * - Subtraction cannot overflow.
      */
     function sub(uint256 a, uint256 b) internal pure returns (uint256) {
          return sub(a, b, "SafeMath: subtraction overflow");
     }

     /**
      * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
      * overflow (when the result is negative).
      *
      * Counterpart to Solidity's `-` operator.
      *
      * Requirements:
      *
      * - Subtraction cannot overflow.
      */
     function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
          require(b <= a, errorMessage);
          uint256 c = a - b;

          return c;
     }

     /**
      * @dev Returns the multiplication of two unsigned integers, reverting on
      * overflow.
      *
      * Counterpart to Solidity's `*` operator.
      *
      * Requirements:
      *
      * - Multiplication cannot overflow.
      */
     function mul(uint256 a, uint256 b) internal pure returns (uint256) {
          //Gas optimization: this is cheaper than requiring 'a' not being zero, but the
          //benefit is lost if 'b' is also tested.
          //See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
          if (a == 0) {
               return 0;
          }

          uint256 c = a * b;
          require(c / a == b, "SafeMath: multiplication overflow");

          return c;
     }

     /**
      * @dev Returns the integer division of two unsigned integers. Reverts on
      * division by zero. The result is rounded towards zero.
      *
      * Counterpart to Solidity's `/` operator. Note: 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 div(uint256 a, uint256 b) internal pure returns (uint256) {
          return div(a, b, "SafeMath: division by zero");
     }

     /**
      * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
      * division by zero. The result is rounded towards zero.
      *
      * Counterpart to Solidity's `/` operator. Note: 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 div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
          require(b > 0, errorMessage);
          uint256 c = a / b;
          //assert(a == b * c + a % b); //There is no case in which this doesn't hold

          return c;
     }

     /**
      * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
      * Reverts 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) 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; 
       
       //Mainnet

       rewardTokens.push(address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)); //USDC - 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
       
       nextRewardToken = rewardTokens[0];
  }

  

  ///@dev Distributes dividends whenever ether is paid to this contract.
  receive() external payable {
     distributeDividends();
  }