// Contract: DEKU
// Target function: function setShare(address shareholder, uint256 amount) external override onlyToken
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.7;

/**
 * Standard SafeMath, stripped down to just add/sub/mul/div
 */
library SafeMath {
	function add(uint256 a, uint256 b) internal pure returns (uint256) {
		uint256 c = a + b;
		require(c >= a, "SafeMath: addition overflow");

		return c;
	}

	function sub(uint256 a, uint256 b) internal pure returns (uint256) {
		return sub(a, b, "SafeMath: subtraction overflow");
	}

	function sub(
		uint256 a,
		uint256 b,
		string memory errorMessage
	) internal pure returns (uint256) {
		require(b <= a, errorMessage);
		uint256 c = a - b;

		return c;
	}

	function mul(uint256 a, uint256 b) internal pure returns (uint256) {
		if (a == 0) {
			return 0;
		}

		uint256 c = a * b;
		require(c / a == b, "SafeMath: multiplication overflow");

		return c;
	}

	function div(uint256 a, uint256 b) internal pure returns (uint256) {
		return div(a, b, "SafeMath: division by zero");
	}

	function div(
		uint256 a,
		uint256 b,
		string memory errorMessage
	) internal pure returns (uint256) {
		// Solidity only automatically asserts when dividing by 0
		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;
	}
}

/**
 * BEP20 standard interface.
 */
interface IBEP20 {
	function totalSupply() external view returns (uint256);

	function decimals() external view returns (uint8);

	function symbol() external view returns (string memory);

	function name() external view returns (string memory);

	function getOwner() external view returns (address);

	function balanceOf(address account) external view returns (uint256);

	function transfer(address recipient, uint256 amount) external returns (bool);

	function allowance(address _owner, address spender) external view returns (uint256);

	function approve(address spender, uint256 amount) external returns (bool);

	function transferFrom(
		address sender,
		address recipient,
		uint256 amount
	) external returns (bool);

	event Transfer(address indexed from, address indexed to, uint256 value);
	event Approval(address indexed owner, address indexed spender, uint256 value);
}

/**
 * Allows for contract ownership along with multi-address authorization
 */
abstract contract Auth {
	address internal owner;

	constructor(address _owner) {
		owner = _owner;
	}

	/**
	 * Function modifier to require caller to be contract deployer
	 */
	modifier onlyOwner() {
		require(isOwner(msg.sender), "!Owner");
		_;
	}

	/**
	 * Check if address is owner
	 */
	function isOwner(address account) private view returns (bool) {
		return account == owner;
	}

	/**
	 * Transfer ownership to new address. Caller must be deployer. Leaves old deployer authorized
	 */
	function transferOwnership(address payable adr) public onlyOwner {
		owner = adr;
		emit OwnershipTransferred(adr);
	}

	event OwnershipTransferred(address owner);
}

interface IDEXFactory {
	function createPair(address tokenA, address tokenB) external returns (address pair);
}

interface IDEXRouter {
	function factory() external pure returns (address);

	function WETH() external pure returns (address);

	function addLiquidity(
		address tokenA,
		address tokenB,
		uint256 amountADesired,
		uint256 amountBDesired,
		uint256 amountAMin,
		uint256 amountBMin,
		address to,
		uint256 deadline
	)
		external
		returns (
			uint256 amountA,
			uint256 amountB,
			uint256 liquidity
		);

	function addLiquidityETH(
		address token,
		uint256 amountTokenDesired,
		uint256 amountTokenMin,
		uint256 amountETHMin,
		address to,
		uint256 deadline
	)
		external
		payable
		returns (
			uint256 amountToken,
			uint256 amountETH,
			uint256 liquidity
		);

	function swapExactTokensForTokensSupportingFeeOnTransferTokens(
		uint256 amountIn,
		uint256 amountOutMin,
		address[] calldata path,
		address to,
		uint256 deadline
	) external;

	function swapExactETHForTokensSupportingFeeOnTransferTokens(
		uint256 amountOutMin,
		address[] calldata path,
		address to,
		uint256 deadline
	) external payable;

	function swapExactTokensForETHSupportingFeeOnTransferTokens(
		uint256 amountIn,
		uint256 amountOutMin,
		address[] calldata path,
		address to,
		uint256 deadline
	) external;
}

interface IDividendDistributor {
	function setShare(address shareholder, uint256 amount) external;

	function deposit() external payable;

	function claimDividend(address shareholder) external;

	function setTreasury(address treasury) external;

	function getDividendsClaimedOf(address shareholder) external returns (uint256);
}

contract DividendDistributor is IDividendDistributor {
	using SafeMath for uint256;

	address public _token;
	address public _owner;
	address public _treasury;

	struct Share {
		uint256 amount;
		uint256 totalExcluded;
		uint256 totalClaimed;
	}

	address[] private shareholders;
	mapping(address => uint256) private shareholderIndexes;

	mapping(address => Share) public shares;

	uint256 public totalShares;
	uint256 public totalDividends;
	uint256 public totalClaimed;
	uint256 public dividendsPerShare;
	uint256 private dividendsPerShareAccuracyFactor = 10**36;

	modifier onlyToken() {
		require(msg.sender == _token);
		_;
	}

	modifier onlyOwner() {
		require(msg.sender == _owner);
		_;
	}

	constructor(address owner, address treasury) {
		_token = msg.sender;
		_owner = payable(owner);
		_treasury = payable(treasury);
	}