// File: Context.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

// File: RPEPELPURPLE.sol



// SPDX-License-Identifier: MIT

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//////// THIS IS THE RPEPE.LPURPLE POOL OF LP STAKING - rPepe Token Staking ////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

pragma solidity ^0.6.0;

import "./SafeMath.sol";
import "./Context.sol";

interface IUniswapV2Pair {
    function totalSupply() external view returns (uint);
    function transferFrom(address sender, address recipient, uint amount) external returns (bool);
    function transfer(address recipient, uint amount) external returns (bool);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

contract RPEPELPURPLE is Context {
    using SafeMath for uint256;
    
    // Contract state variables
    address private _UniswapV2Pair;
    uint256 private _totalStakedAmount;
    mapping(address => uint256) private _stakedAmount;
    address[] private _stakers;

    // Events
    event Staked(address account, uint256 amount);
    event Unstaked(address account, uint256 amount);
    
    constructor(address UniswapV2Pair) public {
        _UniswapV2Pair = UniswapV2Pair;
    }
    
    /**
     * @dev Stake rPEPE-ETH LP tokens
     *
     * Requirement
     *
     * - In this pool, don't care about 2.5% fee for stake/unstake
     *
     * @param amount: Amount of LP tokens to deposit
     */
    function stake(uint256 amount) public {
        require(amount > 0, "Staking amount must be more than zero");
        // Transfer tokens from staker to the contract amount
        require(IUniswapV2Pair(_UniswapV2Pair).transferFrom(_msgSender(), address(this), uint(amount)), "It has failed to transfer tokens from staker to contract.");
        // add staker to array
        if (_stakedAmount[_msgSender()] == 0) {
            _stakers.push(_msgSender());
        }
        // Increase the total staked amount
        _totalStakedAmount = _totalStakedAmount.add(amount);
        // Add new stake amount
        _stakedAmount[_msgSender()] = _stakedAmount[_msgSender()].add(amount);
        emit Staked(_msgSender(), amount);
    }

    /**
     * @dev Unstake staked rPEPE-ETH LP tokens
     * 
     * Requirement
     *
     * - In this pool, don't care about 2.5% fee for stake/unstake
     *
     * @param amount: Amount of LP tokens to unstake
     */
    function unstake(uint256 amount) public {
        // Transfer tokens from contract amount to staker
        require(IUniswapV2Pair(_UniswapV2Pair).transfer(_msgSender(), uint(amount)), "It has failed to transfer tokens from contract to staker.");
        // Decrease the total staked amount
        _totalStakedAmount = _totalStakedAmount.sub(amount);
        // Decrease the staker's amount
        _stakedAmount[_msgSender()] = _stakedAmount[_msgSender()].sub(amount);
        // remove staker from array
        if (_stakedAmount[_msgSender()] == 0) {
            for (uint256 i=0; i < _stakers.length; i++) {
                if (_stakers[i] == _msgSender()) {
                    _stakers[i] = _stakers[_stakers.length.sub(1)];
                    _stakers.pop();
                    break;
                }
            }
        }
        emit Unstaked(_msgSender(), amount);
    }
    
    /**
     * @dev API to get the total staked LP amount of all stakers
     */
    function getTotalStakedLPAmount() external view returns (uint256) {
        return _totalStakedAmount;
    }

    /**
     * @dev API to get the staker's staked LP amount
     */
    function getStakedLPAmount(address account) external view returns (uint256) {
        return _stakedAmount[account];
    }

    /**
     * @dev API to get the total staked rPEPE amount of all stakers
     */
    function getTotalStakedAmount() external view returns (uint256) {
        return _getStakedPepeAmount(_totalStakedAmount);
    }

    /**
     * @dev API to get the staker's staked rPEPE amount
     */
    function getStakedAmount(address account) external view returns (uint256) {
        return _getStakedPepeAmount(_stakedAmount[account]);
    }

    /**
     * @dev API to get the staker's array
     */
    function getStakers() external view returns (address[] memory) {
        return _stakers;
    }

    /**
     * @dev count and return pepe amount from lp token amount in uniswap v2 pool
     * 
     * Formula
     * 
     * - rPEPE = (staked LP / total LP in uniswap pool) * rPEPE in uniswap pool
     */
    function _getStakedPepeAmount(uint256 amount) internal view returns (uint256)  {
        (uint112 pepeAmount,,) = IUniswapV2Pair(_UniswapV2Pair).getReserves();
        // get the total amount of LP token in uniswap v2 pool
        uint totalAmount = IUniswapV2Pair(_UniswapV2Pair).totalSupply();
        return amount.mul(uint256(pepeAmount)).div(uint256(totalAmount));
    }
}
// File: SafeMath.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
 
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;
    }

    function ceil(uint256 a, uint256 m) internal pure returns (uint256) {
        uint256 c = add(a, m);
        uint256 d = sub(c, 1);
        return mul(div(d,m),m);
    }
}