/**

Enigma Platform - $ENG

Build safe, build with Enigma. Combining crypto and NFTs in a utility-enabled ecosystem.

https://t.me/EnigmaPlatform
enigma-token.com
assuredefi.com/projects/enigma
twitter.com/EnigmaTokenETH


*/


// SPDX-License-Identifier: MIT
pragma solidity =0.8.10 >=0.8.10 >=0.8.0 <0.9.0;
pragma experimental ABIEncoderV2;

////// lib/openzeppelin-contracts/contracts/utils/Context.sol
// OpenZeppelin Contracts v4.4.0 (utils/Context.sol)

/* 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) {
 return msg.data;
 }
}

////// lib/openzeppelin-contracts/contracts/access/Ownable.sol
// OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)

/* pragma solidity ^0.8.0; */

/* import "../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 private _owner;

 event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

 /**
 * @dev Initializes the contract setting the deployer as the initial owner.
 */
 constructor() {
 _transferOwnership(_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 {
 _transferOwnership(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");
 _transferOwnership(newOwner);
 }

 /**
 * @dev Transfers ownership of the contract to a new account (`newOwner`).
 * Internal function without access restriction.
 */
 function _transferOwnership(address newOwner) internal virtual {
 address oldOwner = _owner;
 _owner = newOwner;
 emit OwnershipTransferred(oldOwner, newOwner);
 }
}

////// lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
// OpenZeppelin Contracts v4.4.0 (token/ERC20/IERC20.sol)

/* 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);
}

////// lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol
// OpenZeppelin Contracts v4.4.0 (token/ERC20/extensions/IERC20Metadata.sol)

/* pragma solidity ^0.8.0; */

/* import "../IERC20.sol"; */

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
 /**
 * @dev Returns the name of the token.
 */
 function name() external view returns (string memory);

 /**
 * @dev Returns the symbol of the token.
 */
 function symbol() external view returns (string memory);

 /**
 * @dev Returns the decimals places of the token.
 */
 function decimals() external view returns (uint8);
}

////// lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol
// OpenZeppelin Contracts v4.4.0 (token/ERC20/ERC20.sol)

/* pragma solidity ^0.8.0; */

/* import "./IERC20.sol"; */
/* import "./extensions/IERC20Metadata.sol"; */
/* import "../../utils/Context.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 Contracts guidelines: functions revert
 * instead 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, IERC20Metadata {
 mapping(address => uint256) private _balances;

 mapping(address => mapping(address => uint256)) private _allowances;

 uint256 private _totalSupply;

 string private _name;
 string private _symbol;

 /**
 * @dev Sets the values for {name} and {symbol}.
 *
 * The default value of {decimals} is 18. To select a different value for
 * {decimals} you should overload it.
 *
 * All two of these values are immutable: they can only be set once during
 * construction.
 */
 constructor(string memory name_, string memory symbol_) {
 _name = name_;
 _symbol = symbol_;
 }

 /**
 * @dev Returns the name of the token.
 */
 function name() public view virtual override returns (string memory) {
 return _name;
 }

 /**
 * @dev Returns the symbol of the token, usually a shorter version of the
 * name.
 */
 function symbol() public view virtual override 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 this function is
 * overridden;
 *
 * 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 virtual override returns (uint8) {
 return 18;
 }

 /**
 * @dev See {IERC20-totalSupply}.
 */
 function totalSupply() public view virtual override returns (uint256) {
 return _totalSupply;
 }

 /**
 * @dev See {IERC20-balanceOf}.
 */
 function balanceOf(address account) public view virtual 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);

 uint256 currentAllowance = _allowances[sender][_msgSender()];
 require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
 unchecked {
 _approve(sender, _msgSender(), currentAllowance - amount);
 }

 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] + 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) {
 uint256 currentAllowance = _allowances[_msgSender()][spender];
 require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
 unchecked {
 _approve(_msgSender(), spender, currentAllowance - subtractedValue);
 }

 return true;
 }

 /**
 * @dev Moves `amount` of tokens from `sender` to `recipient`.
 *
 * This 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);

 uint256 senderBalance = _balances[sender];
 require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
 unchecked {
 _balances[sender] = senderBalance - amount;
 }
 _balances[recipient] += amount;

 emit Transfer(sender, recipient, amount);

 _afterTokenTransfer(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:
 *
 * - `account` 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 += amount;
 _balances[account] += amount;
 emit Transfer(address(0), account, amount);

 _afterTokenTransfer(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);

 uint256 accountBalance = _balances[account];
 require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
 unchecked {
 _balances[account] = accountBalance - amount;
 }
 _totalSupply -= amount;

 emit Transfer(account, address(0), amount);

 _afterTokenTransfer(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 transferred to `to`.
 * - when `from` is zero, `amount` tokens will be minted for `to`.
 * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
 * - `from` and `to` are never both zero.
 *
 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
 */
 function _beforeTokenTransfer(
 address from,
 address to,
 uint256 amount
 ) internal virtual {}

 /**
 * @dev Hook that is called after any transfer of tokens. This includes
 * minting and burning.
 *
 * Calling conditions:
 *
 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
 * has been transferred to `to`.
 * - when `from` is zero, `amount` tokens have been minted for `to`.
 * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
 * - `from` and `to` are never both zero.
 *
 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
 */
 function _afterTokenTransfer(
 address from,
 address to,
 uint256 amount
 ) internal virtual {}
}

////// lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol
// OpenZeppelin Contracts v4.4.0 (utils/math/SafeMath.sol)

/* 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 generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
 /**
 * @dev Returns the addition of two unsigned integers, with an overflow flag.
 *
 * _Available since v3.4._
 */
 function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
 unchecked {
 uint256 c = a + b;
 if (c < a) return (false, 0);
 return (true, c);
 }
 }

 /**
 * @dev Returns the substraction of two unsigned integers, with an overflow flag.
 *
 * _Available since v3.4._
 */
 function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
 unchecked {
 if (b > a) return (false, 0);
 return (true, a - b);
 }
 }

 /**
 * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
 *
 * _Available since v3.4._
 */
 function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
 unchecked {
 // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
 // benefit is lost if 'b' is also tested.
 // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
 if (a == 0) return (true, 0);
 uint256 c = a * b;
 if (c / a != b) return (false, 0);
 return (true, c);
 }
 }

 /**
 * @dev Returns the division of two unsigned integers, with a division by zero flag.
 *
 * _Available since v3.4._
 */
 function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
 unchecked {
 if (b == 0) return (false, 0);
 return (true, a / b);
 }
 }

 /**
 * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
 *
 * _Available since v3.4._
 */
 function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
 unchecked {
 if (b == 0) return (false, 0);
 return (true, a % b);
 }
 }

 /**
 * @dev Returns the addition of two unsigned integers, reverting on
 * overflow.
 *
 * Counterpart to Solidity's `+` operator.
 *
 * Requirements:
 *
 * - Addition cannot overflow.
 */
 function add(uint256 a, uint256 b) internal pure returns (uint256) {
 return a + b;
 }

 /**
 * @dev Returns the subtraction of two unsigned integers, reverting on
 * overflow (when the result is negative).
 *
 * Counterpart to Solidity's `-` operator.
 *
 * Requirements:
 *
 * - Subtraction cannot overflow.
 */
 function sub(uint256 a, uint256 b) internal pure returns (uint256) {
 return a - b;
 }

 /**
 * @dev Returns the multiplication of two unsigned integers, reverting on
 * overflow.
 *
 * Counterpart to Solidity's `*` operator.
 *
 * Requirements:
 *
 * - Multiplication cannot overflow.
 */
 function mul(uint256 a, uint256 b) internal pure returns (uint256) {
 return a * b;
 }

 /**
 * @dev Returns the integer division of two unsigned integers, reverting on
 * division by zero. The result is rounded towards zero.
 *
 * Counterpart to Solidity's `/` operator.
 *
 * Requirements:
 *
 * - The divisor cannot be zero.
 */
 function div(uint256 a, uint256 b) internal pure returns (uint256) {
 return a / b;
 }

 /**
 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
 * reverting when dividing by zero.
 *
 * Counterpart to Solidity's `%` operator. This function uses a `revert`
 * opcode (which leaves remaining gas untouched) while Solidity uses an
 * invalid opcode to revert (consuming all remaining gas).
 *
 * Requirements:
 *
 * - The divisor cannot be zero.
 */
 function mod(uint256 a, uint256 b) internal pure returns (uint256) {
 return a % b;
 }

 /**
 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
 * overflow (when the result is negative).
 *
 * CAUTION: This function is deprecated because it requires allocating memory for the error
 * message unnecessarily. For custom revert reasons use {trySub}.
 *
 * Counterpart to Solidity's `-` operator.
 *
 * Requirements:
 *
 * - Subtraction cannot overflow.
 */
 function sub(
 uint256 a,
 uint256 b,
 string memory errorMessage
 ) internal pure returns (uint256) {
 unchecked {
 require(b <= a, errorMessage);
 return a - b;
 }
 }

 /**
 * @dev Returns the integer division of two unsigned integers, reverting with custom message on
 * division by zero. The result is rounded towards zero.
 *
 * Counterpart to Solidity's `/` operator. Note: this function uses a
 * `revert` opcode (which leaves remaining gas untouched) while Solidity
 * uses an invalid opcode to revert (consuming all remaining gas).
 *
 * Requirements:
 *
 * - The divisor cannot be zero.
 */
 function div(
 uint256 a,
 uint256 b,
 string memory errorMessage
 ) internal pure returns (uint256) {
 unchecked {
 require(b > 0, errorMessage);
 return a / b;
 }
 }

 /**
 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
 * reverting with custom message when dividing by zero.
 *
 * CAUTION: This function is deprecated because it requires allocating memory for the error
 * message unnecessarily. For custom revert reasons use {tryMod}.
 *
 * Counterpart to Solidity's `%` operator. This function uses a `revert`
 * opcode (which leaves remaining gas untouched) while Solidity uses an
 * invalid opcode to revert (consuming all remaining gas).
 *
 * Requirements:
 *
 * - The divisor cannot be zero.
 */
 function mod(
 uint256 a,
 uint256 b,
 string memory errorMessage
 ) internal pure returns (uint256) {
 unchecked {
 require(b > 0, errorMessage);
 return a % b;
 }
 }
}

////// src/IUniswapV2Factory.sol
/* pragma solidity 0.8.10; */
/* pragma experimental ABIEncoderV2; */

interface IUniswapV2Factory {
 event PairCreated(
 address indexed token0,
 address indexed token1,
 address pair,
 uint256
 );

 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(uint256) external view returns (address pair);

 function allPairsLength() external view returns (uint256);

 function createPair(address tokenA, address tokenB)
 external
 returns (address pair);

 function setFeeTo(address) external;

 function setFeeToSetter(address) external;
}

////// src/IUniswapV2Pair.sol
/* pragma solidity 0.8.10; */
/* pragma experimental ABIEncoderV2; */

interface IUniswapV2Pair {
 event Approval(
 address indexed owner,
 address indexed spender,
 uint256 value
 );
 event Transfer(address indexed from, address indexed to, uint256 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 (uint256);

 function balanceOf(address owner) external view returns (uint256);

 function allowance(address owner, address spender)
 external
 view
 returns (uint256);

 function approve(address spender, uint256 value) external returns (bool);

 function transfer(address to, uint256 value) external returns (bool);

 function transferFrom(
 address from,
 address to,
 uint256 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 (uint256);

 function permit(
 address owner,
 address spender,
 uint256 value,
 uint256 deadline,
 uint8 v,
 bytes32 r,
 bytes32 s
 ) external;

 event Mint(address indexed sender, uint256 amount0, uint256 amount1);
 event Burn(
 address indexed sender,
 uint256 amount0,
 uint256 amount1,
 address indexed to
 );
 event Swap(
 address indexed sender,
 uint256 amount0In,
 uint256 amount1In,
 uint256 amount0Out,
 uint256 amount1Out,
 address indexed to
 );
 event Sync(uint112 reserve0, uint112 reserve1);

 function MINIMUM_LIQUIDITY() external pure returns (uint256);

 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 (uint256);

 function price1CumulativeLast() external view returns (uint256);

 function kLast() external view returns (uint256);

 function mint(address to) external returns (uint256 liquidity);

 function burn(address to)
 external
 returns (uint256 amount0, uint256 amount1);

 function swap(
 uint256 amount0Out,
 uint256 amount1Out,
 address to,
 bytes calldata data
 ) external;

 function skim(address to) external;

 function sync() external;

 function initialize(address, address) external;
}

////// src/IUniswapV2Router02.sol
/* pragma solidity 0.8.10; */
/* pragma experimental ABIEncoderV2; */

interface IUniswapV2Router02 {
 function factory() external pure returns (address);

 function WETH() external pure returns (address);

 function addLiquidity(
 address tokenA,
 address tokenB,
 uint256 amountADesired,
 uint256 amountBDesired,
 uint256 amountAMin,
 uint256 amountBMin,
 address to,
 uint256 deadline
 )
 external
 returns (
 uint256 amountA,
 uint256 amountB,
 uint256 liquidity
 );

 function addLiquidityETH(
 address token,
 uint256 amountTokenDesired,
 uint256 amountTokenMin,
 uint256 amountETHMin,
 address to,
 uint256 deadline
 )
 external
 payable
 returns (
 uint256 amountToken,
 uint256 amountETH,
 uint256 liquidity
 );

 function swapExactTokensForTokensSupportingFeeOnTransferTokens(
 uint256 amountIn,
 uint256 amountOutMin,
 address[] calldata path,
 address to,
 uint256 deadline
 ) external;

 function swapExactETHForTokensSupportingFeeOnTransferTokens(
 uint256 amountOutMin,
 address[] calldata path,
 address to,
 uint256 deadline
 ) external payable;

 function swapExactTokensForETHSupportingFeeOnTransferTokens(
 uint256 amountIn,
 uint256 amountOutMin,
 address[] calldata path,
 address to,
 uint256 deadline
 ) external;
}

/* pragma solidity >=0.8.10; */

/* import {IUniswapV2Router02} from "./IUniswapV2Router02.sol"; */
/* import {IUniswapV2Factory} from "./IUniswapV2Factory.sol"; */
/* import {IUniswapV2Pair} from "./IUniswapV2Pair.sol"; */
/* import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */
/* import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; */
/* import {Ownable} from "lib/openzeppelin-contracts/contracts/access/Ownable.sol"; */
/* import {SafeMath} from "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol"; */

contract ENIGMAPLATFORM is ERC20, Ownable {
 using SafeMath for uint256;

 IUniswapV2Router02 public immutable uniswapV2Router;
 address public immutable uniswapV2Pair;
 address public constant deadAddress = address(0xdead);

 bool private swapping;

 address public marketingWallet;
 address public devWallet;

 uint256 public maxTransactionAmount;
 uint256 public swapTokensAtAmount;
 uint256 public maxWallet;

 uint256 public percentForLPBurn = 25; // 25 = .25%
 bool public lpBurnEnabled = true;
 uint256 public lpBurnFrequency = 3600 seconds;
 uint256 public lastLpBurnTime;

 uint256 public manualBurnFrequency = 30 minutes;
 uint256 public lastManualLpBurnTime;

 bool public limitsInEffect = true;
 bool public tradingActive = false;
 bool public swapEnabled = false;

 // Anti-bot and anti-whale mappings and variables
 mapping(address => uint256) private _holderLastTransferTimestamp; // to hold last Transfers temporarily during launch
 bool public transferDelayEnabled = true;

 uint256 public buyTotalFees;
 uint256 public buyMarketingFee;
 uint256 public buyLiquidityFee;
 uint256 public buyDevFee;

 uint256 public sellTotalFees;
 uint256 public sellMarketingFee;
 uint256 public sellLiquidityFee;
 uint256 public sellDevFee;

 uint256 public tokensForMarketing;
 uint256 public tokensForLiquidity;
 uint256 public tokensForDev;

 /******************/

 // exlcude from fees and max transaction amount
 mapping(address => bool) private _isExcludedFromFees;
 mapping(address => bool) public _isExcludedMaxTransactionAmount;

 // store addresses that a automatic market maker pairs. Any transfer *to* these addresses
 // could be subject to a maximum transfer amount
 mapping(address => bool) public automatedMarketMakerPairs;

 event UpdateUniswapV2Router(
 address indexed newAddress,
 address indexed oldAddress
 );

 event ExcludeFromFees(address indexed account, bool isExcluded);

 event SetAutomatedMarketMakerPair(address indexed pair, bool indexed value);

 event marketingWalletUpdated(
 address indexed newWallet,
 address indexed oldWallet
 );

 event devWalletUpdated(
 address indexed newWallet,
 address indexed oldWallet
 );

 event SwapAndLiquify(
 uint256 tokensSwapped,
 uint256 ethReceived,
 uint256 tokensIntoLiquidity
 );

 event AutoNukeLP();

 event ManualNukeLP();

 constructor() ERC20("Enigma Platform", "ENG") {
 IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(
 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
 );

 excludeFromMaxTransaction(address(_uniswapV2Router), true);
 uniswapV2Router = _uniswapV2Router;

 uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
 .createPair(address(this), _uniswapV2Router.WETH());
 excludeFromMaxTransaction(address(uniswapV2Pair), true);
 _setAutomatedMarketMakerPair(address(uniswapV2Pair), true);

 uint256 _buyMarketingFee = 4;
 uint256 _buyLiquidityFee = 0;
 uint256 _buyDevFee = 0;

 uint256 _sellMarketingFee = 35;
 uint256 _sellLiquidityFee = 0;
 uint256 _sellDevFee = 0;

 uint256 totalSupply = 1_000_000_000 * 1e18;

 maxTransactionAmount = 20_000_000 * 1e18; // 2% from total supply maxTransactionAmountTxn
 maxWallet = 20_000_000 * 1e18; // 2% from total supply maxWallet
 swapTokensAtAmount = (totalSupply * 5) / 10000; // 0.05% swap wallet

 buyMarketingFee = _buyMarketingFee;
 buyLiquidityFee = _buyLiquidityFee;
 buyDevFee = _buyDevFee;
 buyTotalFees = buyMarketingFee + buyLiquidityFee + buyDevFee;

 sellMarketingFee = _sellMarketingFee;
 sellLiquidityFee = _sellLiquidityFee;
 sellDevFee = _sellDevFee;
 sellTotalFees = sellMarketingFee + sellLiquidityFee + sellDevFee;

 marketingWallet = address(0x952d11988e5323Ce1a491d356E67084746cF2B7D); // set as marketing wallet
 devWallet = address(0x952d11988e5323Ce1a491d356E67084746cF2B7D); // set as dev wallet

 // exclude from paying fees or having max transaction amount
 excludeFromFees(owner(), true);
 excludeFromFees(address(this), true);
 excludeFromFees(address(0xdead), true);

 excludeFromMaxTransaction(owner(), true);
 excludeFromMaxTransaction(address(this), true);
 excludeFromMaxTransaction(address(0xdead), true);

 /*
 _mint is an internal function in ERC20.sol that is only called here,
 and CANNOT be called ever again
 */
 _mint(msg.sender, totalSupply);
 }

 receive() external payable {}

 // once enabled, can never be turned off
 function enableTrading() external onlyOwner {
 tradingActive = true;
 swapEnabled = true;
 lastLpBurnTime = block.timestamp;
 }

 // remove limits after token is stable
 function removeLimits() external onlyOwner returns (bool) {
 limitsInEffect = false;
 return true;
 }

 // disable Transfer delay - cannot be reenabled
 function disableTransferDelay() external onlyOwner returns (bool) {
 transferDelayEnabled = false;
 return true;
 }

 // change the minimum amount of tokens to sell from fees
 function updateSwapTokensAtAmount(uint256 newAmount)
 external
 onlyOwner
 returns (bool)
 {
 require(
 newAmount >= (totalSupply() * 1) / 100000,
 "Swap amount cannot be lower than 0.001% total supply."
 );
 require(
 newAmount <= (totalSupply() * 5) / 1000,
 "Swap amount cannot be higher than 0.5% total supply."
 );
 swapTokensAtAmount = newAmount;
 return true;
 }

 function updateMaxTxnAmount(uint256 newNum) external onlyOwner {
 require(
 newNum >= ((totalSupply() * 1) / 1000) / 1e18,
 "Cannot set maxTransactionAmount lower than 0.1%"
 );
 maxTransactionAmount = newNum * (10**18);
 }

 function updateMaxWalletAmount(uint256 newNum) external onlyOwner {
 require(
 newNum >= ((totalSupply() * 5) / 1000) / 1e18,
 "Cannot set maxWallet lower than 0.5%"
 );
 maxWallet = newNum * (10**18);
 }

 function excludeFromMaxTransaction(address updAds, bool isEx)
 public
 onlyOwner
 {
 _isExcludedMaxTransactionAmount[updAds] = isEx;
 }

 // only use to disable contract sales if absolutely necessary (emergency use only)
 function updateSwapEnabled(bool enabled) external onlyOwner {
 swapEnabled = enabled;
 }

 function updateBuyFees(
 uint256 _marketingFee,
 uint256 _liquidityFee,
 uint256 _devFee
 ) external onlyOwner {
 buyMarketingFee = _marketingFee;
 buyLiquidityFee = _liquidityFee;
 buyDevFee = _devFee;
 buyTotalFees = buyMarketingFee + buyLiquidityFee + buyDevFee;
 require(buyTotalFees <= 11, "Must keep fees at 11% or less");
 }

 function updateSellFees(
 uint256 _marketingFee,
 uint256 _liquidityFee,
 uint256 _devFee
 ) external onlyOwner {
 sellMarketingFee = _marketingFee;
 sellLiquidityFee = _liquidityFee;
 sellDevFee = _devFee;
 sellTotalFees = sellMarketingFee + sellLiquidityFee + sellDevFee;
 require(sellTotalFees <= 49, "Must keep fees at 49% or less");
 }

 function excludeFromFees(address account, bool excluded) public onlyOwner {
 _isExcludedFromFees[account] = excluded;
 emit ExcludeFromFees(account, excluded);
 }

 function setAutomatedMarketMakerPair(address pair, bool value)
 public
 onlyOwner
 {
 require(
 pair != uniswapV2Pair,
 "The pair cannot be removed from automatedMarketMakerPairs"
 );

 _setAutomatedMarketMakerPair(pair, value);
 }

 function _setAutomatedMarketMakerPair(address pair, bool value) private {
 automatedMarketMakerPairs[pair] = value;

 emit SetAutomatedMarketMakerPair(pair, value);
 }

 function updateMarketingWallet(address newMarketingWallet)
 external
 onlyOwner
 {
 emit marketingWalletUpdated(newMarketingWallet, marketingWallet);
 marketingWallet = newMarketingWallet;
 }

 function updateDevWallet(address newWallet) external onlyOwner {
 emit devWalletUpdated(newWallet, devWallet);
 devWallet = newWallet;
 }

 function isExcludedFromFees(address account) public view returns (bool) {
 return _isExcludedFromFees[account];
 }

 event BoughtEarly(address indexed sniper);

 function _transfer(
 address from,
 address to,
 uint256 amount
 ) internal override {
 require(from != address(0), "ERC20: transfer from the zero address");
 require(to != address(0), "ERC20: transfer to the zero address");

 if (amount == 0) {
 super._transfer(from, to, 0);
 return;
 }

 if (limitsInEffect) {
 if (
 from != owner() &&
 to != owner() &&
 to != address(0) &&
 to != address(0xdead) &&
 !swapping
 ) {
 if (!tradingActive) {
 require(
 _isExcludedFromFees[from] || _isExcludedFromFees[to],
 "Trading is not active."
 );
 }

 // at launch if the transfer delay is enabled, ensure the block timestamps for purchasers is set -- during launch.
 if (transferDelayEnabled) {
 if (
 to != owner() &&
 to != address(uniswapV2Router) &&
 to != address(uniswapV2Pair)
 ) {
 require(
 _holderLastTransferTimestamp[tx.origin] <
 block.number,
 "_transfer:: Transfer Delay enabled. Only one purchase per block allowed."
 );
 _holderLastTransferTimestamp[tx.origin] = block.number;
 }
 }

 //when buy
 if (
 automatedMarketMakerPairs[from] &&
 !_isExcludedMaxTransactionAmount[to]
 ) {
 require(
 amount <= maxTransactionAmount,
 "Buy transfer amount exceeds the maxTransactionAmount."
 );
 require(
 amount + balanceOf(to) <= maxWallet,
 "Max wallet exceeded"
 );
 }
 //when sell
 else if (
 automatedMarketMakerPairs[to] &&
 !_isExcludedMaxTransactionAmount[from]
 ) {
 require(
 amount <= maxTransactionAmount,
 "Sell transfer amount exceeds the maxTransactionAmount."
 );
 } else if (!_isExcludedMaxTransactionAmount[to]) {
 require(
 amount + balanceOf(to) <= maxWallet,
 "Max wallet exceeded"
 );
 }
 }
 }

 uint256 contractTokenBalance = balanceOf(address(this));

 bool canSwap = contractTokenBalance >= swapTokensAtAmount;

 if (
 canSwap &&
 swapEnabled &&
 !swapping &&
 !automatedMarketMakerPairs[from] &&
 !_isExcludedFromFees[from] &&
 !_isExcludedFromFees[to]
 ) {
 swapping = true;

 swapBack();

 swapping = false;
 }

 if (
 !swapping &&
 automatedMarketMakerPairs[to] &&
 lpBurnEnabled &&
 block.timestamp >= lastLpBurnTime + lpBurnFrequency &&
 !_isExcludedFromFees[from]
 ) {
 autoBurnLiquidityPairTokens();
 }

 bool takeFee = !swapping;

 // if any account belongs to _isExcludedFromFee account then remove the fee
 if (_isExcludedFromFees[from] || _isExcludedFromFees[to]) {
 takeFee = false;
 }

 uint256 fees = 0;
 // only take fees on buys/sells, do not take on wallet transfers
 if (takeFee) {
 // on sell
 if (automatedMarketMakerPairs[to] && sellTotalFees > 0) {
 fees = amount.mul(sellTotalFees).div(100);
 tokensForLiquidity += (fees * sellLiquidityFee) / sellTotalFees;
 tokensForDev += (fees * sellDevFee) / sellTotalFees;
 tokensForMarketing += (fees * sellMarketingFee) / sellTotalFees;
 }
 // on buy
 else if (automatedMarketMakerPairs[from] && buyTotalFees > 0) {
 fees = amount.mul(buyTotalFees).div(100);
 tokensForLiquidity += (fees * buyLiquidityFee) / buyTotalFees;
 tokensForDev += (fees * buyDevFee) / buyTotalFees;
 tokensForMarketing += (fees * buyMarketingFee) / buyTotalFees;
 }

 if (fees > 0) {
 super._transfer(from, address(this), fees);
 }

 amount -= fees;
 }

 super._transfer(from, to, amount);
 }

 function swapTokensForEth(uint256 tokenAmount) private {
 // generate the uniswap pair path of token -> weth
 address[] memory path = new address[](2);
 path[0] = address(this);
 path[1] = uniswapV2Router.WETH();

 _approve(address(this), address(uniswapV2Router), tokenAmount);

 // make the swap
 uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
 tokenAmount,
 0, // accept any amount of ETH
 path,
 address(this),
 block.timestamp
 );
 }

 function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
 // approve token transfer to cover all possible scenarios
 _approve(address(this), address(uniswapV2Router), tokenAmount);

 // add the liquidity
 uniswapV2Router.addLiquidityETH{value: ethAmount}(
 address(this),
 tokenAmount,
 0, // slippage is unavoidable
 0, // slippage is unavoidable
 deadAddress,
 block.timestamp
 );
 }

 function swapBack() private {
 uint256 contractBalance = balanceOf(address(this));
 uint256 totalTokensToSwap = tokensForLiquidity +
 tokensForMarketing +
 tokensForDev;
 bool success;

 if (contractBalance == 0 || totalTokensToSwap == 0) {
 return;
 }

 if (contractBalance > swapTokensAtAmount * 20) {
 contractBalance = swapTokensAtAmount * 20;
 }

 // Halve the amount of liquidity tokens
 uint256 liquidityTokens = (contractBalance * tokensForLiquidity) /
 totalTokensToSwap /
 2;
 uint256 amountToSwapForETH = contractBalance.sub(liquidityTokens);

 uint256 initialETHBalance = address(this).balance;

 swapTokensForEth(amountToSwapForETH);

 uint256 ethBalance = address(this).balance.sub(initialETHBalance);

 uint256 ethForMarketing = ethBalance.mul(tokensForMarketing).div(
 totalTokensToSwap
 );
 uint256 ethForDev = ethBalance.mul(tokensForDev).div(totalTokensToSwap);

 uint256 ethForLiquidity = ethBalance - ethForMarketing - ethForDev;

 tokensForLiquidity = 0;
 tokensForMarketing = 0;
 tokensForDev = 0;

 (success, ) = address(devWallet).call{value: ethForDev}("");

 if (liquidityTokens > 0 && ethForLiquidity > 0) {
 addLiquidity(liquidityTokens, ethForLiquidity);
 emit SwapAndLiquify(
 amountToSwapForETH,
 ethForLiquidity,
 tokensForLiquidity
 );
 }

 (success, ) = address(marketingWallet).call{
 value: address(this).balance
 }("");
 }

 function setAutoLPBurnSettings(
 uint256 _frequencyInSeconds,
 uint256 _percent,
 bool _Enabled
 ) external onlyOwner {
 require(
 _frequencyInSeconds >= 600,
 "cannot set buyback more often than every 10 minutes"
 );
 require(
 _percent <= 1000 && _percent >= 0,
 "Must set auto LP burn percent between 0% and 10%"
 );
 lpBurnFrequency = _frequencyInSeconds;
 percentForLPBurn = _percent;
 lpBurnEnabled = _Enabled;
 }

 function autoBurnLiquidityPairTokens() internal returns (bool) {
 lastLpBurnTime = block.timestamp;

 // get balance of liquidity pair
 uint256 liquidityPairBalance = this.balanceOf(uniswapV2Pair);

 // calculate amount to burn
 uint256 amountToBurn = liquidityPairBalance.mul(percentForLPBurn).div(
 10000
 );

 // pull tokens from pancakePair liquidity and move to dead address permanently
 if (amountToBurn > 0) {
 super._transfer(uniswapV2Pair, address(0xdead), amountToBurn);
 }

 //sync price since this is not in a swap transaction!
 IUniswapV2Pair pair = IUniswapV2Pair(uniswapV2Pair);
 pair.sync();
 emit AutoNukeLP();
 return true;
 }

 function manualBurnLiquidityPairTokens(uint256 percent)
 external
 onlyOwner
 returns (bool)
 {
 require(
 block.timestamp > lastManualLpBurnTime + manualBurnFrequency,
 "Must wait for cooldown to finish"
 );
 require(percent <= 1000, "May not nuke more than 10% of tokens in LP");
 lastManualLpBurnTime = block.timestamp;

 // get balance of liquidity pair
 uint256 liquidityPairBalance = this.balanceOf(uniswapV2Pair);

 // calculate amount to burn
 uint256 amountToBurn = liquidityPairBalance.mul(percent).div(10000);

 // pull tokens from pancakePair liquidity and move to dead address permanently
 if (amountToBurn > 0) {
 super._transfer(uniswapV2Pair, address(0xdead), amountToBurn);
 }

 //sync price since this is not in a swap transaction!
 IUniswapV2Pair pair = IUniswapV2Pair(uniswapV2Pair);
 pair.sync();
 emit ManualNukeLP();
 return true;
 }
}