pragma solidity 0.6.12;

interface IERC20 {

    /**

     * @dev Returns the amount of tokens in existence.

     */

    function totalSupply() external view returns (uint256);



    /**

     * @dev Returns the amount of tokens owned by `account`.

     */

    function balanceOf(address account) external view returns (uint256);



    /**

     * @dev Moves `amount` tokens from the caller's account to `recipient`.

     *

     * Returns a boolean value indicating whether the operation succeeded.

     *

     * Emits a {Transfer} event.

     */

    function transfer(address recipient, uint256 amount) external returns (bool);



    /**

     * @dev Returns the remaining number of tokens that `spender` will be

     * allowed to spend on behalf of `owner` through {transferFrom}. This is

     * zero by default.

     *

     * This value changes when {approve} or {transferFrom} are called.

     */

    function allowance(address owner, address spender) external view returns (uint256);



    /**

     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.

     *

     * Returns a boolean value indicating whether the operation succeeded.

     *

     * IMPORTANT: Beware that changing an allowance with this method brings the risk

     * that someone may use both the old and the new allowance by unfortunate

     * transaction ordering. One possible solution to mitigate this race

     * condition is to first reduce the spender's allowance to 0 and set the

     * desired value afterwards:

     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729

     *

     * Emits an {Approval} event.

     */

    function approve(address spender, uint256 amount) external returns (bool);



    /**

     * @dev Moves `amount` tokens from `sender` to `recipient` using the

     * allowance mechanism. `amount` is then deducted from the caller's

     * allowance.

     *

     * Returns a boolean value indicating whether the operation succeeded.

     *

     * Emits a {Transfer} event.

     */

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);



    /**

     * @dev Emitted when `value` tokens are moved from one account (`from`) to

     * another (`to`).

     *

     * Note that `value` may be zero.

     */

    event Transfer(address indexed from, address indexed to, uint256 value);



    /**

     * @dev Emitted when the allowance of a `spender` for an `owner` is set by

     * a call to {approve}. `value` is the new allowance.

     */

    event Approval(address indexed owner, address indexed spender, uint256 value);

}

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;

    }

}

interface sbControllerInterface {

  function requestRewards(address miner, uint256 amount) external;



  function isValuePoolAccepted(address valuePool) external view returns (bool);



  function getValuePoolRewards(address valuePool, uint256 day) external view returns (uint256);



  function getValuePoolMiningFee(address valuePool) external returns (uint256, uint256);



  function getValuePoolUnminingFee(address valuePool) external returns (uint256, uint256);



  function getValuePoolClaimingFee(address valuePool) external returns (uint256, uint256);



  function isServicePoolAccepted(address servicePool) external view returns (bool);



  function getServicePoolRewards(address servicePool, uint256 day) external view returns (uint256);



  function getServicePoolClaimingFee(address servicePool) external returns (uint256, uint256);



  function getServicePoolRequestFeeInWei(address servicePool) external returns (uint256);



  function getVoteForServicePoolsCount() external view returns (uint256);



  function getVoteForServicesCount() external view returns (uint256);



  function getVoteCastersRewards(uint256 dayNumber) external view returns (uint256);



  function getVoteReceiversRewards(uint256 dayNumber) external view returns (uint256);



  function getMinerMinMineDays() external view returns (uint256);



  function getServiceMinMineDays() external view returns (uint256);



  function getMinerMinMineAmountInWei() external view returns (uint256);



  function getServiceMinMineAmountInWei() external view returns (uint256);



  function getValuePoolVestingDays(address valuePool) external view returns (uint256);



  function getServicePoolVestingDays(address poservicePoolol) external view returns (uint256);



  function getVoteCasterVestingDays() external view returns (uint256);



  function getVoteReceiverVestingDays() external view returns (uint256);

}

interface sbEthFeePoolInterface {

  function deposit() external payable;

}

contract sbLpValuePool {

    using SafeMath for uint256;



    bool public initDone;

    address public admin;

    address public pendingAdmin;

    address public superAdmin;

    address public pendingSuperAdmin;



    sbEthFeePoolInterface public sbEthFeePool;

    sbControllerInterface public sbController;

    IERC20 public token;



    mapping(address => uint256[]) public minerMineDays;

    mapping(address => uint256[]) public minerMineAmounts;

    mapping(address => uint256[]) public minerMineMineSeconds;



    uint256[] public mineDays;

    uint256[] public mineAmounts;

    uint256[] public mineMineSeconds;



    mapping(address => uint256) public minerDayLastClaimedFor;



    function init(

        address sbEthFeePoolAddress,

        address sbControllerAddress,

        address tokenAddress,

        address adminAddress,

        address superAdminAddress

    ) public {

        require(!initDone, "init done");

        sbEthFeePool = sbEthFeePoolInterface(sbEthFeePoolAddress);

        sbController = sbControllerInterface(sbControllerAddress);

        token = IERC20(tokenAddress);

        admin = adminAddress;

        superAdmin = superAdminAddress;

        initDone = true;

    }



    // ADMIN

    // *************************************************************************************

    function setPendingAdmin(address newPendingAdmin) public {

        require(msg.sender == admin, "not admin");

        pendingAdmin = newPendingAdmin;

    }



    function acceptAdmin() public {

        require(

            msg.sender == pendingAdmin && msg.sender != address(0),

            "not pendingAdmin"

        );

        admin = pendingAdmin;

        pendingAdmin = address(0);

    }



    function setPendingSuperAdmin(address newPendingSuperAdmin) public {

        require(msg.sender == superAdmin, "not superAdmin");

        pendingSuperAdmin = newPendingSuperAdmin;

    }



    function acceptSuperAdmin() public {

        require(

            msg.sender == pendingSuperAdmin && msg.sender != address(0),

            "not pendingSuperAdmin"

        );

        superAdmin = pendingSuperAdmin;

        pendingSuperAdmin = address(0);

    }



    // MINING

    // *************************************************************************************

    function mine(uint256 amount) public payable {

        require(amount > 0, "zero");

        (uint256 numerator, uint256 denominator) = sbController

            .getValuePoolMiningFee(address(this));

        uint256 fee = amount.mul(numerator).div(denominator);

        require(msg.value == fee, "invalid fee");

        sbEthFeePool.deposit{value: msg.value}();

        token.transferFrom(msg.sender, address(this), amount);

        uint256 currentDay = _getCurrentDay();

        _update(

            minerMineDays[msg.sender],

            minerMineAmounts[msg.sender],

            minerMineMineSeconds[msg.sender],

            amount,

            true,

            currentDay

        );

        _update(

            mineDays,

            mineAmounts,

            mineMineSeconds,

            amount,

            true,

            currentDay

        );

    }



    function unmine(uint256 amount) public payable {

        require(amount > 0, "zero");

        (uint256 numerator, uint256 denominator) = sbController

            .getValuePoolUnminingFee(address(this));

        uint256 fee = amount.mul(numerator).div(denominator);

        require(msg.value == fee, "invalid fee");

        sbEthFeePool.deposit{value: msg.value}();

        uint256 currentDay = _getCurrentDay();

        _update(

            minerMineDays[msg.sender],

            minerMineAmounts[msg.sender],

            minerMineMineSeconds[msg.sender],

            amount,

            false,

            currentDay

        );

        _update(

            mineDays,

            mineAmounts,

            mineMineSeconds,

            amount,

            false,

            currentDay

        );

        token.transfer(msg.sender, amount);

    }



    function getMinerDayLastClaimedFor(address miner)

        public

        view

        returns (uint256)

    {

        uint256 len = minerMineDays[miner].length;

        if (len != 0) {

            return

                minerDayLastClaimedFor[miner] == 0

                    ? minerMineDays[miner][0].sub(1)

                    : minerDayLastClaimedFor[miner];

        }

        return 0;

    }



    function getMinerMineData(address miner, uint256 dayNumber)

        public

        view

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256 day = dayNumber == 0 ? _getCurrentDay() : dayNumber;

        return _getMinerMineData(miner, day);

    }



    function getMineData(uint256 dayNumber)

        public

        view

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256 day = dayNumber == 0 ? _getCurrentDay() : dayNumber;

        return _getMineData(day);

    }



    // CLAIMING

    // *************************************************************************************

    function claimAll() public payable {

        uint256 len = minerMineDays[msg.sender].length;

        require(len != 0, "no mines");

        uint256 currentDay = _getCurrentDay();

        uint256 dayLastClaimedFor = minerDayLastClaimedFor[msg.sender] == 0

            ? minerMineDays[msg.sender][0].sub(1)

            : minerDayLastClaimedFor[msg.sender];

        uint256 vestingDays = sbController.getValuePoolVestingDays(

            address(this)

        );

        require(

            currentDay > dayLastClaimedFor.add(vestingDays),

            "already claimed"

        );

        // fee is calculated in _claim

        _claim(currentDay, msg.sender, dayLastClaimedFor, vestingDays);

    }



    function claimUpTo(uint256 day) public payable {

        uint256 len = minerMineDays[msg.sender].length;

        require(len != 0, "no mines");

        require(day <= _getCurrentDay(), "invalid day");

        uint256 dayLastClaimedFor = minerDayLastClaimedFor[msg.sender] == 0

            ? minerMineDays[msg.sender][0].sub(1)

            : minerDayLastClaimedFor[msg.sender];

        uint256 vestingDays = sbController.getValuePoolVestingDays(

            address(this)

        );

        require(day > dayLastClaimedFor.add(vestingDays), "already claimed");

        // fee is calculated in _claim

        _claim(day, msg.sender, dayLastClaimedFor, vestingDays);

    }



    function getRewardsDueAll(address miner) public view returns (uint256) {

        uint256 len = minerMineDays[miner].length;

        if (len == 0) {

            return 0;

        }

        uint256 currentDay = _getCurrentDay();

        uint256 dayLastClaimedFor = minerDayLastClaimedFor[miner] == 0

            ? minerMineDays[miner][0].sub(1)

            : minerDayLastClaimedFor[miner];

        uint256 vestingDays = sbController.getValuePoolVestingDays(

            address(this)

        );

        if (!(currentDay > dayLastClaimedFor.add(vestingDays))) {

            return 0;

        }

        return

            _getRewardsDue(currentDay, miner, dayLastClaimedFor, vestingDays);

    }



    function getRewardsDueUpTo(uint256 day, address miner)

        public

        view

        returns (uint256)

    {

        uint256 len = minerMineDays[miner].length;

        if (len == 0) {

            return 0;

        }

        require(day <= _getCurrentDay(), "invalid day");

        uint256 dayLastClaimedFor = minerDayLastClaimedFor[miner] == 0

            ? minerMineDays[miner][0].sub(1)

            : minerDayLastClaimedFor[miner];

        uint256 vestingDays = sbController.getValuePoolVestingDays(

            address(this)

        );

        if (!(day > dayLastClaimedFor.add(vestingDays))) {

            return 0;

        }

        return _getRewardsDue(day, miner, dayLastClaimedFor, vestingDays);

    }



    // SUPPORT

    // *************************************************************************************

    function _getMinerMineData(address miner, uint256 day)

        internal

        view

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256[] memory _Days = minerMineDays[miner];

        uint256[] memory _Amounts = minerMineAmounts[miner];

        uint256[] memory _UnitSeconds = minerMineMineSeconds[miner];

        return _get(_Days, _Amounts, _UnitSeconds, day);

    }



    function _getMineData(uint256 day)

        internal

        view

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        return _get(mineDays, mineAmounts, mineMineSeconds, day);

    }



    function _get(

        uint256[] memory _Days,

        uint256[] memory _Amounts,

        uint256[] memory _UnitSeconds,

        uint256 day

    )

        internal

        pure

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256 len = _Days.length;

        if (len == 0) {

            return (day, 0, 0);

        }

        if (day < _Days[0]) {

            return (day, 0, 0);

        }

        uint256 lastIndex = len.sub(1);

        uint256 lastMinedDay = _Days[lastIndex];

        if (day == lastMinedDay) {

            return (day, _Amounts[lastIndex], _UnitSeconds[lastIndex]);

        } else if (day > lastMinedDay) {

            return (day, _Amounts[lastIndex], _Amounts[lastIndex].mul(1 days));

        }

        return _find(_Days, _Amounts, _UnitSeconds, day);

    }



    function _find(

        uint256[] memory _Days,

        uint256[] memory _Amounts,

        uint256[] memory _UnitSeconds,

        uint256 day

    )

        internal

        pure

        returns (

            uint256,

            uint256,

            uint256

        )

    {

        uint256 left = 0;

        uint256 right = _Days.length.sub(1);

        uint256 middle = right.add(left).div(2);

        while (left < right) {

            if (_Days[middle] == day) {

                return (day, _Amounts[middle], _UnitSeconds[middle]);

            } else if (_Days[middle] > day) {

                if (middle > 0 && _Days[middle.sub(1)] < day) {

                    return (

                        day,

                        _Amounts[middle.sub(1)],

                        _Amounts[middle.sub(1)].mul(1 days)

                    );

                }

                if (middle == 0) {

                    return (day, 0, 0);

                }

                right = middle.sub(1);

            } else if (_Days[middle] < day) {

                if (

                    middle < _Days.length.sub(1) && _Days[middle.add(1)] > day

                ) {

                    return (

                        day,

                        _Amounts[middle],

                        _Amounts[middle].mul(1 days)

                    );

                }

                left = middle.add(1);

            }

            middle = right.add(left).div(2);

        }

        if (_Days[middle] != day) {

            return (day, 0, 0);

        } else {

            return (day, _Amounts[middle], _UnitSeconds[middle]);

        }

    }



    function _update(

        uint256[] storage _Days,

        uint256[] storage _Amounts,

        uint256[] storage _UnitSeconds,

        uint256 amount,

        bool adding,

        uint256 currentDay

    ) internal {

        uint256 len = _Days.length;

        uint256 secondsInADay = 1 days;

        uint256 secondsSinceStartOfDay = block.timestamp % secondsInADay;

        uint256 secondsUntilEndOfDay = secondsInADay.sub(

            secondsSinceStartOfDay

        );



        if (len == 0) {

            if (adding) {

                _Days.push(currentDay);

                _Amounts.push(amount);

                _UnitSeconds.push(amount.mul(secondsUntilEndOfDay));

            } else {

                require(false, "1: not enough mine");

            }

        } else {

            uint256 lastIndex = len.sub(1);

            uint256 lastMinedDay = _Days[lastIndex];

            uint256 lastMinedAmount = _Amounts[lastIndex];

            uint256 lastUnitSeconds = _UnitSeconds[lastIndex];



            uint256 newAmount;

            uint256 newUnitSeconds;



            if (lastMinedDay == currentDay) {

                if (adding) {

                    newAmount = lastMinedAmount.add(amount);

                    newUnitSeconds = lastUnitSeconds.add(

                        amount.mul(secondsUntilEndOfDay)

                    );

                } else {

                    require(lastMinedAmount >= amount, "2: not enough mine");

                    newAmount = lastMinedAmount.sub(amount);

                    newUnitSeconds = lastUnitSeconds.sub(

                        amount.mul(secondsUntilEndOfDay)

                    );

                }

                _Amounts[lastIndex] = newAmount;

                _UnitSeconds[lastIndex] = newUnitSeconds;

            } else {

                if (adding) {

                    newAmount = lastMinedAmount.add(amount);

                    newUnitSeconds = lastMinedAmount.mul(1 days).add(

                        amount.mul(secondsUntilEndOfDay)

                    );

                } else {

                    require(lastMinedAmount >= amount, "3: not enough mine");

                    newAmount = lastMinedAmount.sub(amount);

                    newUnitSeconds = lastMinedAmount.mul(1 days).sub(

                        amount.mul(secondsUntilEndOfDay)

                    );

                }

                _Days.push(currentDay);

                _Amounts.push(newAmount);

                _UnitSeconds.push(newUnitSeconds);

            }

        }

    }



    function _claim(

        uint256 upToDay,

        address miner,

        uint256 dayLastClaimedFor,

        uint256 vestingDays

    ) internal {

        uint256 rewards = _getRewardsDue(

            upToDay,

            miner,

            dayLastClaimedFor,

            vestingDays

        );

        require(rewards > 0, "no rewards");

        (uint256 numerator, uint256 denominator) = sbController

            .getValuePoolClaimingFee(address(this));

        uint256 fee = rewards.mul(numerator).div(denominator);

        require(msg.value == fee, "invalid fee");

        sbEthFeePool.deposit{value: msg.value}();

        minerDayLastClaimedFor[miner] = upToDay.sub(vestingDays);

        sbController.requestRewards(miner, rewards);

    }



    function _getRewardsDue(

        uint256 upToDay,

        address miner,

        uint256 dayLastClaimedFor,

        uint256 vestingDays

    ) internal view returns (uint256) {

        uint256 rewards;

        for (

            uint256 day = dayLastClaimedFor.add(1);

            day <= upToDay.sub(vestingDays);

            day++

        ) {

            (, , uint256 minerMineSecondsForDay) = _getMinerMineData(

                miner,

                day

            );

            (, , uint256 mineSecondsForDay) = _getMineData(day);

            if (mineSecondsForDay == 0) {

                continue;

            }

            uint256 availableRewards = sbController.getValuePoolRewards(

                address(this),

                day

            );

            uint256 amount = availableRewards.mul(minerMineSecondsForDay).div(

                mineSecondsForDay

            );

            rewards = rewards.add(amount);

        }

        return rewards;

    }



    function _getCurrentDay() internal view returns (uint256) {

        return block.timestamp.div(1 days).add(1);

    }

}

{"success": true, "error": null, "results": {"detectors": [{"check": "weak-prng", "impact": "High", "confidence": "Medium"}, {"check": "incorrect-equality", "impact": "Medium", "confidence": "High"}, {"check": "unchecked-transfer", "impact": "High", "confidence": "Medium"}]}}