ETH Price: $1,801.66 (-0.27%)

Transaction Decoder

Block:
14657084 at Apr-26-2022 12:56:56 AM +UTC
Transaction Fee:
0.007808672438637538 ETH $14.07
Gas Used:
158,353 Gas / 49.311806146 Gwei

Emitted Events:

297 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000055a3ab62e460004d1d478f702a98714438bca4b8, 0x000000000000000000000000eca4200ba68043e8a656421d76020d7e81bd5d7b, 0000000000000000000000000000000000000000000001550dbc4ec045bdfb00 )
298 UniswapV2Pair.Transfer( from=[Receiver] TokensFarm, to=[Sender] 0xeca4200ba68043e8a656421d76020d7e81bd5d7b, value=143489478382512020550 )
299 TokensFarm.Withdraw( user=[Sender] 0xeca4200ba68043e8a656421d76020d7e81bd5d7b, stakeId=0, amount=143489478382512020550 )

Account State Difference:

  Address   Before After State Difference Code
0x0529Bf56...C0FF47c4c
0x4da0C483...E3e4AdF75
0x55A3ab62...438bca4b8
(Ethermine)
1,341.645285863345985905 Eth1,341.645681745845985905 Eth0.0003958825
0xECA4200b...e81BD5D7b
0.043745855195500422 Eth
Nonce: 237
0.035937182756862884 Eth
Nonce: 238
0.007808672438637538

Execution Trace

TokensFarm.withdraw( _amount=143489478382512020550, stakeId=0 )
  • TokensFarm.withdraw( _amount=143489478382512020550, stakeId=0 )
    • TransparentUpgradeableProxy.a9059cbb( )
      • EPIKERC20.transfer( recipient=0xECA4200ba68043e8A656421d76020D7e81BD5D7b, amount=6291329481740782533376 ) => ( True )
      • UniswapV2Pair.transfer( to=0xECA4200ba68043e8A656421d76020D7e81BD5D7b, value=143489478382512020550 ) => ( True )
        File 1 of 5: TokensFarm
        // Sources flattened with hardhat v2.1.2 https://hardhat.org
        
        // File @openzeppelin/contracts/token/ERC20/IERC20.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        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);
        }
        
        
        // File @openzeppelin/contracts/math/SafeMath.sol
        
        
        pragma solidity >=0.6.0 <0.8.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, with an overflow flag.
             *
             * _Available since v3.4._
             */
            function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                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) {
                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) {
                // 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) {
                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) {
                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) {
                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) {
                require(b <= a, "SafeMath: subtraction overflow");
                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) {
                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, reverting 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) {
                require(b > 0, "SafeMath: division by zero");
                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) {
                require(b > 0, "SafeMath: modulo by zero");
                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) {
                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.
             *
             * CAUTION: This function is deprecated because it requires allocating memory for the error
             * message unnecessarily. For custom revert reasons use {tryDiv}.
             *
             * 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);
                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) {
                require(b > 0, errorMessage);
                return a % b;
            }
        }
        
        
        // File @openzeppelin/contracts/utils/Address.sol
        
        
        pragma solidity >=0.6.2 <0.8.0;
        
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
        
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
        
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                require(isContract(target), "Address: delegate call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
        
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        
        // File @openzeppelin/contracts/token/ERC20/SafeERC20.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        
        
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using SafeMath for uint256;
            using Address for address;
        
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
        
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
        
            /**
             * @dev Deprecated. This function has issues similar to the ones found in
             * {IERC20-approve}, and its usage is discouraged.
             *
             * Whenever possible, use {safeIncreaseAllowance} and
             * {safeDecreaseAllowance} instead.
             */
            function safeApprove(IERC20 token, address spender, uint256 value) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                // solhint-disable-next-line max-line-length
                require((value == 0) || (token.allowance(address(this), spender) == 0),
                    "SafeERC20: approve from non-zero to non-zero allowance"
                );
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
        
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).add(value);
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
        
                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                if (returndata.length > 0) { // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        
        
        // File @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
        
        
        pragma solidity >=0.6.2 <0.8.0;
        
        /**
         * @dev Collection of functions related to the address type
         */
        library AddressUpgradeable {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
        
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
        
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
        
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        
        // File @openzeppelin/contracts-upgradeable/proxy/Initializable.sol
        
        
        // solhint-disable-next-line compiler-version
        pragma solidity >=0.4.24 <0.8.0;
        
        /**
         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
         * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
         *
         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
         * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
         *
         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
         */
        abstract contract Initializable {
        
            /**
             * @dev Indicates that the contract has been initialized.
             */
            bool private _initialized;
        
            /**
             * @dev Indicates that the contract is in the process of being initialized.
             */
            bool private _initializing;
        
            /**
             * @dev Modifier to protect an initializer function from being invoked twice.
             */
            modifier initializer() {
                require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
        
                bool isTopLevelCall = !_initializing;
                if (isTopLevelCall) {
                    _initializing = true;
                    _initialized = true;
                }
        
                _;
        
                if (isTopLevelCall) {
                    _initializing = false;
                }
            }
        
            /// @dev Returns true if and only if the function is running in the constructor
            function _isConstructor() private view returns (bool) {
                return !AddressUpgradeable.isContract(address(this));
            }
        }
        
        
        // File @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol
        
        
        pragma solidity >=0.6.0 <0.8.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 ContextUpgradeable is Initializable {
            function __Context_init() internal initializer {
                __Context_init_unchained();
            }
        
            function __Context_init_unchained() internal initializer {
            }
            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;
            }
            uint256[50] private __gap;
        }
        
        
        // File @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * By default, the owner account will be the one that deploys the contract. This
         * can later be changed with {transferOwnership}.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
            address private _owner;
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            function __Ownable_init() internal initializer {
                __Context_init_unchained();
                __Ownable_init_unchained();
            }
        
            function __Ownable_init_unchained() internal initializer {
                address msgSender = _msgSender();
                _owner = msgSender;
                emit OwnershipTransferred(address(0), msgSender);
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view virtual 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;
            }
            uint256[49] private __gap;
        }
        
        
        // File @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        /**
         * @dev Contract module that helps prevent reentrant calls to a function.
         *
         * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
         * available, which can be applied to functions to make sure there are no nested
         * (reentrant) calls to them.
         *
         * Note that because there is a single `nonReentrant` guard, functions marked as
         * `nonReentrant` may not call one another. This can be worked around by making
         * those functions `private`, and then adding `external` `nonReentrant` entry
         * points to them.
         *
         * TIP: If you would like to learn more about reentrancy and alternative ways
         * to protect against it, check out our blog post
         * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
         */
        abstract contract ReentrancyGuardUpgradeable is Initializable {
            // Booleans are more expensive than uint256 or any type that takes up a full
            // word because each write operation emits an extra SLOAD to first read the
            // slot's contents, replace the bits taken up by the boolean, and then write
            // back. This is the compiler's defense against contract upgrades and
            // pointer aliasing, and it cannot be disabled.
        
            // The values being non-zero value makes deployment a bit more expensive,
            // but in exchange the refund on every call to nonReentrant will be lower in
            // amount. Since refunds are capped to a percentage of the total
            // transaction's gas, it is best to keep them low in cases like this one, to
            // increase the likelihood of the full refund coming into effect.
            uint256 private constant _NOT_ENTERED = 1;
            uint256 private constant _ENTERED = 2;
        
            uint256 private _status;
        
            function __ReentrancyGuard_init() internal initializer {
                __ReentrancyGuard_init_unchained();
            }
        
            function __ReentrancyGuard_init_unchained() internal initializer {
                _status = _NOT_ENTERED;
            }
        
            /**
             * @dev Prevents a contract from calling itself, directly or indirectly.
             * Calling a `nonReentrant` function from another `nonReentrant`
             * function is not supported. It is possible to prevent this from happening
             * by making the `nonReentrant` function external, and make it call a
             * `private` function that does the actual work.
             */
            modifier nonReentrant() {
                // On the first call to nonReentrant, _notEntered will be true
                require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
        
                // Any calls to nonReentrant after this point will fail
                _status = _ENTERED;
        
                _;
        
                // By storing the original value once again, a refund is triggered (see
                // https://eips.ethereum.org/EIPS/eip-2200)
                _status = _NOT_ENTERED;
            }
            uint256[49] private __gap;
        }
        
        
        // File contracts/TokensFarm.sol
        
        pragma solidity 0.6.12;
        
        
        
        
        
        contract TokensFarm is OwnableUpgradeable, ReentrancyGuardUpgradeable {
        
            using SafeMath for uint256;
            using SafeERC20 for IERC20;
        
            // Enums
            enum EarlyWithdrawPenalty {
                NO_PENALTY,
                BURN_REWARDS,
                REDISTRIBUTE_REWARDS
            }
        
            // Info of each user.
            struct StakeInfo {
                // How many tokens the user has provided.
                uint256 amount;
                // Reward debt.
                uint256 rewardDebt;
                // Time when user deposited.
                uint256 depositTime;
                // Time when user withdraw
                uint256 withdrawTime;
                // Address of user
                address addressOfUser;
            }
        
            // Address of ERC20 token contract.
            IERC20 public tokenStaked;
            // Last time number that ERC20s distribution occurs.
            uint256 public lastRewardTime;
            // Accumulated ERC20s per share, times 1e18.
            uint256 public accERC20PerShare;
            // Total tokens deposited in the farm.
            uint256 public totalDeposits;
            // If contractor allows early withdraw on stakes
            bool public isEarlyWithdrawAllowed;
            // Minimal period of time to stake
            uint256 public minTimeToStake;
            // Address of the ERC20 Token contract.
            IERC20 public erc20;
            // The total amount of ERC20 that's paid out as reward.
            uint256 public paidOut;
            // ERC20 tokens rewarded per second.
            uint256 public rewardPerSecond;
            // Total rewards added to farm
            uint256 public totalRewards;
            // Info of each user that stakes ERC20 tokens.
            mapping(address => StakeInfo[]) public stakeInfo;
            // The time when farming starts.
            uint256 public startTime;
            // The time when farming ends.
            uint256 public endTime;
            // Early withdraw penalty
            EarlyWithdrawPenalty public penalty;
            // Stake fee percent
            uint256 public stakeFeePercent;
            // Reward fee percent
            uint256 public rewardFeePercent;
            // Fee collector address
            address payable public feeCollector;
            // Flat fee amount
            uint256 public flatFeeAmount;
            // Fee option
            bool public isFlatFeeAllowed;
            // Total tokens burned
            uint256 public totalTokensBurned;
            // Total fee collected
            uint256 public totalFeeCollectedETH;
            // Total fee collected in tokens
            uint256 public totalFeeCollectedTokens;
            // Address of farm instance
            address public farmImplementation;
            // NumberOfUsers participating in farm
            uint256 public noOfUsers;
            // Addresses of all users that are currently participating
            address[] public participants;
            // Mapping of every users spot in array
            mapping(address => uint256) public id;
        
            // Events
            event Deposit(
                address indexed user,
                uint256 indexed stakeId,
                uint256 indexed amount
            );
            event Withdraw(
                address indexed user,
                uint256 indexed stakeId,
                uint256 indexed amount
            );
            event EmergencyWithdraw(
                address indexed user,
                uint256 indexed stakeId,
                uint256 indexed amount
            );
            event EarlyWithdrawPenaltySet(EarlyWithdrawPenalty indexed penalty);
            event MinTimeToStakeSet(uint256 indexed minTimeToStake);
            event IsEarlyWithdrawAllowedSet(bool indexed allowed);
            event StakeFeePercentSet(uint256 indexed stakeFeePercent);
            event RewardFeePercentSet(uint256 indexed rewardFeePercent);
            event FlatFeeAmountSet(uint256 indexed flatFeeAmount);
            event IsFlatFeeAllowedSet(bool indexed allowed);
            event FeeCollectorSet(address payable indexed feeCollector);
        
            // Modifiers
            modifier validateStakeByStakeId(address _user, uint256 stakeId) {
                require(stakeId < stakeInfo[_user].length, "Stake does not exist");
                _;
            }
        
            /**
             * @notice function sets initial state of contract
             *
             * @param _erc20 - address of reward token
             * @param _rewardPerSecond - number of reward per second
             * @param _startTime - beginning of farm
             * @param _minTimeToStake - how much time needs to pass before staking
             * @param _isEarlyWithdrawAllowed - is early withdraw allowed or not
             * @param _penalty - ENUM(what type of penalty)
             * @param _tokenStaked - address of token which is staked
             * @param _stakeFeePercent - fee percent for staking
             * @param _rewardFeePercent - fee percent for reward distribution
             * @param _flatFeeAmount - flat fee amount
             * @param _isFlatFeeAllowed - is flat fee  allowed or not
             */
            function initialize(
                address _erc20,
                uint256 _rewardPerSecond,
                uint256 _startTime,
                uint256 _minTimeToStake,
                bool _isEarlyWithdrawAllowed,
                uint256 _penalty,
                address _tokenStaked,
                uint256 _stakeFeePercent,
                uint256 _rewardFeePercent,
                uint256 _flatFeeAmount,
                address payable _feeCollector,
                bool _isFlatFeeAllowed,
                address _farmImplementation
            )
                external
                initializer
            {
                // Upgrading ownership
                __Ownable_init();
                __ReentrancyGuard_init();
        
                // Requires for correct initialization
                require(_erc20 != address(0x0), "Wrong token address.");
                require(_rewardPerSecond > 0, "Rewards per second must be > 0.");
                require(
                    _startTime >= block.timestamp,
                    "Start time can not be in the past."
                );
                require(_stakeFeePercent < 100, "Stake fee must be < 100.");
                require(_rewardFeePercent < 100, "Reward fee must be < 100.");
                require(
                    _feeCollector != address(0x0),
                    "Wrong fee collector address."
                );
        
                // Initialization of contract
                erc20 = IERC20(_erc20);
                rewardPerSecond = _rewardPerSecond;
                startTime = _startTime;
                endTime = _startTime;
                minTimeToStake = _minTimeToStake;
                isEarlyWithdrawAllowed = _isEarlyWithdrawAllowed;
                stakeFeePercent = _stakeFeePercent;
                rewardFeePercent = _rewardFeePercent;
                flatFeeAmount = _flatFeeAmount;
                feeCollector = _feeCollector;
                isFlatFeeAllowed = _isFlatFeeAllowed;
                farmImplementation = _farmImplementation;
        
                _setEarlyWithdrawPenalty(_penalty);
                _addPool(IERC20(_tokenStaked));
            }
        
            // All Internal functions
        
            /**
             * @notice function is adding a new ERC20 token to the pool
             *
             * @param _tokenStaked - address of token staked
             */
            function _addPool(
                IERC20 _tokenStaked
            )
                internal
            {
                require(
                    address(_tokenStaked) != address(0x0),
                    "Must input valid address."
                );
                require(
                    address(tokenStaked) == address(0x0),
                    "Pool can be set only once."
                );
        
                uint256 _lastRewardTime = block.timestamp > startTime
                ? block.timestamp
                : startTime;
        
                tokenStaked = _tokenStaked;
                lastRewardTime = _lastRewardTime;
                accERC20PerShare = 0;
                totalDeposits = 0;
            }
        
            /**
             * @notice function is setting early withdrawal penalty, if applicable
             *
             * @param _penalty - number of penalty
             */
            function _setEarlyWithdrawPenalty(
                uint256 _penalty
            )
                internal
            {
                penalty = EarlyWithdrawPenalty(_penalty);
                emit EarlyWithdrawPenaltySet(penalty);
            }
        
            /**
             * @notice function is adding participant from farm
             *
             * @param user - address of user
             *
             * @return boolean - if adding is successful or not
             */
            function _addParticipant(
                address user
            )
                internal
                returns(bool)
            {
                if(stakeInfo[user].length > 0){
                    return false;
                }
        
                id[user] = noOfUsers;
                noOfUsers++;
                participants.push(user);
        
                return true;
            }
        
            /**
             * @notice function is removing participant from farm
             *
             * @param user - address of user
             * @param amount - how many is user withdrawing
             *
             * @return boolean - if removal is successful or not
             */
            function _removeParticipant(
                address user,
                uint256 amount
            )
                internal
                returns(bool)
            {
                uint256 totalAmount;
        
                if(noOfUsers == 1){
                    totalAmount = 0;
                    for(uint256 i = 0; i < stakeInfo[user].length; i++){
                        totalAmount += stakeInfo[user][i].amount;
                    }
        
                    if(amount == totalAmount){
                        delete id[user];
                        participants.pop();
                        noOfUsers--;
        
                        return true;
                    }
                }
                else{
                    totalAmount = 0;
                    for(uint256 i = 0; i < stakeInfo[user].length; i++){
                        totalAmount += stakeInfo[user][i].amount;
                    }
        
                    if(amount == totalAmount){
                        uint256 deletedUserId = id[user];
                        address lastUserInParticipantsArray = participants[participants.length - 1];
                        participants[deletedUserId] = lastUserInParticipantsArray;
                        id[lastUserInParticipantsArray] = deletedUserId;
        
                        delete id[user];
                        participants.pop();
                        noOfUsers--;
        
                        return true;
                    }
                }
        
                return false;
            }
        
            // All setter's functions
        
            /**
             * @notice function is setting new minimum time to stake value
             *
             * @param _minTimeToStake - min time to stake
             */
            function setMinTimeToStake(
                uint256 _minTimeToStake
            )
                external
                onlyOwner
            {
                minTimeToStake = _minTimeToStake;
                emit MinTimeToStakeSet(minTimeToStake);
            }
        
            /**
             * @notice function is setting new state of early withdraw
             *
             * @param _isEarlyWithdrawAllowed - is early withdraw allowed or not
             */
            function setIsEarlyWithdrawAllowed(
                bool _isEarlyWithdrawAllowed
            )
                external
                onlyOwner
            {
                isEarlyWithdrawAllowed = _isEarlyWithdrawAllowed;
                emit IsEarlyWithdrawAllowedSet(isEarlyWithdrawAllowed);
            }
        
            /**
             * @notice function is setting new stake fee percent value
             *
             * @param _stakeFeePercent - stake fee percent
             */
            function setStakeFeePercent(
                uint256 _stakeFeePercent
            )
                external
                onlyOwner
            {
                stakeFeePercent = _stakeFeePercent;
                emit StakeFeePercentSet(stakeFeePercent);
            }
        
            /**
             * @notice function is setting new reward fee percent value
             *
             * @param _rewardFeePercent - reward fee percent
             */
            function setRewardFeePercent(
                uint256 _rewardFeePercent
            )
                external
                onlyOwner
            {
                rewardFeePercent = _rewardFeePercent;
                emit RewardFeePercentSet(rewardFeePercent);
        
            }
        
            /**
             * @notice function is setting new flat fee amount
             *
             * @param _flatFeeAmount - flat fee amount
             */
            function setFlatFeeAmount(
                uint256 _flatFeeAmount
            )
                external
                onlyOwner
            {
                flatFeeAmount = _flatFeeAmount;
                emit FlatFeeAmountSet(flatFeeAmount);
            }
        
            /**
             * @notice function is setting flat fee allowed
             *
             * @param _isFlatFeeAllowed - is flat fee allowed or not
             */
            function setIsFlatFeeAllowed(
                bool _isFlatFeeAllowed
            )
                external
                onlyOwner
            {
                isFlatFeeAllowed = _isFlatFeeAllowed;
                emit IsFlatFeeAllowedSet(isFlatFeeAllowed);
            }
        
            /**
             * @notice function is setting feeCollector on new address
             *
             * @param _feeCollector - address of newFeeCollector
             */
            function setFeeCollector(
                address payable _feeCollector
            )
                external
                onlyOwner
            {
                feeCollector = _feeCollector;
                emit FeeCollectorSet(feeCollector);
            }
        
            // All view functions
        
            /**
             * @notice function is getting number to see deposited ERC20 token for a user.
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return deposited ERC20 token for a user
             */
            function deposited(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory  stake = stakeInfo[_user][stakeId];
                return stake.amount;
            }
        
            /**
             * @notice function is getting number to see pending ERC20s for a user.
             *
             * @dev pending reward = (user.amount * pool.accERC20PerShare) - user.rewardDebt
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return pending ERC20s for a user.
             */
            function pending(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory stake = stakeInfo[_user][stakeId];
        
                if (stake.amount == 0) {
                    return 0;
                }
        
                uint256 _accERC20PerShare = accERC20PerShare;
                uint256 tokenSupply = totalDeposits;
        
                if (block.timestamp > lastRewardTime && tokenSupply != 0) {
                    uint256 lastTime = block.timestamp < endTime
                    ? block.timestamp
                    : endTime;
                    uint256 timeToCompare = lastRewardTime < endTime
                    ? lastRewardTime
                    : endTime;
                    uint256 nrOfSeconds = lastTime.sub(timeToCompare);
                    uint256 erc20Reward = nrOfSeconds.mul(rewardPerSecond);
                    _accERC20PerShare = _accERC20PerShare.add(
                        erc20Reward.mul(1e18).div(tokenSupply)
                    );
                }
        
                return
                stake.amount.mul(_accERC20PerShare).div(1e18).sub(stake.rewardDebt);
            }
        
            /**
             * @notice function is getting number to see deposit timestamp for a user.
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return time when user deposited specific stake
             */
            function depositTimestamp(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory stake = stakeInfo[_user][stakeId];
                return stake.depositTime;
            }
        
            /**
             * @notice function is getting number to see withdraw timestamp for a user.
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return time when user withdraw specific stake
             */
            function withdrawTimestamp(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory stake = stakeInfo[_user][stakeId];
                return stake.withdrawTime;
            }
        
            /**
             * @notice function is getting number for total rewards the farm has yet to pay out.
             *
             * @return how many total reward the farm has yet to pay out.
             */
            function totalPending()
                external
                view
                returns (uint256)
            {
                if (block.timestamp <= startTime) {
                    return 0;
                }
        
                uint256 lastTime = block.timestamp < endTime
                ? block.timestamp
                : endTime;
                return rewardPerSecond.mul(lastTime - startTime).sub(paidOut);
            }
        
            /**
             * @notice function is getting number of stakes user has
             *
             * @param user - address of user
             *
             * @return how many times has user staked tokens
             */
            function getNumberOfUserStakes(
                address user
            )
                external
                view
                returns (uint256)
            {
                return stakeInfo[user].length;
            }
        
            /**
             * @notice function is getting user pending amounts, stakes and deposit time
             *
             * @param user - address of user
             *
             * @return array of deposits,pendingAmounts and depositTime
             */
            function getUserStakesAndPendingAmounts(
                address user
            )
                external
                view
                returns (
                    uint256[] memory,
                    uint256[] memory,
                    uint256[] memory
                )
            {
                uint256 numberOfStakes = stakeInfo[user].length;
        
                uint256[] memory deposits = new uint256[](numberOfStakes);
                uint256[] memory pendingAmounts = new uint256[](numberOfStakes);
                uint256[] memory depositTime = new uint256[](numberOfStakes);
        
                for (uint256 i = 0; i < numberOfStakes; i++) {
                    deposits[i] = deposited(user, i);
                    pendingAmounts[i] = pending(user, i);
                    depositTime[i] = depositTimestamp(user, i);
                }
        
                return (deposits, pendingAmounts, depositTime);
            }
        
            /**
             * @notice function is getting total rewards locked/unlocked
             *
             * @return totalRewardsUnlocked
             * @return totalRewardsLocked
             */
            function getTotalRewardsLockedUnlocked()
                external
                view
                returns (uint256, uint256)
            {
                uint256 totalRewardsLocked;
                uint256 totalRewardsUnlocked;
        
                if (block.timestamp <= startTime) {
                    totalRewardsUnlocked = 0;
                    totalRewardsLocked = totalRewards;
                } else {
                    uint256 lastTime = block.timestamp < endTime
                    ? block.timestamp
                    : endTime;
                    totalRewardsUnlocked = rewardPerSecond.mul(lastTime - startTime);
                    totalRewardsLocked = totalRewards - totalRewardsUnlocked;
                }
        
                return (totalRewardsUnlocked, totalRewardsLocked);
            }
        
            // Money managing functions
        
            /**
             * @notice function is funding the farm, increase the end time
             *
             * @param _amount - how many tokens is funded
             */
            function fund(
                uint256 _amount
            )
                external
            {
                uint256 balanceBefore = erc20.balanceOf(address(this));
                erc20.safeTransferFrom(address(msg.sender), address(this), _amount);
                uint256 balanceAfter = erc20.balanceOf(address(this));
        
                uint256 fundAmount;
                if(balanceAfter.sub(balanceBefore) <= _amount){
                    fundAmount = balanceAfter.sub(balanceBefore);
                }
                else{
                    fundAmount = _amount;
                }
        
                _fundInternal(fundAmount);
            }
        
            /**
             * @notice function is internally funding the farm by adding farmed rewards by user to the end
             *
             * @param _amount - how many tokens is funded
             */
            function _fundInternal(
                uint256 _amount
            )
                internal
            {
                require(
                    block.timestamp < endTime,
                    "fund: too late, the farm is closed"
                );
                require(_amount > 0, "Amount must be greater than 0.");
                // Compute new end time
                endTime += _amount.div(rewardPerSecond);
                // Increase farm total rewards
                totalRewards = totalRewards.add(_amount);
            }
        
            /**
             * @notice function is updating reward variables of the given pool to be up-to-date.
             */
            function updatePool()
                public
            {
                uint256 lastTime = block.timestamp < endTime
                ? block.timestamp
                : endTime;
        
                if (lastTime <= lastRewardTime) {
                    return;
                }
        
                uint256 tokenSupply = totalDeposits;
        
                if (tokenSupply == 0) {
                    lastRewardTime = lastTime;
                    return;
                }
        
                uint256 nrOfSeconds = lastTime.sub(lastRewardTime);
                uint256 erc20Reward = nrOfSeconds.mul(rewardPerSecond);
        
                accERC20PerShare = accERC20PerShare.add(
                    erc20Reward.mul(1e18).div(tokenSupply)
                );
                lastRewardTime = block.timestamp;
            }
        
            /**
             * @notice function is depositing ERC20 tokens to Farm for ERC20 allocation.
             *
             * @param _amount - how many tokens user is depositing
             */
            function deposit(
                uint256 _amount
            )
                external
                nonReentrant
                payable
            {
                require(
                    block.timestamp < endTime,
                    "Deposit: too late, the farm is closed"
                );
        
                StakeInfo memory stake;
                uint256 stakedAmount;
        
                // Update pool
                updatePool();
        
                uint256 beforeBalance = tokenStaked.balanceOf(address(this));
                tokenStaked.safeTransferFrom(
                    address(msg.sender),
                    address(this),
                    _amount
                );
                uint256 afterBalance = tokenStaked.balanceOf(address(this));
        
                if(afterBalance.sub(beforeBalance) <= _amount){
                    stakedAmount = afterBalance.sub(beforeBalance);
                }
                else{
                    stakedAmount = _amount;
                }
        
                if (isFlatFeeAllowed) {
                    // Collect flat fee
                    require(
                        msg.value >= flatFeeAmount,
                        "Payable amount is less than fee amount."
                    );
        
                    totalFeeCollectedETH = totalFeeCollectedETH.add(msg.value);
                } else if (stakeFeePercent > 0) {
                    // Handle this case only if flat fee is not allowed, and stakeFeePercent > 0
                    // Compute the fee
                    uint256 feeAmount = stakedAmount.mul(stakeFeePercent).div(100);
                    // Compute stake amount
                    stakedAmount = stakedAmount.sub(feeAmount);
                    totalFeeCollectedTokens = totalFeeCollectedTokens.add(feeAmount);
                }
        
                // Increase total deposits
                totalDeposits = totalDeposits.add(stakedAmount);
                // Update user accounting
                stake.amount = stakedAmount;
                stake.rewardDebt = stake.amount.mul(accERC20PerShare).div(1e18);
                stake.depositTime = block.timestamp;
                stake.addressOfUser = address(msg.sender);
                stake.withdrawTime = 0;
        
                _addParticipant(address(msg.sender));
        
                // Compute stake id
                uint256 stakeId = stakeInfo[msg.sender].length;
                // Push new stake to array of stakes for user
                stakeInfo[msg.sender].push(stake);
                // Emit deposit event
                emit Deposit(msg.sender, stakeId, stakedAmount);
            }
        
            // All withdraw functions
        
            /**
             * @notice function is withdrawing with caring about rewards
             *
             * @param _amount - how many tokens wants to be withdrawn
             * @param stakeId - Id of user stake
             */
            function withdraw(
                uint256 _amount,
                uint256 stakeId
            )
                external
                nonReentrant
                payable
                validateStakeByStakeId(msg.sender, stakeId)
            {
                bool minimalTimeStakeRespected;
                StakeInfo storage stake = stakeInfo[msg.sender][stakeId];
        
                require(
                    stake.amount >= _amount,
                    "withdraw: can't withdraw more than deposit"
                );
        
                updatePool();
                minimalTimeStakeRespected =
                stake.depositTime.add(minTimeToStake) <= block.timestamp;
                // if early withdraw is not allowed, user can't withdraw funds before
                if (!isEarlyWithdrawAllowed) {
                    // Check if user has respected minimal time to stake, require it.
                    require(
                        minimalTimeStakeRespected,
                        "User can not withdraw funds yet."
                    );
                }
        
                // Compute pending rewards amount of user rewards
                uint256 pendingAmount = stake
                .amount
                .mul(accERC20PerShare)
                .div(1e18)
                .sub(stake.rewardDebt);
        
                // Penalties in case user didn't stake enough time
                if (pendingAmount > 0) {
                    if (
                        penalty == EarlyWithdrawPenalty.BURN_REWARDS &&
                        !minimalTimeStakeRespected
                    ) {
                        // Burn to address (1)
                        totalTokensBurned = totalTokensBurned.add(pendingAmount);
                        _erc20Transfer(address(1), pendingAmount);
                    } else if (
                        penalty == EarlyWithdrawPenalty.REDISTRIBUTE_REWARDS &&
                        !minimalTimeStakeRespected
                    ) {
                        if (block.timestamp >= endTime) {
                            // Burn rewards because farm can not be funded anymore since it ended
                            _erc20Transfer(address(1), pendingAmount);
                            totalTokensBurned = totalTokensBurned.add(pendingAmount);
                        } else {
                            // Re-fund the farm
                            _fundInternal(pendingAmount);
                        }
                    } else {
                        // In case either there's no penalty
                        _erc20Transfer(msg.sender, pendingAmount);
                    }
                }
        
                _removeParticipant(address(msg.sender), _amount);
        
                stake.withdrawTime = block.timestamp;
                stake.amount = stake.amount.sub(_amount);
                stake.rewardDebt = stake.amount.mul(accERC20PerShare).div(1e18);
        
                tokenStaked.safeTransfer(address(msg.sender), _amount);
                totalDeposits = totalDeposits.sub(_amount);
        
                // Emit Withdraw event
                emit Withdraw(msg.sender, stakeId, _amount);
            }
        
            /**
             * @notice function is withdrawing without caring about rewards. EMERGENCY ONLY.
             *
             * @param stakeId - Id of user stake
             */
            function emergencyWithdraw(
                uint256 stakeId
            )
                external
                nonReentrant
                validateStakeByStakeId(msg.sender, stakeId)
            {
                StakeInfo storage stake = stakeInfo[msg.sender][stakeId];
        
                // if early withdraw is not allowed, user can't withdraw funds before
                if (!isEarlyWithdrawAllowed) {
                    bool minimalTimeStakeRespected = stake.depositTime.add(
                        minTimeToStake
                    ) <= block.timestamp;
                    // Check if user has respected minimal time to stake, require it.
                    require(
                        minimalTimeStakeRespected,
                        "User can not withdraw funds yet."
                    );
                }
        
                tokenStaked.safeTransfer(address(msg.sender), stake.amount);
                totalDeposits = totalDeposits.sub(stake.amount);
        
                _removeParticipant(address(msg.sender), stake.amount);
                stake.withdrawTime = block.timestamp;
        
                emit EmergencyWithdraw(msg.sender, stakeId, stake.amount);
        
                stake.amount = 0;
                stake.rewardDebt = 0;
            }
        
            /**
             * @notice function is withdrawing fee collected in ERC value
             */
            function withdrawCollectedFeesERC()
                external
                onlyOwner
            {
                erc20.transfer(feeCollector, totalFeeCollectedTokens);
                totalFeeCollectedTokens = 0;
            }
        
            /**
             * @notice function is withdrawing fee collected in ETH value
             */
            function withdrawCollectedFeesETH()
                external
                onlyOwner
            {
                (bool sent, ) = payable(feeCollector).call{value: totalFeeCollectedETH}("");
                require(sent, "Failed to end flat fee");
                totalFeeCollectedETH = 0;
            }
        
            /**
             * @notice function is withdrawing tokens if stuck
             *
             * @param _erc20 - address of token address
             * @param _amount - number of how many tokens
             * @param _beneficiary - address of user that collects tokens deposited by mistake
             */
            function withdrawTokensIfStuck(
                address _erc20,
                uint256 _amount,
                address _beneficiary
            )
                external
                onlyOwner
            {
                IERC20 token = IERC20(_erc20);
                require(tokenStaked != token, "User tokens can not be pulled");
                require(
                    _beneficiary != address(0x0),
                    "_beneficiary can not be 0x0 address"
                );
        
                token.safeTransfer(_beneficiary, _amount);
            }
        
            /**
             * @notice function is transferring ERC20,
             * and update the required ERC20 to payout all rewards
             *
             * @param _to - transfer on this address
             * @param _amount - number of how many tokens
             */
            function _erc20Transfer(
                address _to,
                uint256 _amount
            )
                internal
            {
                if (isFlatFeeAllowed) {
                    // Collect flat fee
                    require(
                        msg.value >= flatFeeAmount,
                        "Payable amount is less than fee amount."
                    );
                    // Increase amount of fees collected
                    totalFeeCollectedETH = totalFeeCollectedETH.add(msg.value);
                    // send reward
                    erc20.transfer(_to, _amount);
                    paidOut += _amount;
                } else if (stakeFeePercent > 0) {
                    // Collect reward fee
                    uint256 feeAmount = _amount.mul(rewardFeePercent).div(100);
                    uint256 rewardAmount = _amount.sub(feeAmount);
        
                    // Increase amount of fees collected
                    totalFeeCollectedTokens = totalFeeCollectedTokens.add(feeAmount);
        
                    // send reward
                    erc20.transfer(_to, rewardAmount);
                    paidOut += _amount;
                } else {
                    erc20.transfer(_to, _amount);
                    paidOut += _amount;
                }
            }
        }

        File 2 of 5: TransparentUpgradeableProxy
        // SPDX-License-Identifier: MIT
        // File contracts/proxy/Proxy.sol
        pragma solidity ^0.8.0;
        /**
         * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
         * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
         * be specified by overriding the virtual {_implementation} function.
         *
         * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
         * different contract through the {_delegate} function.
         *
         * The success and return data of the delegated call will be returned back to the caller of the proxy.
         */
        abstract contract Proxy {
            /**
             * @dev Delegates the current call to `implementation`.
             *
             * This function does not return to its internall call site, it will return directly to the external caller.
             */
            function _delegate(address implementation) internal virtual {
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    // Copy msg.data. We take full control of memory in this inline assembly
                    // block because it will not return to Solidity code. We overwrite the
                    // Solidity scratch pad at memory position 0.
                    calldatacopy(0, 0, calldatasize())
                    // Call the implementation.
                    // out and outsize are 0 because we don't know the size yet.
                    let result := delegatecall(
                        gas(),
                        implementation,
                        0,
                        calldatasize(),
                        0,
                        0
                    )
                    // Copy the returned data.
                    returndatacopy(0, 0, returndatasize())
                    switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                            revert(0, returndatasize())
                        }
                        default {
                            return(0, returndatasize())
                        }
                }
            }
            /**
             * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
             * and {_fallback} should delegate.
             */
            function _implementation() internal view virtual returns (address);
            /**
             * @dev Delegates the current call to the address returned by `_implementation()`.
             *
             * This function does not return to its internall call site, it will return directly to the external caller.
             */
            function _fallback() internal virtual {
                _beforeFallback();
                _delegate(_implementation());
            }
            /**
             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
             * function in the contract matches the call data.
             */
            fallback() external payable {
                _fallback();
            }
            /**
             * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
             * is empty.
             */
            receive() external payable {
                _fallback();
            }
            /**
             * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
             * call, or as part of the Solidity `fallback` or `receive` functions.
             *
             * If overriden should call `super._beforeFallback()`.
             */
            function _beforeFallback() internal virtual {}
        }
        // File contracts/utils/Address.sol
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    size := extcodesize(account)
                }
                return size > 0;
            }
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(
                    address(this).balance >= amount,
                    "Address: insufficient balance"
                );
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{value: amount}("");
                require(
                    success,
                    "Address: unable to send value, recipient may have reverted"
                );
            }
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data)
                internal
                returns (bytes memory)
            {
                return functionCall(target, data, "Address: low-level call failed");
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(
                address target,
                bytes memory data,
                uint256 value
            ) internal returns (bytes memory) {
                return
                    functionCallWithValue(
                        target,
                        data,
                        value,
                        "Address: low-level call with value failed"
                    );
            }
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(
                address target,
                bytes memory data,
                uint256 value,
                string memory errorMessage
            ) internal returns (bytes memory) {
                require(
                    address(this).balance >= value,
                    "Address: insufficient balance for call"
                );
                require(isContract(target), "Address: call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) =
                    target.call{value: value}(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data)
                internal
                view
                returns (bytes memory)
            {
                return
                    functionStaticCall(
                        target,
                        data,
                        "Address: low-level static call failed"
                    );
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data)
                internal
                returns (bytes memory)
            {
                return
                    functionDelegateCall(
                        target,
                        data,
                        "Address: low-level delegate call failed"
                    );
            }
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(
                address target,
                bytes memory data,
                string memory errorMessage
            ) internal returns (bytes memory) {
                require(isContract(target), "Address: delegate call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
            function _verifyCallResult(
                bool success,
                bytes memory returndata,
                string memory errorMessage
            ) private pure returns (bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        // File contracts/proxy/ERC1967/ERC1967Proxy.sol
        /**
         * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
         * implementation address that can be changed. This address is stored in storage in the location specified by
         * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
         * implementation behind the proxy.
         *
         * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
         * {TransparentUpgradeableProxy}.
         */
        contract ERC1967Proxy is Proxy {
            /**
             * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
             *
             * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
             * function call, and allows initializating the storage of the proxy like a Solidity constructor.
             */
            constructor(address _logic, bytes memory _data) payable {
                assert(
                    _IMPLEMENTATION_SLOT ==
                        bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
                );
                _setImplementation(_logic);
                if (_data.length > 0) {
                    Address.functionDelegateCall(_logic, _data);
                }
            }
            /**
             * @dev Emitted when the implementation is upgraded.
             */
            event Upgraded(address indexed implementation);
            /**
             * @dev Storage slot with the address of the current implementation.
             * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 private constant _IMPLEMENTATION_SLOT =
                0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
            /**
             * @dev Returns the current implementation address.
             */
            function _implementation()
                internal
                view
                virtual
                override
                returns (address impl)
            {
                bytes32 slot = _IMPLEMENTATION_SLOT;
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    impl := sload(slot)
                }
            }
            /**
             * @dev Upgrades the proxy to a new implementation.
             *
             * Emits an {Upgraded} event.
             */
            function _upgradeTo(address newImplementation) internal virtual {
                _setImplementation(newImplementation);
                emit Upgraded(newImplementation);
            }
            /**
             * @dev Stores a new address in the EIP1967 implementation slot.
             */
            function _setImplementation(address newImplementation) private {
                require(
                    Address.isContract(newImplementation),
                    "ERC1967Proxy: new implementation is not a contract"
                );
                bytes32 slot = _IMPLEMENTATION_SLOT;
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    sstore(slot, newImplementation)
                }
            }
        }
        // File contracts/proxy/transparent/TransparentUpgradeableProxy.sol
        /**
         * @dev This contract implements a proxy that is upgradeable by an admin.
         *
         * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
         * clashing], which can potentially be used in an attack, this contract uses the
         * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
         * things that go hand in hand:
         *
         * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
         * that call matches one of the admin functions exposed by the proxy itself.
         * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
         * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
         * "admin cannot fallback to proxy target".
         *
         * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
         * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
         * to sudden errors when trying to call a function from the proxy implementation.
         *
         * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
         * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
         */
        contract TransparentUpgradeableProxy is ERC1967Proxy {
            /**
             * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
             * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
             */
            constructor(
                address _logic,
                address admin_,
                bytes memory _data
            ) payable ERC1967Proxy(_logic, _data) {
                assert(
                    _ADMIN_SLOT ==
                        bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)
                );
                _setAdmin(admin_);
            }
            /**
             * @dev Emitted when the admin account has changed.
             */
            event AdminChanged(address previousAdmin, address newAdmin);
            /**
             * @dev Storage slot with the admin of the contract.
             * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
             * validated in the constructor.
             */
            bytes32 private constant _ADMIN_SLOT =
                0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
            /**
             * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
             */
            modifier ifAdmin() {
                if (msg.sender == _admin()) {
                    _;
                } else {
                    _fallback();
                }
            }
            /**
             * @dev Returns the current admin.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
             *
             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
             * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
             */
            function admin() external ifAdmin returns (address admin_) {
                admin_ = _admin();
            }
            /**
             * @dev Returns the current implementation.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
             *
             * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
             * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
             * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
             */
            function implementation()
                external
                ifAdmin
                returns (address implementation_)
            {
                implementation_ = _implementation();
            }
            /**
             * @dev Changes the admin of the proxy.
             *
             * Emits an {AdminChanged} event.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
             */
            function changeAdmin(address newAdmin) external virtual ifAdmin {
                require(
                    newAdmin != address(0),
                    "TransparentUpgradeableProxy: new admin is the zero address"
                );
                emit AdminChanged(_admin(), newAdmin);
                _setAdmin(newAdmin);
            }
            /**
             * @dev Upgrade the implementation of the proxy.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
             */
            function upgradeTo(address newImplementation) external virtual ifAdmin {
                _upgradeTo(newImplementation);
            }
            /**
             * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
             * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
             * proxied contract.
             *
             * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
             */
            function upgradeToAndCall(address newImplementation, bytes calldata data)
                external
                payable
                virtual
                ifAdmin
            {
                _upgradeTo(newImplementation);
                Address.functionDelegateCall(newImplementation, data);
            }
            /**
             * @dev Returns the current admin.
             */
            function _admin() internal view virtual returns (address adm) {
                bytes32 slot = _ADMIN_SLOT;
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    adm := sload(slot)
                }
            }
            /**
             * @dev Stores a new address in the EIP1967 admin slot.
             */
            function _setAdmin(address newAdmin) private {
                bytes32 slot = _ADMIN_SLOT;
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    sstore(slot, newAdmin)
                }
            }
            /**
             * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
             */
            function _beforeFallback() internal virtual override {
                require(
                    msg.sender != _admin(),
                    "TransparentUpgradeableProxy: admin cannot fallback to proxy target"
                );
                super._beforeFallback();
            }
        }
        

        File 3 of 5: UniswapV2Pair
        // File: contracts/interfaces/IUniswapV2Pair.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2Pair {
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint value);
        
            function name() external pure returns (string memory);
            function symbol() external pure returns (string memory);
            function decimals() external pure returns (uint8);
            function totalSupply() external view returns (uint);
            function balanceOf(address owner) external view returns (uint);
            function allowance(address owner, address spender) external view returns (uint);
        
            function approve(address spender, uint value) external returns (bool);
            function transfer(address to, uint value) external returns (bool);
            function transferFrom(address from, address to, uint value) external returns (bool);
        
            function DOMAIN_SEPARATOR() external view returns (bytes32);
            function PERMIT_TYPEHASH() external pure returns (bytes32);
            function nonces(address owner) external view returns (uint);
        
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        
            event Mint(address indexed sender, uint amount0, uint amount1);
            event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
            event Swap(
                address indexed sender,
                uint amount0In,
                uint amount1In,
                uint amount0Out,
                uint amount1Out,
                address indexed to
            );
            event Sync(uint112 reserve0, uint112 reserve1);
        
            function MINIMUM_LIQUIDITY() external pure returns (uint);
            function factory() external view returns (address);
            function token0() external view returns (address);
            function token1() external view returns (address);
            function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
            function price0CumulativeLast() external view returns (uint);
            function price1CumulativeLast() external view returns (uint);
            function kLast() external view returns (uint);
        
            function mint(address to) external returns (uint liquidity);
            function burn(address to) external returns (uint amount0, uint amount1);
            function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
            function skim(address to) external;
            function sync() external;
        
            function initialize(address, address) external;
        }
        
        // File: contracts/interfaces/IUniswapV2ERC20.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2ERC20 {
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint value);
        
            function name() external pure returns (string memory);
            function symbol() external pure returns (string memory);
            function decimals() external pure returns (uint8);
            function totalSupply() external view returns (uint);
            function balanceOf(address owner) external view returns (uint);
            function allowance(address owner, address spender) external view returns (uint);
        
            function approve(address spender, uint value) external returns (bool);
            function transfer(address to, uint value) external returns (bool);
            function transferFrom(address from, address to, uint value) external returns (bool);
        
            function DOMAIN_SEPARATOR() external view returns (bytes32);
            function PERMIT_TYPEHASH() external pure returns (bytes32);
            function nonces(address owner) external view returns (uint);
        
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        }
        
        // File: contracts/libraries/SafeMath.sol
        
        pragma solidity =0.5.16;
        
        // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
        
        library SafeMath {
            function add(uint x, uint y) internal pure returns (uint z) {
                require((z = x + y) >= x, 'ds-math-add-overflow');
            }
        
            function sub(uint x, uint y) internal pure returns (uint z) {
                require((z = x - y) <= x, 'ds-math-sub-underflow');
            }
        
            function mul(uint x, uint y) internal pure returns (uint z) {
                require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
            }
        }
        
        // File: contracts/UniswapV2ERC20.sol
        
        pragma solidity =0.5.16;
        
        
        
        contract UniswapV2ERC20 is IUniswapV2ERC20 {
            using SafeMath for uint;
        
            string public constant name = 'Uniswap V2';
            string public constant symbol = 'UNI-V2';
            uint8 public constant decimals = 18;
            uint  public totalSupply;
            mapping(address => uint) public balanceOf;
            mapping(address => mapping(address => uint)) public allowance;
        
            bytes32 public DOMAIN_SEPARATOR;
            // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
            bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
            mapping(address => uint) public nonces;
        
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint value);
        
            constructor() public {
                uint chainId;
                assembly {
                    chainId := chainid
                }
                DOMAIN_SEPARATOR = keccak256(
                    abi.encode(
                        keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                        keccak256(bytes(name)),
                        keccak256(bytes('1')),
                        chainId,
                        address(this)
                    )
                );
            }
        
            function _mint(address to, uint value) internal {
                totalSupply = totalSupply.add(value);
                balanceOf[to] = balanceOf[to].add(value);
                emit Transfer(address(0), to, value);
            }
        
            function _burn(address from, uint value) internal {
                balanceOf[from] = balanceOf[from].sub(value);
                totalSupply = totalSupply.sub(value);
                emit Transfer(from, address(0), value);
            }
        
            function _approve(address owner, address spender, uint value) private {
                allowance[owner][spender] = value;
                emit Approval(owner, spender, value);
            }
        
            function _transfer(address from, address to, uint value) private {
                balanceOf[from] = balanceOf[from].sub(value);
                balanceOf[to] = balanceOf[to].add(value);
                emit Transfer(from, to, value);
            }
        
            function approve(address spender, uint value) external returns (bool) {
                _approve(msg.sender, spender, value);
                return true;
            }
        
            function transfer(address to, uint value) external returns (bool) {
                _transfer(msg.sender, to, value);
                return true;
            }
        
            function transferFrom(address from, address to, uint value) external returns (bool) {
                if (allowance[from][msg.sender] != uint(-1)) {
                    allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                }
                _transfer(from, to, value);
                return true;
            }
        
            function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                bytes32 digest = keccak256(
                    abi.encodePacked(
                        '\x19\x01',
                        DOMAIN_SEPARATOR,
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                    )
                );
                address recoveredAddress = ecrecover(digest, v, r, s);
                require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                _approve(owner, spender, value);
            }
        }
        
        // File: contracts/libraries/Math.sol
        
        pragma solidity =0.5.16;
        
        // a library for performing various math operations
        
        library Math {
            function min(uint x, uint y) internal pure returns (uint z) {
                z = x < y ? x : y;
            }
        
            // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
            function sqrt(uint y) internal pure returns (uint z) {
                if (y > 3) {
                    z = y;
                    uint x = y / 2 + 1;
                    while (x < z) {
                        z = x;
                        x = (y / x + x) / 2;
                    }
                } else if (y != 0) {
                    z = 1;
                }
            }
        }
        
        // File: contracts/libraries/UQ112x112.sol
        
        pragma solidity =0.5.16;
        
        // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
        
        // range: [0, 2**112 - 1]
        // resolution: 1 / 2**112
        
        library UQ112x112 {
            uint224 constant Q112 = 2**112;
        
            // encode a uint112 as a UQ112x112
            function encode(uint112 y) internal pure returns (uint224 z) {
                z = uint224(y) * Q112; // never overflows
            }
        
            // divide a UQ112x112 by a uint112, returning a UQ112x112
            function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                z = x / uint224(y);
            }
        }
        
        // File: contracts/interfaces/IERC20.sol
        
        pragma solidity >=0.5.0;
        
        interface IERC20 {
            event Approval(address indexed owner, address indexed spender, uint value);
            event Transfer(address indexed from, address indexed to, uint value);
        
            function name() external view returns (string memory);
            function symbol() external view returns (string memory);
            function decimals() external view returns (uint8);
            function totalSupply() external view returns (uint);
            function balanceOf(address owner) external view returns (uint);
            function allowance(address owner, address spender) external view returns (uint);
        
            function approve(address spender, uint value) external returns (bool);
            function transfer(address to, uint value) external returns (bool);
            function transferFrom(address from, address to, uint value) external returns (bool);
        }
        
        // File: contracts/interfaces/IUniswapV2Factory.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2Factory {
            event PairCreated(address indexed token0, address indexed token1, address pair, uint);
        
            function feeTo() external view returns (address);
            function feeToSetter() external view returns (address);
        
            function getPair(address tokenA, address tokenB) external view returns (address pair);
            function allPairs(uint) external view returns (address pair);
            function allPairsLength() external view returns (uint);
        
            function createPair(address tokenA, address tokenB) external returns (address pair);
        
            function setFeeTo(address) external;
            function setFeeToSetter(address) external;
        }
        
        // File: contracts/interfaces/IUniswapV2Callee.sol
        
        pragma solidity >=0.5.0;
        
        interface IUniswapV2Callee {
            function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
        }
        
        // File: contracts/UniswapV2Pair.sol
        
        pragma solidity =0.5.16;
        
        
        
        
        
        
        
        
        contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
            using SafeMath  for uint;
            using UQ112x112 for uint224;
        
            uint public constant MINIMUM_LIQUIDITY = 10**3;
            bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
        
            address public factory;
            address public token0;
            address public token1;
        
            uint112 private reserve0;           // uses single storage slot, accessible via getReserves
            uint112 private reserve1;           // uses single storage slot, accessible via getReserves
            uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
        
            uint public price0CumulativeLast;
            uint public price1CumulativeLast;
            uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
        
            uint private unlocked = 1;
            modifier lock() {
                require(unlocked == 1, 'UniswapV2: LOCKED');
                unlocked = 0;
                _;
                unlocked = 1;
            }
        
            function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                _reserve0 = reserve0;
                _reserve1 = reserve1;
                _blockTimestampLast = blockTimestampLast;
            }
        
            function _safeTransfer(address token, address to, uint value) private {
                (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
            }
        
            event Mint(address indexed sender, uint amount0, uint amount1);
            event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
            event Swap(
                address indexed sender,
                uint amount0In,
                uint amount1In,
                uint amount0Out,
                uint amount1Out,
                address indexed to
            );
            event Sync(uint112 reserve0, uint112 reserve1);
        
            constructor() public {
                factory = msg.sender;
            }
        
            // called once by the factory at time of deployment
            function initialize(address _token0, address _token1) external {
                require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                token0 = _token0;
                token1 = _token1;
            }
        
            // update reserves and, on the first call per block, price accumulators
            function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                    // * never overflows, and + overflow is desired
                    price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                    price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                }
                reserve0 = uint112(balance0);
                reserve1 = uint112(balance1);
                blockTimestampLast = blockTimestamp;
                emit Sync(reserve0, reserve1);
            }
        
            // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
            function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                address feeTo = IUniswapV2Factory(factory).feeTo();
                feeOn = feeTo != address(0);
                uint _kLast = kLast; // gas savings
                if (feeOn) {
                    if (_kLast != 0) {
                        uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                        uint rootKLast = Math.sqrt(_kLast);
                        if (rootK > rootKLast) {
                            uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                            uint denominator = rootK.mul(5).add(rootKLast);
                            uint liquidity = numerator / denominator;
                            if (liquidity > 0) _mint(feeTo, liquidity);
                        }
                    }
                } else if (_kLast != 0) {
                    kLast = 0;
                }
            }
        
            // this low-level function should be called from a contract which performs important safety checks
            function mint(address to) external lock returns (uint liquidity) {
                (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                uint balance0 = IERC20(token0).balanceOf(address(this));
                uint balance1 = IERC20(token1).balanceOf(address(this));
                uint amount0 = balance0.sub(_reserve0);
                uint amount1 = balance1.sub(_reserve1);
        
                bool feeOn = _mintFee(_reserve0, _reserve1);
                uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                if (_totalSupply == 0) {
                    liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                   _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                } else {
                    liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                }
                require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                _mint(to, liquidity);
        
                _update(balance0, balance1, _reserve0, _reserve1);
                if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                emit Mint(msg.sender, amount0, amount1);
            }
        
            // this low-level function should be called from a contract which performs important safety checks
            function burn(address to) external lock returns (uint amount0, uint amount1) {
                (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                address _token0 = token0;                                // gas savings
                address _token1 = token1;                                // gas savings
                uint balance0 = IERC20(_token0).balanceOf(address(this));
                uint balance1 = IERC20(_token1).balanceOf(address(this));
                uint liquidity = balanceOf[address(this)];
        
                bool feeOn = _mintFee(_reserve0, _reserve1);
                uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                _burn(address(this), liquidity);
                _safeTransfer(_token0, to, amount0);
                _safeTransfer(_token1, to, amount1);
                balance0 = IERC20(_token0).balanceOf(address(this));
                balance1 = IERC20(_token1).balanceOf(address(this));
        
                _update(balance0, balance1, _reserve0, _reserve1);
                if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                emit Burn(msg.sender, amount0, amount1, to);
            }
        
            // this low-level function should be called from a contract which performs important safety checks
            function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
        
                uint balance0;
                uint balance1;
                { // scope for _token{0,1}, avoids stack too deep errors
                address _token0 = token0;
                address _token1 = token1;
                require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                balance0 = IERC20(_token0).balanceOf(address(this));
                balance1 = IERC20(_token1).balanceOf(address(this));
                }
                uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                }
        
                _update(balance0, balance1, _reserve0, _reserve1);
                emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
            }
        
            // force balances to match reserves
            function skim(address to) external lock {
                address _token0 = token0; // gas savings
                address _token1 = token1; // gas savings
                _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
            }
        
            // force reserves to match balances
            function sync() external lock {
                _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
            }
        }

        File 4 of 5: TokensFarm
        // Sources flattened with hardhat v2.1.2 https://hardhat.org
        
        // File @openzeppelin/contracts/token/ERC20/IERC20.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        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);
        }
        
        
        // File @openzeppelin/contracts/math/SafeMath.sol
        
        
        pragma solidity >=0.6.0 <0.8.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, with an overflow flag.
             *
             * _Available since v3.4._
             */
            function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                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) {
                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) {
                // 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) {
                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) {
                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) {
                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) {
                require(b <= a, "SafeMath: subtraction overflow");
                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) {
                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, reverting 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) {
                require(b > 0, "SafeMath: division by zero");
                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) {
                require(b > 0, "SafeMath: modulo by zero");
                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) {
                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.
             *
             * CAUTION: This function is deprecated because it requires allocating memory for the error
             * message unnecessarily. For custom revert reasons use {tryDiv}.
             *
             * 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);
                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) {
                require(b > 0, errorMessage);
                return a % b;
            }
        }
        
        
        // File @openzeppelin/contracts/utils/Address.sol
        
        
        pragma solidity >=0.6.2 <0.8.0;
        
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
        
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
        
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                return functionDelegateCall(target, data, "Address: low-level delegate call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a delegate call.
             *
             * _Available since v3.4._
             */
            function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                require(isContract(target), "Address: delegate call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.delegatecall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
        
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        
        // File @openzeppelin/contracts/token/ERC20/SafeERC20.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        
        
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using SafeMath for uint256;
            using Address for address;
        
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
        
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
        
            /**
             * @dev Deprecated. This function has issues similar to the ones found in
             * {IERC20-approve}, and its usage is discouraged.
             *
             * Whenever possible, use {safeIncreaseAllowance} and
             * {safeDecreaseAllowance} instead.
             */
            function safeApprove(IERC20 token, address spender, uint256 value) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                // solhint-disable-next-line max-line-length
                require((value == 0) || (token.allowance(address(this), spender) == 0),
                    "SafeERC20: approve from non-zero to non-zero allowance"
                );
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
        
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).add(value);
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
        
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                // the target address contains contract code and also asserts for success in the low-level call.
        
                bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                if (returndata.length > 0) { // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        
        
        // File @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol
        
        
        pragma solidity >=0.6.2 <0.8.0;
        
        /**
         * @dev Collection of functions related to the address type
         */
        library AddressUpgradeable {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // This method relies on extcodesize, which returns 0 for contracts in
                // construction, since the code is only stored at the end of the
                // constructor execution.
        
                uint256 size;
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
        
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
        
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        
            /**
             * @dev Performs a Solidity function call using a low level `call`. A
             * plain`call` is an unsafe replacement for a function call: use this
             * function instead.
             *
             * If `target` reverts with a revert reason, it is bubbled up by this
             * function (like regular Solidity function calls).
             *
             * Returns the raw returned data. To convert to the expected return value,
             * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
             *
             * Requirements:
             *
             * - `target` must be a contract.
             * - calling `target` with `data` must not revert.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCall(target, data, "Address: low-level call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
             * `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                return functionCallWithValue(target, data, 0, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but also transferring `value` wei to `target`.
             *
             * Requirements:
             *
             * - the calling contract must have an ETH balance of at least `value`.
             * - the called Solidity function must be `payable`.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
             * with `errorMessage` as a fallback revert reason when `target` reverts.
             *
             * _Available since v3.1._
             */
            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                require(address(this).balance >= value, "Address: insufficient balance for call");
                require(isContract(target), "Address: call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.call{ value: value }(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                return functionStaticCall(target, data, "Address: low-level static call failed");
            }
        
            /**
             * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
             * but performing a static call.
             *
             * _Available since v3.3._
             */
            function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                require(isContract(target), "Address: static call to non-contract");
        
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = target.staticcall(data);
                return _verifyCallResult(success, returndata, errorMessage);
            }
        
            function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                if (success) {
                    return returndata;
                } else {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
        
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
        }
        
        
        // File @openzeppelin/contracts-upgradeable/proxy/Initializable.sol
        
        
        // solhint-disable-next-line compiler-version
        pragma solidity >=0.4.24 <0.8.0;
        
        /**
         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
         * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
         *
         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
         * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
         *
         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
         */
        abstract contract Initializable {
        
            /**
             * @dev Indicates that the contract has been initialized.
             */
            bool private _initialized;
        
            /**
             * @dev Indicates that the contract is in the process of being initialized.
             */
            bool private _initializing;
        
            /**
             * @dev Modifier to protect an initializer function from being invoked twice.
             */
            modifier initializer() {
                require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
        
                bool isTopLevelCall = !_initializing;
                if (isTopLevelCall) {
                    _initializing = true;
                    _initialized = true;
                }
        
                _;
        
                if (isTopLevelCall) {
                    _initializing = false;
                }
            }
        
            /// @dev Returns true if and only if the function is running in the constructor
            function _isConstructor() private view returns (bool) {
                return !AddressUpgradeable.isContract(address(this));
            }
        }
        
        
        // File @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol
        
        
        pragma solidity >=0.6.0 <0.8.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 ContextUpgradeable is Initializable {
            function __Context_init() internal initializer {
                __Context_init_unchained();
            }
        
            function __Context_init_unchained() internal initializer {
            }
            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;
            }
            uint256[50] private __gap;
        }
        
        
        // File @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * By default, the owner account will be the one that deploys the contract. This
         * can later be changed with {transferOwnership}.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
            address private _owner;
        
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        
            /**
             * @dev Initializes the contract setting the deployer as the initial owner.
             */
            function __Ownable_init() internal initializer {
                __Context_init_unchained();
                __Ownable_init_unchained();
            }
        
            function __Ownable_init_unchained() internal initializer {
                address msgSender = _msgSender();
                _owner = msgSender;
                emit OwnershipTransferred(address(0), msgSender);
            }
        
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view virtual 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;
            }
            uint256[49] private __gap;
        }
        
        
        // File @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol
        
        
        pragma solidity >=0.6.0 <0.8.0;
        
        /**
         * @dev Contract module that helps prevent reentrant calls to a function.
         *
         * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
         * available, which can be applied to functions to make sure there are no nested
         * (reentrant) calls to them.
         *
         * Note that because there is a single `nonReentrant` guard, functions marked as
         * `nonReentrant` may not call one another. This can be worked around by making
         * those functions `private`, and then adding `external` `nonReentrant` entry
         * points to them.
         *
         * TIP: If you would like to learn more about reentrancy and alternative ways
         * to protect against it, check out our blog post
         * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
         */
        abstract contract ReentrancyGuardUpgradeable is Initializable {
            // Booleans are more expensive than uint256 or any type that takes up a full
            // word because each write operation emits an extra SLOAD to first read the
            // slot's contents, replace the bits taken up by the boolean, and then write
            // back. This is the compiler's defense against contract upgrades and
            // pointer aliasing, and it cannot be disabled.
        
            // The values being non-zero value makes deployment a bit more expensive,
            // but in exchange the refund on every call to nonReentrant will be lower in
            // amount. Since refunds are capped to a percentage of the total
            // transaction's gas, it is best to keep them low in cases like this one, to
            // increase the likelihood of the full refund coming into effect.
            uint256 private constant _NOT_ENTERED = 1;
            uint256 private constant _ENTERED = 2;
        
            uint256 private _status;
        
            function __ReentrancyGuard_init() internal initializer {
                __ReentrancyGuard_init_unchained();
            }
        
            function __ReentrancyGuard_init_unchained() internal initializer {
                _status = _NOT_ENTERED;
            }
        
            /**
             * @dev Prevents a contract from calling itself, directly or indirectly.
             * Calling a `nonReentrant` function from another `nonReentrant`
             * function is not supported. It is possible to prevent this from happening
             * by making the `nonReentrant` function external, and make it call a
             * `private` function that does the actual work.
             */
            modifier nonReentrant() {
                // On the first call to nonReentrant, _notEntered will be true
                require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
        
                // Any calls to nonReentrant after this point will fail
                _status = _ENTERED;
        
                _;
        
                // By storing the original value once again, a refund is triggered (see
                // https://eips.ethereum.org/EIPS/eip-2200)
                _status = _NOT_ENTERED;
            }
            uint256[49] private __gap;
        }
        
        
        // File contracts/TokensFarm.sol
        
        pragma solidity 0.6.12;
        
        
        
        
        
        contract TokensFarm is OwnableUpgradeable, ReentrancyGuardUpgradeable {
        
            using SafeMath for uint256;
            using SafeERC20 for IERC20;
        
            // Enums
            enum EarlyWithdrawPenalty {
                NO_PENALTY,
                BURN_REWARDS,
                REDISTRIBUTE_REWARDS
            }
        
            // Info of each user.
            struct StakeInfo {
                // How many tokens the user has provided.
                uint256 amount;
                // Reward debt.
                uint256 rewardDebt;
                // Time when user deposited.
                uint256 depositTime;
                // Time when user withdraw
                uint256 withdrawTime;
                // Address of user
                address addressOfUser;
            }
        
            // Address of ERC20 token contract.
            IERC20 public tokenStaked;
            // Last time number that ERC20s distribution occurs.
            uint256 public lastRewardTime;
            // Accumulated ERC20s per share, times 1e18.
            uint256 public accERC20PerShare;
            // Total tokens deposited in the farm.
            uint256 public totalDeposits;
            // If contractor allows early withdraw on stakes
            bool public isEarlyWithdrawAllowed;
            // Minimal period of time to stake
            uint256 public minTimeToStake;
            // Address of the ERC20 Token contract.
            IERC20 public erc20;
            // The total amount of ERC20 that's paid out as reward.
            uint256 public paidOut;
            // ERC20 tokens rewarded per second.
            uint256 public rewardPerSecond;
            // Total rewards added to farm
            uint256 public totalRewards;
            // Info of each user that stakes ERC20 tokens.
            mapping(address => StakeInfo[]) public stakeInfo;
            // The time when farming starts.
            uint256 public startTime;
            // The time when farming ends.
            uint256 public endTime;
            // Early withdraw penalty
            EarlyWithdrawPenalty public penalty;
            // Stake fee percent
            uint256 public stakeFeePercent;
            // Reward fee percent
            uint256 public rewardFeePercent;
            // Fee collector address
            address payable public feeCollector;
            // Flat fee amount
            uint256 public flatFeeAmount;
            // Fee option
            bool public isFlatFeeAllowed;
            // Total tokens burned
            uint256 public totalTokensBurned;
            // Total fee collected
            uint256 public totalFeeCollectedETH;
            // Total fee collected in tokens
            uint256 public totalFeeCollectedTokens;
            // Address of farm instance
            address public farmImplementation;
            // NumberOfUsers participating in farm
            uint256 public noOfUsers;
            // Addresses of all users that are currently participating
            address[] public participants;
            // Mapping of every users spot in array
            mapping(address => uint256) public id;
        
            // Events
            event Deposit(
                address indexed user,
                uint256 indexed stakeId,
                uint256 indexed amount
            );
            event Withdraw(
                address indexed user,
                uint256 indexed stakeId,
                uint256 indexed amount
            );
            event EmergencyWithdraw(
                address indexed user,
                uint256 indexed stakeId,
                uint256 indexed amount
            );
            event EarlyWithdrawPenaltySet(EarlyWithdrawPenalty indexed penalty);
            event MinTimeToStakeSet(uint256 indexed minTimeToStake);
            event IsEarlyWithdrawAllowedSet(bool indexed allowed);
            event StakeFeePercentSet(uint256 indexed stakeFeePercent);
            event RewardFeePercentSet(uint256 indexed rewardFeePercent);
            event FlatFeeAmountSet(uint256 indexed flatFeeAmount);
            event IsFlatFeeAllowedSet(bool indexed allowed);
            event FeeCollectorSet(address payable indexed feeCollector);
        
            // Modifiers
            modifier validateStakeByStakeId(address _user, uint256 stakeId) {
                require(stakeId < stakeInfo[_user].length, "Stake does not exist");
                _;
            }
        
            /**
             * @notice function sets initial state of contract
             *
             * @param _erc20 - address of reward token
             * @param _rewardPerSecond - number of reward per second
             * @param _startTime - beginning of farm
             * @param _minTimeToStake - how much time needs to pass before staking
             * @param _isEarlyWithdrawAllowed - is early withdraw allowed or not
             * @param _penalty - ENUM(what type of penalty)
             * @param _tokenStaked - address of token which is staked
             * @param _stakeFeePercent - fee percent for staking
             * @param _rewardFeePercent - fee percent for reward distribution
             * @param _flatFeeAmount - flat fee amount
             * @param _isFlatFeeAllowed - is flat fee  allowed or not
             */
            function initialize(
                address _erc20,
                uint256 _rewardPerSecond,
                uint256 _startTime,
                uint256 _minTimeToStake,
                bool _isEarlyWithdrawAllowed,
                uint256 _penalty,
                address _tokenStaked,
                uint256 _stakeFeePercent,
                uint256 _rewardFeePercent,
                uint256 _flatFeeAmount,
                address payable _feeCollector,
                bool _isFlatFeeAllowed,
                address _farmImplementation
            )
                external
                initializer
            {
                // Upgrading ownership
                __Ownable_init();
                __ReentrancyGuard_init();
        
                // Requires for correct initialization
                require(_erc20 != address(0x0), "Wrong token address.");
                require(_rewardPerSecond > 0, "Rewards per second must be > 0.");
                require(
                    _startTime >= block.timestamp,
                    "Start time can not be in the past."
                );
                require(_stakeFeePercent < 100, "Stake fee must be < 100.");
                require(_rewardFeePercent < 100, "Reward fee must be < 100.");
                require(
                    _feeCollector != address(0x0),
                    "Wrong fee collector address."
                );
        
                // Initialization of contract
                erc20 = IERC20(_erc20);
                rewardPerSecond = _rewardPerSecond;
                startTime = _startTime;
                endTime = _startTime;
                minTimeToStake = _minTimeToStake;
                isEarlyWithdrawAllowed = _isEarlyWithdrawAllowed;
                stakeFeePercent = _stakeFeePercent;
                rewardFeePercent = _rewardFeePercent;
                flatFeeAmount = _flatFeeAmount;
                feeCollector = _feeCollector;
                isFlatFeeAllowed = _isFlatFeeAllowed;
                farmImplementation = _farmImplementation;
        
                _setEarlyWithdrawPenalty(_penalty);
                _addPool(IERC20(_tokenStaked));
            }
        
            // All Internal functions
        
            /**
             * @notice function is adding a new ERC20 token to the pool
             *
             * @param _tokenStaked - address of token staked
             */
            function _addPool(
                IERC20 _tokenStaked
            )
                internal
            {
                require(
                    address(_tokenStaked) != address(0x0),
                    "Must input valid address."
                );
                require(
                    address(tokenStaked) == address(0x0),
                    "Pool can be set only once."
                );
        
                uint256 _lastRewardTime = block.timestamp > startTime
                ? block.timestamp
                : startTime;
        
                tokenStaked = _tokenStaked;
                lastRewardTime = _lastRewardTime;
                accERC20PerShare = 0;
                totalDeposits = 0;
            }
        
            /**
             * @notice function is setting early withdrawal penalty, if applicable
             *
             * @param _penalty - number of penalty
             */
            function _setEarlyWithdrawPenalty(
                uint256 _penalty
            )
                internal
            {
                penalty = EarlyWithdrawPenalty(_penalty);
                emit EarlyWithdrawPenaltySet(penalty);
            }
        
            /**
             * @notice function is adding participant from farm
             *
             * @param user - address of user
             *
             * @return boolean - if adding is successful or not
             */
            function _addParticipant(
                address user
            )
                internal
                returns(bool)
            {
                if(stakeInfo[user].length > 0){
                    return false;
                }
        
                id[user] = noOfUsers;
                noOfUsers++;
                participants.push(user);
        
                return true;
            }
        
            /**
             * @notice function is removing participant from farm
             *
             * @param user - address of user
             * @param amount - how many is user withdrawing
             *
             * @return boolean - if removal is successful or not
             */
            function _removeParticipant(
                address user,
                uint256 amount
            )
                internal
                returns(bool)
            {
                uint256 totalAmount;
        
                if(noOfUsers == 1){
                    totalAmount = 0;
                    for(uint256 i = 0; i < stakeInfo[user].length; i++){
                        totalAmount += stakeInfo[user][i].amount;
                    }
        
                    if(amount == totalAmount){
                        delete id[user];
                        participants.pop();
                        noOfUsers--;
        
                        return true;
                    }
                }
                else{
                    totalAmount = 0;
                    for(uint256 i = 0; i < stakeInfo[user].length; i++){
                        totalAmount += stakeInfo[user][i].amount;
                    }
        
                    if(amount == totalAmount){
                        uint256 deletedUserId = id[user];
                        address lastUserInParticipantsArray = participants[participants.length - 1];
                        participants[deletedUserId] = lastUserInParticipantsArray;
                        id[lastUserInParticipantsArray] = deletedUserId;
        
                        delete id[user];
                        participants.pop();
                        noOfUsers--;
        
                        return true;
                    }
                }
        
                return false;
            }
        
            // All setter's functions
        
            /**
             * @notice function is setting new minimum time to stake value
             *
             * @param _minTimeToStake - min time to stake
             */
            function setMinTimeToStake(
                uint256 _minTimeToStake
            )
                external
                onlyOwner
            {
                minTimeToStake = _minTimeToStake;
                emit MinTimeToStakeSet(minTimeToStake);
            }
        
            /**
             * @notice function is setting new state of early withdraw
             *
             * @param _isEarlyWithdrawAllowed - is early withdraw allowed or not
             */
            function setIsEarlyWithdrawAllowed(
                bool _isEarlyWithdrawAllowed
            )
                external
                onlyOwner
            {
                isEarlyWithdrawAllowed = _isEarlyWithdrawAllowed;
                emit IsEarlyWithdrawAllowedSet(isEarlyWithdrawAllowed);
            }
        
            /**
             * @notice function is setting new stake fee percent value
             *
             * @param _stakeFeePercent - stake fee percent
             */
            function setStakeFeePercent(
                uint256 _stakeFeePercent
            )
                external
                onlyOwner
            {
                stakeFeePercent = _stakeFeePercent;
                emit StakeFeePercentSet(stakeFeePercent);
            }
        
            /**
             * @notice function is setting new reward fee percent value
             *
             * @param _rewardFeePercent - reward fee percent
             */
            function setRewardFeePercent(
                uint256 _rewardFeePercent
            )
                external
                onlyOwner
            {
                rewardFeePercent = _rewardFeePercent;
                emit RewardFeePercentSet(rewardFeePercent);
        
            }
        
            /**
             * @notice function is setting new flat fee amount
             *
             * @param _flatFeeAmount - flat fee amount
             */
            function setFlatFeeAmount(
                uint256 _flatFeeAmount
            )
                external
                onlyOwner
            {
                flatFeeAmount = _flatFeeAmount;
                emit FlatFeeAmountSet(flatFeeAmount);
            }
        
            /**
             * @notice function is setting flat fee allowed
             *
             * @param _isFlatFeeAllowed - is flat fee allowed or not
             */
            function setIsFlatFeeAllowed(
                bool _isFlatFeeAllowed
            )
                external
                onlyOwner
            {
                isFlatFeeAllowed = _isFlatFeeAllowed;
                emit IsFlatFeeAllowedSet(isFlatFeeAllowed);
            }
        
            /**
             * @notice function is setting feeCollector on new address
             *
             * @param _feeCollector - address of newFeeCollector
             */
            function setFeeCollector(
                address payable _feeCollector
            )
                external
                onlyOwner
            {
                feeCollector = _feeCollector;
                emit FeeCollectorSet(feeCollector);
            }
        
            // All view functions
        
            /**
             * @notice function is getting number to see deposited ERC20 token for a user.
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return deposited ERC20 token for a user
             */
            function deposited(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory  stake = stakeInfo[_user][stakeId];
                return stake.amount;
            }
        
            /**
             * @notice function is getting number to see pending ERC20s for a user.
             *
             * @dev pending reward = (user.amount * pool.accERC20PerShare) - user.rewardDebt
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return pending ERC20s for a user.
             */
            function pending(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory stake = stakeInfo[_user][stakeId];
        
                if (stake.amount == 0) {
                    return 0;
                }
        
                uint256 _accERC20PerShare = accERC20PerShare;
                uint256 tokenSupply = totalDeposits;
        
                if (block.timestamp > lastRewardTime && tokenSupply != 0) {
                    uint256 lastTime = block.timestamp < endTime
                    ? block.timestamp
                    : endTime;
                    uint256 timeToCompare = lastRewardTime < endTime
                    ? lastRewardTime
                    : endTime;
                    uint256 nrOfSeconds = lastTime.sub(timeToCompare);
                    uint256 erc20Reward = nrOfSeconds.mul(rewardPerSecond);
                    _accERC20PerShare = _accERC20PerShare.add(
                        erc20Reward.mul(1e18).div(tokenSupply)
                    );
                }
        
                return
                stake.amount.mul(_accERC20PerShare).div(1e18).sub(stake.rewardDebt);
            }
        
            /**
             * @notice function is getting number to see deposit timestamp for a user.
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return time when user deposited specific stake
             */
            function depositTimestamp(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory stake = stakeInfo[_user][stakeId];
                return stake.depositTime;
            }
        
            /**
             * @notice function is getting number to see withdraw timestamp for a user.
             *
             * @param _user - address of user
             * @param stakeId - id of user stake
             *
             * @return time when user withdraw specific stake
             */
            function withdrawTimestamp(
                address _user,
                uint256 stakeId
            )
                public
                view
                validateStakeByStakeId(_user, stakeId)
                returns (uint256)
            {
                StakeInfo memory stake = stakeInfo[_user][stakeId];
                return stake.withdrawTime;
            }
        
            /**
             * @notice function is getting number for total rewards the farm has yet to pay out.
             *
             * @return how many total reward the farm has yet to pay out.
             */
            function totalPending()
                external
                view
                returns (uint256)
            {
                if (block.timestamp <= startTime) {
                    return 0;
                }
        
                uint256 lastTime = block.timestamp < endTime
                ? block.timestamp
                : endTime;
                return rewardPerSecond.mul(lastTime - startTime).sub(paidOut);
            }
        
            /**
             * @notice function is getting number of stakes user has
             *
             * @param user - address of user
             *
             * @return how many times has user staked tokens
             */
            function getNumberOfUserStakes(
                address user
            )
                external
                view
                returns (uint256)
            {
                return stakeInfo[user].length;
            }
        
            /**
             * @notice function is getting user pending amounts, stakes and deposit time
             *
             * @param user - address of user
             *
             * @return array of deposits,pendingAmounts and depositTime
             */
            function getUserStakesAndPendingAmounts(
                address user
            )
                external
                view
                returns (
                    uint256[] memory,
                    uint256[] memory,
                    uint256[] memory
                )
            {
                uint256 numberOfStakes = stakeInfo[user].length;
        
                uint256[] memory deposits = new uint256[](numberOfStakes);
                uint256[] memory pendingAmounts = new uint256[](numberOfStakes);
                uint256[] memory depositTime = new uint256[](numberOfStakes);
        
                for (uint256 i = 0; i < numberOfStakes; i++) {
                    deposits[i] = deposited(user, i);
                    pendingAmounts[i] = pending(user, i);
                    depositTime[i] = depositTimestamp(user, i);
                }
        
                return (deposits, pendingAmounts, depositTime);
            }
        
            /**
             * @notice function is getting total rewards locked/unlocked
             *
             * @return totalRewardsUnlocked
             * @return totalRewardsLocked
             */
            function getTotalRewardsLockedUnlocked()
                external
                view
                returns (uint256, uint256)
            {
                uint256 totalRewardsLocked;
                uint256 totalRewardsUnlocked;
        
                if (block.timestamp <= startTime) {
                    totalRewardsUnlocked = 0;
                    totalRewardsLocked = totalRewards;
                } else {
                    uint256 lastTime = block.timestamp < endTime
                    ? block.timestamp
                    : endTime;
                    totalRewardsUnlocked = rewardPerSecond.mul(lastTime - startTime);
                    totalRewardsLocked = totalRewards - totalRewardsUnlocked;
                }
        
                return (totalRewardsUnlocked, totalRewardsLocked);
            }
        
            // Money managing functions
        
            /**
             * @notice function is funding the farm, increase the end time
             *
             * @param _amount - how many tokens is funded
             */
            function fund(
                uint256 _amount
            )
                external
            {
                uint256 balanceBefore = erc20.balanceOf(address(this));
                erc20.safeTransferFrom(address(msg.sender), address(this), _amount);
                uint256 balanceAfter = erc20.balanceOf(address(this));
        
                uint256 fundAmount;
                if(balanceAfter.sub(balanceBefore) <= _amount){
                    fundAmount = balanceAfter.sub(balanceBefore);
                }
                else{
                    fundAmount = _amount;
                }
        
                _fundInternal(fundAmount);
            }
        
            /**
             * @notice function is internally funding the farm by adding farmed rewards by user to the end
             *
             * @param _amount - how many tokens is funded
             */
            function _fundInternal(
                uint256 _amount
            )
                internal
            {
                require(
                    block.timestamp < endTime,
                    "fund: too late, the farm is closed"
                );
                require(_amount > 0, "Amount must be greater than 0.");
                // Compute new end time
                endTime += _amount.div(rewardPerSecond);
                // Increase farm total rewards
                totalRewards = totalRewards.add(_amount);
            }
        
            /**
             * @notice function is updating reward variables of the given pool to be up-to-date.
             */
            function updatePool()
                public
            {
                uint256 lastTime = block.timestamp < endTime
                ? block.timestamp
                : endTime;
        
                if (lastTime <= lastRewardTime) {
                    return;
                }
        
                uint256 tokenSupply = totalDeposits;
        
                if (tokenSupply == 0) {
                    lastRewardTime = lastTime;
                    return;
                }
        
                uint256 nrOfSeconds = lastTime.sub(lastRewardTime);
                uint256 erc20Reward = nrOfSeconds.mul(rewardPerSecond);
        
                accERC20PerShare = accERC20PerShare.add(
                    erc20Reward.mul(1e18).div(tokenSupply)
                );
                lastRewardTime = block.timestamp;
            }
        
            /**
             * @notice function is depositing ERC20 tokens to Farm for ERC20 allocation.
             *
             * @param _amount - how many tokens user is depositing
             */
            function deposit(
                uint256 _amount
            )
                external
                nonReentrant
                payable
            {
                require(
                    block.timestamp < endTime,
                    "Deposit: too late, the farm is closed"
                );
        
                StakeInfo memory stake;
                uint256 stakedAmount;
        
                // Update pool
                updatePool();
        
                uint256 beforeBalance = tokenStaked.balanceOf(address(this));
                tokenStaked.safeTransferFrom(
                    address(msg.sender),
                    address(this),
                    _amount
                );
                uint256 afterBalance = tokenStaked.balanceOf(address(this));
        
                if(afterBalance.sub(beforeBalance) <= _amount){
                    stakedAmount = afterBalance.sub(beforeBalance);
                }
                else{
                    stakedAmount = _amount;
                }
        
                if (isFlatFeeAllowed) {
                    // Collect flat fee
                    require(
                        msg.value >= flatFeeAmount,
                        "Payable amount is less than fee amount."
                    );
        
                    totalFeeCollectedETH = totalFeeCollectedETH.add(msg.value);
                } else if (stakeFeePercent > 0) {
                    // Handle this case only if flat fee is not allowed, and stakeFeePercent > 0
                    // Compute the fee
                    uint256 feeAmount = stakedAmount.mul(stakeFeePercent).div(100);
                    // Compute stake amount
                    stakedAmount = stakedAmount.sub(feeAmount);
                    totalFeeCollectedTokens = totalFeeCollectedTokens.add(feeAmount);
                }
        
                // Increase total deposits
                totalDeposits = totalDeposits.add(stakedAmount);
                // Update user accounting
                stake.amount = stakedAmount;
                stake.rewardDebt = stake.amount.mul(accERC20PerShare).div(1e18);
                stake.depositTime = block.timestamp;
                stake.addressOfUser = address(msg.sender);
                stake.withdrawTime = 0;
        
                _addParticipant(address(msg.sender));
        
                // Compute stake id
                uint256 stakeId = stakeInfo[msg.sender].length;
                // Push new stake to array of stakes for user
                stakeInfo[msg.sender].push(stake);
                // Emit deposit event
                emit Deposit(msg.sender, stakeId, stakedAmount);
            }
        
            // All withdraw functions
        
            /**
             * @notice function is withdrawing with caring about rewards
             *
             * @param _amount - how many tokens wants to be withdrawn
             * @param stakeId - Id of user stake
             */
            function withdraw(
                uint256 _amount,
                uint256 stakeId
            )
                external
                nonReentrant
                payable
                validateStakeByStakeId(msg.sender, stakeId)
            {
                bool minimalTimeStakeRespected;
                StakeInfo storage stake = stakeInfo[msg.sender][stakeId];
        
                require(
                    stake.amount >= _amount,
                    "withdraw: can't withdraw more than deposit"
                );
        
                updatePool();
                minimalTimeStakeRespected =
                stake.depositTime.add(minTimeToStake) <= block.timestamp;
                // if early withdraw is not allowed, user can't withdraw funds before
                if (!isEarlyWithdrawAllowed) {
                    // Check if user has respected minimal time to stake, require it.
                    require(
                        minimalTimeStakeRespected,
                        "User can not withdraw funds yet."
                    );
                }
        
                // Compute pending rewards amount of user rewards
                uint256 pendingAmount = stake
                .amount
                .mul(accERC20PerShare)
                .div(1e18)
                .sub(stake.rewardDebt);
        
                // Penalties in case user didn't stake enough time
                if (pendingAmount > 0) {
                    if (
                        penalty == EarlyWithdrawPenalty.BURN_REWARDS &&
                        !minimalTimeStakeRespected
                    ) {
                        // Burn to address (1)
                        totalTokensBurned = totalTokensBurned.add(pendingAmount);
                        _erc20Transfer(address(1), pendingAmount);
                    } else if (
                        penalty == EarlyWithdrawPenalty.REDISTRIBUTE_REWARDS &&
                        !minimalTimeStakeRespected
                    ) {
                        if (block.timestamp >= endTime) {
                            // Burn rewards because farm can not be funded anymore since it ended
                            _erc20Transfer(address(1), pendingAmount);
                            totalTokensBurned = totalTokensBurned.add(pendingAmount);
                        } else {
                            // Re-fund the farm
                            _fundInternal(pendingAmount);
                        }
                    } else {
                        // In case either there's no penalty
                        _erc20Transfer(msg.sender, pendingAmount);
                    }
                }
        
                _removeParticipant(address(msg.sender), _amount);
        
                stake.withdrawTime = block.timestamp;
                stake.amount = stake.amount.sub(_amount);
                stake.rewardDebt = stake.amount.mul(accERC20PerShare).div(1e18);
        
                tokenStaked.safeTransfer(address(msg.sender), _amount);
                totalDeposits = totalDeposits.sub(_amount);
        
                // Emit Withdraw event
                emit Withdraw(msg.sender, stakeId, _amount);
            }
        
            /**
             * @notice function is withdrawing without caring about rewards. EMERGENCY ONLY.
             *
             * @param stakeId - Id of user stake
             */
            function emergencyWithdraw(
                uint256 stakeId
            )
                external
                nonReentrant
                validateStakeByStakeId(msg.sender, stakeId)
            {
                StakeInfo storage stake = stakeInfo[msg.sender][stakeId];
        
                // if early withdraw is not allowed, user can't withdraw funds before
                if (!isEarlyWithdrawAllowed) {
                    bool minimalTimeStakeRespected = stake.depositTime.add(
                        minTimeToStake
                    ) <= block.timestamp;
                    // Check if user has respected minimal time to stake, require it.
                    require(
                        minimalTimeStakeRespected,
                        "User can not withdraw funds yet."
                    );
                }
        
                tokenStaked.safeTransfer(address(msg.sender), stake.amount);
                totalDeposits = totalDeposits.sub(stake.amount);
        
                _removeParticipant(address(msg.sender), stake.amount);
                stake.withdrawTime = block.timestamp;
        
                emit EmergencyWithdraw(msg.sender, stakeId, stake.amount);
        
                stake.amount = 0;
                stake.rewardDebt = 0;
            }
        
            /**
             * @notice function is withdrawing fee collected in ERC value
             */
            function withdrawCollectedFeesERC()
                external
                onlyOwner
            {
                erc20.transfer(feeCollector, totalFeeCollectedTokens);
                totalFeeCollectedTokens = 0;
            }
        
            /**
             * @notice function is withdrawing fee collected in ETH value
             */
            function withdrawCollectedFeesETH()
                external
                onlyOwner
            {
                (bool sent, ) = payable(feeCollector).call{value: totalFeeCollectedETH}("");
                require(sent, "Failed to end flat fee");
                totalFeeCollectedETH = 0;
            }
        
            /**
             * @notice function is withdrawing tokens if stuck
             *
             * @param _erc20 - address of token address
             * @param _amount - number of how many tokens
             * @param _beneficiary - address of user that collects tokens deposited by mistake
             */
            function withdrawTokensIfStuck(
                address _erc20,
                uint256 _amount,
                address _beneficiary
            )
                external
                onlyOwner
            {
                IERC20 token = IERC20(_erc20);
                require(tokenStaked != token, "User tokens can not be pulled");
                require(
                    _beneficiary != address(0x0),
                    "_beneficiary can not be 0x0 address"
                );
        
                token.safeTransfer(_beneficiary, _amount);
            }
        
            /**
             * @notice function is transferring ERC20,
             * and update the required ERC20 to payout all rewards
             *
             * @param _to - transfer on this address
             * @param _amount - number of how many tokens
             */
            function _erc20Transfer(
                address _to,
                uint256 _amount
            )
                internal
            {
                if (isFlatFeeAllowed) {
                    // Collect flat fee
                    require(
                        msg.value >= flatFeeAmount,
                        "Payable amount is less than fee amount."
                    );
                    // Increase amount of fees collected
                    totalFeeCollectedETH = totalFeeCollectedETH.add(msg.value);
                    // send reward
                    erc20.transfer(_to, _amount);
                    paidOut += _amount;
                } else if (stakeFeePercent > 0) {
                    // Collect reward fee
                    uint256 feeAmount = _amount.mul(rewardFeePercent).div(100);
                    uint256 rewardAmount = _amount.sub(feeAmount);
        
                    // Increase amount of fees collected
                    totalFeeCollectedTokens = totalFeeCollectedTokens.add(feeAmount);
        
                    // send reward
                    erc20.transfer(_to, rewardAmount);
                    paidOut += _amount;
                } else {
                    erc20.transfer(_to, _amount);
                    paidOut += _amount;
                }
            }
        }

        File 5 of 5: EPIKERC20
        // SPDX-License-Identifier: MIT
        // solhint-disable-next-line compiler-version
        pragma solidity ^0.8.0;
        import "./Initializable.sol";
        import "./v2/CappedEPIK.sol";
        import "./v2/MintableEPIK.sol";
        import "./v2/Ownable.sol";
        /**
         * @title EPIK LOGIC TOKEN
         * @dev This contract is a mock to test initializable functionality
         */
        contract EPIKERC20 is Initializable, ERC20Capped, ERC20Mintable, Ownable {
          function initialize(string memory name_, string memory symbol_, uint8 decimals_, uint256 cap_, address owner_) public initializer {
            _name = name_;
            _symbol = symbol_;
            _decimals = decimals_;
            _cap = cap_;
            _owner = owner_;
          }
          /**
            * @dev Function to mint tokens.
            *
            * NOTE: restricting access to owner only. See {ERC20Mintable-mint}.
            *
            * @param account The address that will receive the minted tokens
            * @param amount The amount of tokens to mint
            */
          function _mint(address account, uint256 amount) internal override onlyOwner {
              super._mint(account, amount);
          }
          /**
            * @dev Function to stop minting new tokens.
            *
            * NOTE: restricting access to owner only. See {ERC20Mintable-finishMinting}.
            */
          function _finishMinting() internal override onlyOwner {
              super._finishMinting();
          }
          /**
            * @dev See {ERC20-_beforeTokenTransfer}. See {ERC20Capped-_beforeTokenTransfer}.
            */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Capped) {
              super._beforeTokenTransfer(from, to, amount);
          }
        }
        // SPDX-License-Identifier: MIT
        // solhint-disable-next-line compiler-version
        pragma solidity ^0.8.0;
        /**
         * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
         * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
         * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
         * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
         *
         * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
         * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
         *
         * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
         * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
         */
        abstract contract Initializable {
            /**
             * @dev Indicates that the contract has been initialized.
             */
            bool private _initialized;
            /**
             * @dev Indicates that the contract is in the process of being initialized.
             */
            bool private _initializing;
            /**
             * @dev Modifier to protect an initializer function from being invoked twice.
             */
            modifier initializer() {
                require(_initializing || !_initialized, "Initializable: contract is already initialized");
                bool isTopLevelCall = !_initializing;
                if (isTopLevelCall) {
                    _initializing = true;
                    _initialized = true;
                }
                _;
                if (isTopLevelCall) {
                    _initializing = false;
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "./ERC20.sol";
        /**
         * @dev Extension of {ERC20} that adds a cap to the supply of tokens.
         */
        abstract contract ERC20Capped is ERC20 {
            using SafeMath for uint256;
            uint256 internal _cap;
            /**
             * @dev Returns the cap on the token's total supply.
             */
            function cap() public view returns (uint256) {
                return _cap;
            }
            /**
             * @dev See {ERC20-_beforeTokenTransfer}.
             *
             * Requirements:
             *
             * - minted tokens must not cause the total supply to go over the cap.
             */
            function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
                super._beforeTokenTransfer(from, to, amount);
                if (from == address(0)) { // When minting tokens
                    require(totalSupply().add(amount) <= _cap, "ERC20Capped: cap exceeded");
                }
            }
        }// SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "./ERC20.sol";
        /**
         * @title ERC20Mintable
         * @dev Implementation of the ERC20Mintable. Extension of {ERC20} that adds a minting behaviour.
         */
        abstract contract ERC20Mintable is ERC20 {
            // indicates if minting is finished
            bool internal _mintingFinished = false;
            /**
             * @dev Emitted during finish minting
             */
            event MintFinished();
            /**
             * @dev Tokens can be minted only before minting finished.
             */
            modifier canMint() {
                require(!_mintingFinished, "ERC20Mintable: minting is finished");
                _;
            }
            /**
             * @return if minting is finished or not.
             */
            function mintingFinished() public view returns (bool) {
                return _mintingFinished;
            }
            /**
             * @dev Function to mint tokens.
             *
             * WARNING: it allows everyone to mint new tokens. Access controls MUST be defined in derived contracts.
             *
             * @param account The address that will receive the minted tokens
             * @param amount The amount of tokens to mint
             */
            function mint(address account, uint256 amount) public canMint {
                _mint(account, amount);
            }
            /**
             * @dev Function to stop minting new tokens.
             *
             * WARNING: it allows everyone to finish minting. Access controls MUST be defined in derived contracts.
             */
            function finishMinting() public canMint {
                _finishMinting();
            }
            /**
             * @dev Function to stop minting new tokens.
             */
            function _finishMinting() internal virtual {
                _mintingFinished = true;
                emit MintFinished();
            }
        }// SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "@openzeppelin/contracts/utils/Context.sol";
        /**
         * @dev Contract module which provides a basic access control mechanism, where
         * there is an account (an owner) that can be granted exclusive access to
         * specific functions.
         *
         * By default, the owner account will be the one that deploys the contract. This
         * can later be changed with {transferOwnership}.
         *
         * This module is used through inheritance. It will make available the modifier
         * `onlyOwner`, which can be applied to your functions to restrict their use to
         * the owner.
         */
        abstract contract Ownable is Context {
            address internal _owner;
            event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
            /**
             * @dev Returns the address of the current owner.
             */
            function owner() public view virtual 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;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "@openzeppelin/contracts/utils/Context.sol";
        import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
        import "@openzeppelin/contracts/utils/math/SafeMath.sol";
        /**
         * @dev Implementation of the {IERC20} interface.
         *
         * This implementation is agnostic to the way tokens are created. This means
         * that a supply mechanism has to be added in a derived contract using {_mint}.
         * For a generic mechanism see {ERC20PresetMinterPauser}.
         *
         * TIP: For a detailed writeup see our guide
         * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
         * to implement supply mechanisms].
         *
         * We have followed general OpenZeppelin guidelines: functions revert instead
         * of returning `false` on failure. This behavior is nonetheless conventional
         * and does not conflict with the expectations of ERC20 applications.
         *
         * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
         * This allows applications to reconstruct the allowance for all accounts just
         * by listening to said events. Other implementations of the EIP may not emit
         * these events, as it isn't required by the specification.
         *
         * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
         * functions have been added to mitigate the well-known issues around setting
         * allowances. See {IERC20-approve}.
         */
        contract ERC20 is Context, IERC20 {
            using SafeMath for uint256;
            mapping (address => uint256) private _balances;
            mapping (address => mapping (address => uint256)) private _allowances;
            uint256 private _totalSupply;
            string internal _name;
            string internal _symbol;
            uint8 internal _decimals;
            /**
             * @dev Returns the name of the token.
             */
            function name() public view returns (string memory) {
                return _name;
            }
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view returns (string memory) {
                return _symbol;
            }
            /**
             * @dev Returns the number of decimals used to get its user representation.
             * For example, if `decimals` equals `2`, a balance of `505` tokens should
             * be displayed to a user as `5,05` (`505 / 10 ** 2`).
             *
             * Tokens usually opt for a value of 18, imitating the relationship between
             * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
             * called.
             *
             * NOTE: This information is only used for _display_ purposes: it in
             * no way affects any of the arithmetic of the contract, including
             * {IERC20-balanceOf} and {IERC20-transfer}.
             */
            function decimals() public view returns (uint8) {
                return _decimals;
            }
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view override returns (uint256) {
                return _totalSupply;
            }
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view override returns (uint256) {
                return _balances[account];
            }
            /**
             * @dev See {IERC20-transfer}.
             *
             * Requirements:
             *
             * - `recipient` cannot be the zero address.
             * - the caller must have a balance of at least `amount`.
             */
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                _transfer(_msgSender(), recipient, amount);
                return true;
            }
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender) public view virtual override returns (uint256) {
                return _allowances[owner][spender];
            }
            /**
             * @dev See {IERC20-approve}.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 amount) public virtual override returns (bool) {
                _approve(_msgSender(), spender, amount);
                return true;
            }
            /**
             * @dev See {IERC20-transferFrom}.
             *
             * Emits an {Approval} event indicating the updated allowance. This is not
             * required by the EIP. See the note at the beginning of {ERC20}.
             *
             * Requirements:
             *
             * - `sender` and `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             * - the caller must have allowance for ``sender``'s tokens of at least
             * `amount`.
             */
            function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                _transfer(sender, recipient, amount);
                _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                return true;
            }
            /**
             * @dev Atomically increases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                return true;
            }
            /**
             * @dev Atomically decreases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             * - `spender` must have allowance for the caller of at least
             * `subtractedValue`.
             */
            function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                return true;
            }
            /**
             * @dev Moves tokens `amount` from `sender` to `recipient`.
             *
             * This is internal function is equivalent to {transfer}, and can be used to
             * e.g. implement automatic token fees, slashing mechanisms, etc.
             *
             * Emits a {Transfer} event.
             *
             * Requirements:
             *
             * - `sender` cannot be the zero address.
             * - `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             */
            function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                require(sender != address(0), "ERC20: transfer from the zero address");
                require(recipient != address(0), "ERC20: transfer to the zero address");
                _beforeTokenTransfer(sender, recipient, amount);
                _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                _balances[recipient] = _balances[recipient].add(amount);
                emit Transfer(sender, recipient, amount);
            }
            /** @dev Creates `amount` tokens and assigns them to `account`, increasing
             * the total supply.
             *
             * Emits a {Transfer} event with `from` set to the zero address.
             *
             * Requirements:
             *
             * - `to` cannot be the zero address.
             */
            function _mint(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: mint to the zero address");
                _beforeTokenTransfer(address(0), account, amount);
                _totalSupply = _totalSupply.add(amount);
                _balances[account] = _balances[account].add(amount);
                emit Transfer(address(0), account, amount);
            }
            /**
             * @dev Destroys `amount` tokens from `account`, reducing the
             * total supply.
             *
             * Emits a {Transfer} event with `to` set to the zero address.
             *
             * Requirements:
             *
             * - `account` cannot be the zero address.
             * - `account` must have at least `amount` tokens.
             */
            function _burn(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: burn from the zero address");
                _beforeTokenTransfer(account, address(0), amount);
                _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                _totalSupply = _totalSupply.sub(amount);
                emit Transfer(account, address(0), amount);
            }
            /**
             * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
             *
             * This internal function is equivalent to `approve`, and can be used to
             * e.g. set automatic allowances for certain subsystems, etc.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `owner` cannot be the zero address.
             * - `spender` cannot be the zero address.
             */
            function _approve(address owner, address spender, uint256 amount) internal virtual {
                require(owner != address(0), "ERC20: approve from the zero address");
                require(spender != address(0), "ERC20: approve to the zero address");
                _allowances[owner][spender] = amount;
                emit Approval(owner, spender, amount);
            }
            /**
             * @dev Hook that is called before any transfer of tokens. This includes
             * minting and burning.
             *
             * Calling conditions:
             *
             * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
             * will be to 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 { }
        }// SPDX-License-Identifier: MIT
        pragma solidity ^0.8.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 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) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes calldata) {
                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                return msg.data;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        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);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        // 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 no longer needed starting with Solidity 0.8. 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. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * 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;
                }
            }
        }