creation_sourcecode: // SPDX-License-Identifier: MIT
  
  pragma solidity ^0.8.1;
  
  library Address {
      function isContract(address account) internal view returns (bool) {
          // This method relies on extcodesize/address.code.length, which returns 0
          // for contracts in construction, since the code is only stored at the end
          // of the constructor execution.
  
          return account.code.length > 0;
      }
      function sendValue(address payable recipient, uint256 amount) internal {
          require(address(this).balance >= amount, "Address: insufficient balance");
  
          (bool success, ) = recipient.call{value: amount}("");
          require(success, "Address: unable to send value, recipient may have reverted");
      }
      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, "Address: low-level call failed");
      }
      function functionCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, errorMessage);
      }
      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");
      }
      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");
          (bool success, bytes memory returndata) = target.call{value: value}(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
          return functionStaticCall(target, data, "Address: low-level static call failed");
      }
      function functionStaticCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          (bool success, bytes memory returndata) = target.staticcall(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
      }
      function functionDelegateCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          (bool success, bytes memory returndata) = target.delegatecall(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
      function verifyCallResultFromTarget(
          address target,
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          if (success) {
              if (returndata.length == 0) {
                  // only check isContract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  require(isContract(target), "Address: call to non-contract");
              }
              return returndata;
          } else {
              _revert(returndata, errorMessage);
          }
      }
      function verifyCallResult(
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal pure returns (bytes memory) {
          if (success) {
              return returndata;
          } else {
              _revert(returndata, errorMessage);
          }
      }
      function _revert(bytes memory returndata, string memory errorMessage) private pure {
          // 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
              /// @solidity memory-safe-assembly
              assembly {
                  let returndata_size := mload(returndata)
                  revert(add(32, returndata), returndata_size)
              }
          } else {
              revert(errorMessage);
          }
      }
  }
  
  pragma solidity ^0.8.0;
  
  interface IERC20 {
      event Transfer(address indexed from, address indexed to, uint256 value);
      event Approval(address indexed owner, address indexed spender, uint256 value);
      function totalSupply() external view returns (uint256);
      function balanceOf(address account) external view returns (uint256);
      function transfer(address to, uint256 amount) external returns (bool);
      function allowance(address owner, address spender) external view returns (uint256);
      function approve(address spender, uint256 amount) external returns (bool);
      function transferFrom(address from, address to, uint256 amount) external returns (bool);
  }
  
  interface IERC20Metadata is IERC20 {
      function name() external view returns (string memory);
      function symbol() external view returns (string memory);
      function decimals() external view returns (uint8);
  }
  
  abstract contract Context {
      function _msgSender() internal view virtual returns (address) {
          return msg.sender;
      }
  
      function _msgData() internal view virtual returns (bytes calldata) {
          return msg.data;
      }
  }
  
  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;
      constructor(string memory name_, string memory symbol_) {
          _name = name_;
          _symbol = symbol_;
      }
      function name() public view virtual override returns (string memory) {
          return _name;
      }
      function symbol() public view virtual override returns (string memory) {
          return _symbol;
      }
      function decimals() public view virtual override returns (uint8) {
          return 18;
      }
      function totalSupply() public view virtual override returns (uint256) {
          return _totalSupply;
      }
      function balanceOf(address account) public view virtual override returns (uint256) {
          return _balances[account];
      }
      function transfer(address to, uint256 amount) public virtual override returns (bool) {
          address owner = _msgSender();
          _transfer(owner, to, amount);
          return true;
      }
      function allowance(address owner, address spender) public view virtual override returns (uint256) {
          return _allowances[owner][spender];
      }
      function approve(address spender, uint256 amount) public virtual override returns (bool) {
          address owner = _msgSender();
          _approve(owner, spender, amount);
          return true;
      }
      function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
          address spender = _msgSender();
          _spendAllowance(from, spender, amount);
          _transfer(from, to, amount);
          return true;
      }
      function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
          address owner = _msgSender();
          _approve(owner, spender, allowance(owner, spender) + addedValue);
          return true;
      }
      function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
          address owner = _msgSender();
          uint256 currentAllowance = allowance(owner, spender);
          require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
          unchecked {
              _approve(owner, spender, currentAllowance - subtractedValue);
          }
  
          return true;
      }
      function _transfer(address from, address to, uint256 amount) internal virtual {
          require(from != address(0), "ERC20: transfer from the zero address");
          require(to != address(0), "ERC20: transfer to the zero address");
  
          _beforeTokenTransfer(from, to, amount);
  
          uint256 fromBalance = _balances[from];
          require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
          unchecked {
              _balances[from] = fromBalance - amount;
              // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
              // decrementing then incrementing.
              _balances[to] += amount;
          }
  
          emit Transfer(from, to, amount);
  
          _afterTokenTransfer(from, to, amount);
      }
      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;
          unchecked {
              // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
              _balances[account] += amount;
          }
          emit Transfer(address(0), account, amount);
  
          _afterTokenTransfer(address(0), account, amount);
      }
      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;
              // Overflow not possible: amount <= accountBalance <= totalSupply.
              _totalSupply -= amount;
          }
  
          emit Transfer(account, address(0), amount);
  
          _afterTokenTransfer(account, address(0), amount);
      }
      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);
      }
      function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
          uint256 currentAllowance = allowance(owner, spender);
          if (currentAllowance != type(uint256).max) {
              require(currentAllowance >= amount, "ERC20: insufficient allowance");
              unchecked {
                  _approve(owner, spender, currentAllowance - amount);
              }
          }
      }
      function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
      function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
  }
  
  abstract contract Ownable is Context {
      address private _owner;
  
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      constructor() {
          _transferOwnership(_msgSender());
      }
      modifier onlyOwner() {
          _checkOwner();
          _;
      }
      function owner() public view virtual returns (address) {
          return _owner;
      }
      function _checkOwner() internal view virtual {
          require(owner() == _msgSender(), "Ownable: caller is not the owner");
      }
      function renounceOwnership() public virtual onlyOwner {
          _transferOwnership(address(0));
      }
      function transferOwnership(address newOwner) public virtual onlyOwner {
          require(newOwner != address(0), "Ownable: new owner is the zero address");
          _transferOwnership(newOwner);
      }
      function _transferOwnership(address newOwner) internal virtual {
          address oldOwner = _owner;
          _owner = newOwner;
          emit OwnershipTransferred(oldOwner, newOwner);
      }
  }
  
  interface IERC20Permit {
      function permit(
          address owner,
          address spender,
          uint256 value,
          uint256 deadline,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external;
      function nonces(address owner) external view returns (uint256);
      function DOMAIN_SEPARATOR() external view returns (bytes32);
  }
  
  library SafeERC20 {
      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));
      }
      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'
          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 oldAllowance = token.allowance(address(this), spender);
          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
      }
      function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
          unchecked {
              uint256 oldAllowance = token.allowance(address(this), spender);
              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
          }
      }
      function forceApprove(IERC20 token, address spender, uint256 value) internal {
          bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
  
          if (!_callOptionalReturnBool(token, approvalCall)) {
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
              _callOptionalReturn(token, approvalCall);
          }
      }
      function safePermit(
          IERC20Permit token,
          address owner,
          address spender,
          uint256 value,
          uint256 deadline,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) internal {
          uint256 nonceBefore = token.nonces(owner);
          token.permit(owner, spender, value, deadline, v, r, s);
          uint256 nonceAfter = token.nonces(owner);
          require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
      }
      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");
          require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
      }
  
      function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
          // 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 cannot use {Address-functionCall} here since this should return false
          // and not revert is the subcall reverts.
  
          (bool success, bytes memory returndata) = address(token).call(data);
          return
              success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
      }
  }
  
  library Math {
      enum Rounding {
          Down, // Toward negative infinity
          Up, // Toward infinity
          Zero // Toward zero
      }
      function max(uint256 a, uint256 b) internal pure returns (uint256) {
          return a > b ? a : b;
      }
      function min(uint256 a, uint256 b) internal pure returns (uint256) {
          return a < b ? a : b;
      }
      function average(uint256 a, uint256 b) internal pure returns (uint256) {
          // (a + b) / 2 can overflow.
          return (a & b) + (a ^ b) / 2;
      }
      function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
          // (a + b - 1) / b can overflow on addition, so we distribute.
          return a == 0 ? 0 : (a - 1) / b + 1;
      }
      function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
          unchecked {
              // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
              // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
              // variables such that product = prod1 * 2^256 + prod0.
              uint256 prod0; // Least significant 256 bits of the product
              uint256 prod1; // Most significant 256 bits of the product
              assembly {
                  let mm := mulmod(x, y, not(0))
                  prod0 := mul(x, y)
                  prod1 := sub(sub(mm, prod0), lt(mm, prod0))
              }
  
              // Handle non-overflow cases, 256 by 256 division.
              if (prod1 == 0) {
                  // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                  // The surrounding unchecked block does not change this fact.
                  // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                  return prod0 / denominator;
              }
  
              // Make sure the result is less than 2^256. Also prevents denominator == 0.
              require(denominator > prod1, "Math: mulDiv overflow");
  
              uint256 remainder;
              assembly {
                  // Compute remainder using mulmod.
                  remainder := mulmod(x, y, denominator)
  
                  // Subtract 256 bit number from 512 bit number.
                  prod1 := sub(prod1, gt(remainder, prod0))
                  prod0 := sub(prod0, remainder)
              }
  
              // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
              // See https://cs.stackexchange.com/q/138556/92363.
  
              // Does not overflow because the denominator cannot be zero at this stage in the function.
              uint256 twos = denominator & (~denominator + 1);
              assembly {
                  // Divide denominator by twos.
                  denominator := div(denominator, twos)
  
                  // Divide [prod1 prod0] by twos.
                  prod0 := div(prod0, twos)
  
                  // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                  twos := add(div(sub(0, twos), twos), 1)
              }
  
              // Shift in bits from prod1 into prod0.
              prod0 |= prod1 * twos;
  
              // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
              // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
              // four bits. That is, denominator * inv = 1 mod 2^4.
              uint256 inverse = (3 * denominator) ^ 2;
  
              // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
              // in modular arithmetic, doubling the correct bits in each step.
              inverse *= 2 - denominator * inverse; // inverse mod 2^8
              inverse *= 2 - denominator * inverse; // inverse mod 2^16
              inverse *= 2 - denominator * inverse; // inverse mod 2^32
              inverse *= 2 - denominator * inverse; // inverse mod 2^64
              inverse *= 2 - denominator * inverse; // inverse mod 2^128
              inverse *= 2 - denominator * inverse; // inverse mod 2^256
  
              // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
              // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
              // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
              // is no longer required.
              result = prod0 * inverse;
              return result;
          }
      }
      function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
          uint256 result = mulDiv(x, y, denominator);
          if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
              result += 1;
          }
          return result;
      }
      function sqrt(uint256 a) internal pure returns (uint256) {
          if (a == 0) {
              return 0;
          }
  
          // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
          //
          // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
          // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
          //
          // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
          // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
          // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
          //
          // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
          uint256 result = 1 << (log2(a) >> 1);
  
          // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
          // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
          // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
          // into the expected uint128 result.
          unchecked {
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              result = (result + a / result) >> 1;
              return min(result, a / result);
          }
      }
      function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = sqrt(a);
              return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
          }
      }
      function log2(uint256 value) internal pure returns (uint256) {
          uint256 result = 0;
          unchecked {
              if (value >> 128 > 0) {
                  value >>= 128;
                  result += 128;
              }
              if (value >> 64 > 0) {
                  value >>= 64;
                  result += 64;
              }
              if (value >> 32 > 0) {
                  value >>= 32;
                  result += 32;
              }
              if (value >> 16 > 0) {
                  value >>= 16;
                  result += 16;
              }
              if (value >> 8 > 0) {
                  value >>= 8;
                  result += 8;
              }
              if (value >> 4 > 0) {
                  value >>= 4;
                  result += 4;
              }
              if (value >> 2 > 0) {
                  value >>= 2;
                  result += 2;
              }
              if (value >> 1 > 0) {
                  result += 1;
              }
          }
          return result;
      }
      function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = log2(value);
              return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
          }
      }
      function log10(uint256 value) internal pure returns (uint256) {
          uint256 result = 0;
          unchecked {
              if (value >= 10 ** 64) {
                  value /= 10 ** 64;
                  result += 64;
              }
              if (value >= 10 ** 32) {
                  value /= 10 ** 32;
                  result += 32;
              }
              if (value >= 10 ** 16) {
                  value /= 10 ** 16;
                  result += 16;
              }
              if (value >= 10 ** 8) {
                  value /= 10 ** 8;
                  result += 8;
              }
              if (value >= 10 ** 4) {
                  value /= 10 ** 4;
                  result += 4;
              }
              if (value >= 10 ** 2) {
                  value /= 10 ** 2;
                  result += 2;
              }
              if (value >= 10 ** 1) {
                  result += 1;
              }
          }
          return result;
      }
      function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = log10(value);
              return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
          }
      }
      function log256(uint256 value) internal pure returns (uint256) {
          uint256 result = 0;
          unchecked {
              if (value >> 128 > 0) {
                  value >>= 128;
                  result += 16;
              }
              if (value >> 64 > 0) {
                  value >>= 64;
                  result += 8;
              }
              if (value >> 32 > 0) {
                  value >>= 32;
                  result += 4;
              }
              if (value >> 16 > 0) {
                  value >>= 16;
                  result += 2;
              }
              if (value >> 8 > 0) {
                  result += 1;
              }
          }
          return result;
      }
      function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
          unchecked {
              uint256 result = log256(value);
              return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
          }
      }
  }
  
  pragma solidity >=0.5.0;
  
  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;
  }
  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;
  }
  
  pragma solidity >=0.6.2;
  
  interface IUniswapV2Router01 {
      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 removeLiquidity(
          address tokenA,
          address tokenB,
          uint256 liquidity,
          uint256 amountAMin,
          uint256 amountBMin,
          address to,
          uint256 deadline
      ) external returns (uint256 amountA, uint256 amountB);
  
      function removeLiquidityETH(
          address token,
          uint256 liquidity,
          uint256 amountTokenMin,
          uint256 amountETHMin,
          address to,
          uint256 deadline
      ) external returns (uint256 amountToken, uint256 amountETH);
  
      function removeLiquidityWithPermit(
          address tokenA,
          address tokenB,
          uint256 liquidity,
          uint256 amountAMin,
          uint256 amountBMin,
          address to,
          uint256 deadline,
          bool approveMax,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external returns (uint256 amountA, uint256 amountB);
  
      function removeLiquidityETHWithPermit(
          address token,
          uint256 liquidity,
          uint256 amountTokenMin,
          uint256 amountETHMin,
          address to,
          uint256 deadline,
          bool approveMax,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external returns (uint256 amountToken, uint256 amountETH);
  
      function swapExactTokensForTokens(
          uint256 amountIn,
          uint256 amountOutMin,
          address[] calldata path,
          address to,
          uint256 deadline
      ) external returns (uint256[] memory amounts);
  
      function swapTokensForExactTokens(
          uint256 amountOut,
          uint256 amountInMax,
          address[] calldata path,
          address to,
          uint256 deadline
      ) external returns (uint256[] memory amounts);
  
      function swapExactETHForTokens(
          uint256 amountOutMin,
          address[] calldata path,
          address to,
          uint256 deadline
      ) external payable returns (uint256[] memory amounts);
  
      function swapTokensForExactETH(
          uint256 amountOut,
          uint256 amountInMax,
          address[] calldata path,
          address to,
          uint256 deadline
      ) external returns (uint256[] memory amounts);
  
      function swapExactTokensForETH(
          uint256 amountIn,
          uint256 amountOutMin,
          address[] calldata path,
          address to,
          uint256 deadline
      ) external returns (uint256[] memory amounts);
  
      function swapETHForExactTokens(
          uint256 amountOut,
          address[] calldata path,
          address to,
          uint256 deadline
      ) external payable returns (uint256[] memory amounts);
  
      function quote(
          uint256 amountA,
          uint256 reserveA,
          uint256 reserveB
      ) external pure returns (uint256 amountB);
  
      function getAmountOut(
          uint256 amountIn,
          uint256 reserveIn,
          uint256 reserveOut
      ) external pure returns (uint256 amountOut);
  
      function getAmountIn(
          uint256 amountOut,
          uint256 reserveIn,
          uint256 reserveOut
      ) external pure returns (uint256 amountIn);
  
      function getAmountsOut(uint256 amountIn, address[] calldata path)
          external
          view
          returns (uint256[] memory amounts);
  
      function getAmountsIn(uint256 amountOut, address[] calldata path)
          external
          view
          returns (uint256[] memory amounts);
  }
  interface IUniswapV2Router02 is IUniswapV2Router01 {
      function removeLiquidityETHSupportingFeeOnTransferTokens(
          address token,
          uint256 liquidity,
          uint256 amountTokenMin,
          uint256 amountETHMin,
          address to,
          uint256 deadline
      ) external returns (uint256 amountETH);
  
      function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
          address token,
          uint256 liquidity,
          uint256 amountTokenMin,
          uint256 amountETHMin,
          address to,
          uint256 deadline,
          bool approveMax,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external returns (uint256 amountETH);
  
      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.19;
  
  interface IUniswapV2Caller {
      function swapExactTokensForTokensSupportingFeeOnTransferTokens(
          address router,
          uint256 amountIn,
          uint256 amountOutMin,
          address[] calldata path,
          uint256 deadline
      ) external;
  }
  
  contract BrAIngent is ERC20, Ownable {
      using SafeERC20 for IERC20;
      
      IUniswapV2Caller public uniswapV2Caller;
  
      uint256 private constant MAX = ~uint256(0);
  
      ///////////////////////////////////////////////////////////////////////////
      address public baseTokenForMarket;
      bool public isBaseTokenWETH;
      bool private inSwapAndLiquify;
      uint24 public sellLiquidityFee;
      uint24 public buyLiquidityFee;
  
      uint24 public sellMarketingFee;
      uint24 public buyMarketingFee;
  
      address public marketingWallet;
      bool public isMarketingFeeBaseToken;
  
      uint256 public minAmountToTakeFee;
      uint256 public maxWallet;
      uint256 public maxTransactionAmount;
  
      address public mainRouter;
      address public mainPair;
  
  
      mapping(address => bool) public isExcludedFromMaxTransactionAmount;
      mapping(address => bool) public isExcludedFromFee;
      mapping(address => bool) public automatedMarketMakerPairs;
  
      uint256 private _liquidityFeeTokens;
      uint256 private _marketingFeeTokens;
      event UpdateLiquidityFee(
          uint24 newSellLiquidityFee,
          uint24 newBuyLiquidityFee,
          uint24 oldSellLiquidityFee,
          uint24 oldBuyLiquidityFee
      );
      event UpdateMarketingFee(
          uint24 newSellMarketingFee,
          uint24 newBuyMarketingFee,
          uint24 oldSellMarketingFee,
          uint24 oldBuyMarketingFee
      );
      event UpdateMarketingWallet(
          address indexed newMarketingWallet,
          bool newIsMarketingFeeBaseToken,
          address indexed oldMarketingWallet,
          bool oldIsMarketingFeeBaseToken
      );
      event ExcludedFromMaxTransactionAmount(address indexed account, bool isExcluded);
      event UpdateMinAmountToTakeFee(uint256 newMinAmountToTakeFee, uint256 oldMinAmountToTakeFee);
      event SetAutomatedMarketMakerPair(address indexed pair, bool value);
      event ExcludedFromFee(address indexed account, bool isEx);
      event SwapAndLiquify(
          uint256 tokensForLiquidity,
          uint256 baseTokenForLiquidity
      );
      event MarketingFeeTaken(
          uint256 marketingFeeTokens,
          uint256 marketingFeeBaseTokenSwapped
      );
      event MainRouterUpdated(
          address mainRouter, address mainPair, address baseTokenForMarket
      );
      event UpdateMaxWallet(uint256 newMaxWallet, uint256 oldMaxWallet);
      event UpdateMaxTransactionAmount(uint256 newMaxTransactionAmount, uint256 oldMaxTransactionAmount);
      ///////////////////////////////////////////////////////////////////////////////
  
      constructor(
          address _marketingWallet,
          address _baseTokenForMarket,
          address _mainRouter,
          bool _isMarketingFeeBaseToken,
          uint24 _sellLiquidityFee,
          uint24 _buyLiquidityFee,
          uint24 _sellMarketingFee,
          uint24 _buyMarketingFee,
          address _uniswapV2Caller
      ) ERC20("BrAIngent", "BrAIn") {
          _mint(msg.sender, 1000000000000000000000000);
          uniswapV2Caller = IUniswapV2Caller(_uniswapV2Caller);
          baseTokenForMarket=_baseTokenForMarket;
          require(_marketingWallet!=address(0), "marketing wallet can not be 0");
          require(_mainRouter!=address(0), "Router address can not be 0");
  
          marketingWallet=_marketingWallet;
          isMarketingFeeBaseToken=_isMarketingFeeBaseToken;
          emit UpdateMarketingWallet(
              marketingWallet,
              isMarketingFeeBaseToken,
              address(0),
              false
          );
          mainRouter=_mainRouter;
          if(baseTokenForMarket != IUniswapV2Router02(mainRouter).WETH()){            
              IERC20(baseTokenForMarket).safeApprove(mainRouter, MAX);            
          }else{
              isBaseTokenWETH=true;
          }
         
          
          _approve(address(this), address(uniswapV2Caller), MAX);
          _approve(address(this), mainRouter, MAX);
          
          emit MainRouterUpdated(mainRouter, mainPair, baseTokenForMarket);
          mainPair = IUniswapV2Factory(IUniswapV2Router02(mainRouter).factory()).createPair(
              address(this),
              baseTokenForMarket
          );
   
          maxWallet = 1000000000000000000000000;
          emit UpdateMaxWallet(1000000000000000000000000, 0);
          maxTransactionAmount = 1000000000000000000000000;
          emit UpdateMaxTransactionAmount(1000000000000000000000000, 0);
          
          sellLiquidityFee=_sellLiquidityFee;
          buyLiquidityFee=_buyLiquidityFee;
          emit UpdateLiquidityFee(sellLiquidityFee, buyLiquidityFee, 0, 0);        
          sellMarketingFee=_sellMarketingFee;
          buyMarketingFee=_buyMarketingFee;
          emit UpdateMarketingFee(
              sellMarketingFee,
              buyMarketingFee,
              0,
              0
          );
          minAmountToTakeFee = 100000000000000000000;
          emit UpdateMinAmountToTakeFee(minAmountToTakeFee, 0);
          isExcludedFromFee[address(this)]=true;
          isExcludedFromFee[marketingWallet]=true;
          isExcludedFromFee[_msgSender()]=true;
          isExcludedFromFee[address(0xdead)] = true;
          isExcludedFromMaxTransactionAmount[address(0xdead)]=true;
          isExcludedFromMaxTransactionAmount[address(this)]=true;
          isExcludedFromMaxTransactionAmount[marketingWallet]=true;
          isExcludedFromMaxTransactionAmount[_msgSender()]=true;
          _setAutomatedMarketMakerPair(mainPair, true);
      }
  
      function updateMainPair(
          address _mainRouter,
          address _baseTokenForMarket
      ) external onlyOwner {
          baseTokenForMarket = _baseTokenForMarket;
          if(mainRouter != _mainRouter){
              _approve(address(this), _mainRouter, MAX);
              mainRouter = _mainRouter;
          } 
          mainPair = IUniswapV2Factory(IUniswapV2Router02(mainRouter).factory()).createPair(
              address(this),
              baseTokenForMarket
          );
          if(baseTokenForMarket != IUniswapV2Router02(mainRouter).WETH()){            
              IERC20(baseTokenForMarket).safeApprove(mainRouter, MAX);
              isBaseTokenWETH=false;            
          }else{
              isBaseTokenWETH=true;
          }
       
          emit MainRouterUpdated(mainRouter, mainPair, baseTokenForMarket);
          _setAutomatedMarketMakerPair(mainPair, true);
      }
  
  
      /////////////////////////////////////////////////////////////////////////////////
      modifier lockTheSwap() {
          inSwapAndLiquify = true;
          _;
          inSwapAndLiquify = false;
      }
  
      function updateLiquidityFee(
          uint24 _sellLiquidityFee,
          uint24 _buyLiquidityFee
      ) external onlyOwner {
          require(
              _sellLiquidityFee + sellMarketingFee <= 200000,
              "sell fee <= 20%"
          );
          require(_buyLiquidityFee + buyMarketingFee <= 200000, "buy fee <= 20%");
          emit UpdateLiquidityFee(
              _sellLiquidityFee,
              _buyLiquidityFee,
              sellLiquidityFee,
              buyLiquidityFee
          );
          sellLiquidityFee = _sellLiquidityFee;
          buyLiquidityFee = _buyLiquidityFee;           
      }
  
      function updateMaxWallet(uint256 _maxWallet) external onlyOwner {
          require(_maxWallet>=totalSupply() / 10000, "maxWallet >= total supply / 10000");
          emit UpdateMaxWallet(_maxWallet, maxWallet);
          maxWallet = _maxWallet;
      }
  
      function updateMaxTransactionAmount(uint256 _maxTransactionAmount)
          external
          onlyOwner
      {
          require(_maxTransactionAmount>=totalSupply() / 10000, "maxTransactionAmount >= total supply / 10000");
          emit UpdateMaxTransactionAmount(_maxTransactionAmount, maxTransactionAmount);
          maxTransactionAmount = _maxTransactionAmount;
      }
  
      function updateMarketingFee(
          uint24 _sellMarketingFee,
          uint24 _buyMarketingFee
      ) external onlyOwner {
          require(
              _sellMarketingFee + sellLiquidityFee <= 200000,
              "sell fee <= 20%"
          );
          require(_buyMarketingFee + buyLiquidityFee <= 200000, "buy fee <= 20%");
          emit UpdateMarketingFee(
              _sellMarketingFee,
              _buyMarketingFee,
              sellMarketingFee,
              buyMarketingFee
          );
          sellMarketingFee = _sellMarketingFee;
          buyMarketingFee = _buyMarketingFee;  
      }
  
      function updateMarketingWallet(
          address _marketingWallet,
          bool _isMarketingFeeBaseToken
      ) external onlyOwner {
          require(_marketingWallet != address(0), "marketing wallet can't be 0");
          emit UpdateMarketingWallet(_marketingWallet, _isMarketingFeeBaseToken,
              marketingWallet, isMarketingFeeBaseToken);
          marketingWallet = _marketingWallet;
          isMarketingFeeBaseToken = _isMarketingFeeBaseToken;
          isExcludedFromFee[_marketingWallet] = true;
          isExcludedFromMaxTransactionAmount[_marketingWallet] = true;
      }
  
      function updateMinAmountToTakeFee(uint256 _minAmountToTakeFee)
          external
          onlyOwner
      {
          require(_minAmountToTakeFee > 0, "minAmountToTakeFee > 0");
          emit UpdateMinAmountToTakeFee(_minAmountToTakeFee, minAmountToTakeFee);
          minAmountToTakeFee = _minAmountToTakeFee;     
      }
  
      function setAutomatedMarketMakerPair(address pair, bool value)
          public
          onlyOwner
      {
          require(
              automatedMarketMakerPairs[pair] != value,
              "Automated market maker pair is already set to that value"
          );
          _setAutomatedMarketMakerPair(pair, value);
      }
  
      function _setAutomatedMarketMakerPair(address pair, bool value) private {        
          automatedMarketMakerPairs[pair] = value;
          isExcludedFromMaxTransactionAmount[pair] = value;
          emit SetAutomatedMarketMakerPair(pair, value);
      }
  
      function excludeFromFee(address account, bool isEx) external onlyOwner {
          require(isExcludedFromFee[account] != isEx, "already");
          isExcludedFromFee[account] = isEx;
          emit ExcludedFromFee(account, isEx);
      }
  
      function excludeFromMaxTransactionAmount(address account, bool isEx)
          external
          onlyOwner
      {
          require(isExcludedFromMaxTransactionAmount[account]!=isEx, "already");
          isExcludedFromMaxTransactionAmount[account] = isEx;
          emit ExcludedFromMaxTransactionAmount(account, isEx);
      }
  
      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");
          uint256 contractTokenBalance = balanceOf(address(this));
          
          uint256 totalTokensTaken = _liquidityFeeTokens + _marketingFeeTokens;
          bool overMinimumTokenBalance = totalTokensTaken >=
              minAmountToTakeFee && totalTokensTaken <= contractTokenBalance;
          // Take Fee
          if (
              !inSwapAndLiquify &&
              balanceOf(mainPair) > 0 &&
              overMinimumTokenBalance &&
              automatedMarketMakerPairs[to]
          ) {
              takeFee();
          }
  
          uint256 _liquidityFee;
          uint256 _marketingFee;
          // If any account belongs to isExcludedFromFee account then remove the fee
  
          if (
              !inSwapAndLiquify &&
              !isExcludedFromFee[from] &&
              !isExcludedFromFee[to]
          ) {
              // Buy
              if (automatedMarketMakerPairs[from]) {
                  _liquidityFee = amount * buyLiquidityFee / 1000000;
                  _marketingFee = amount * buyMarketingFee / 1000000;
              }
              // Sell
              else if (automatedMarketMakerPairs[to]) {
                  _liquidityFee = amount * sellLiquidityFee / 1000000;
                  _marketingFee = amount * sellMarketingFee / 1000000;
              }
              uint256 _feeTotal = _liquidityFee + _marketingFee;
              if (_feeTotal > 0) super._transfer(from, address(this), _feeTotal);
              amount = amount - _liquidityFee - _marketingFee;
              _liquidityFeeTokens = _liquidityFeeTokens + _liquidityFee;
              _marketingFeeTokens = _marketingFeeTokens + _marketingFee;
          }
          super._transfer(from, to, amount);
          if (!inSwapAndLiquify) {
              if (!isExcludedFromMaxTransactionAmount[from]) {
                  require(
                      amount <= maxTransactionAmount,
                      "ERC20: exceeds transfer limit"
                  );
              }
              if (!isExcludedFromMaxTransactionAmount[to]) {
                  require(
                      balanceOf(to) <= maxWallet,
                      "ERC20: exceeds max wallet limit"
                  );
              }
          }
      }
  
      function takeFee() private lockTheSwap {
  
          // Halve the amount of liquidity tokens
          uint256 tokensForLiquidity = _liquidityFeeTokens / 2;
          uint256 initialBaseTokenBalance = isBaseTokenWETH ? address(this).balance
              : IERC20(baseTokenForMarket).balanceOf(address(this));        
           
          uint256 baseTokenForLiquidity;
          if (isMarketingFeeBaseToken) {
              uint256 tokensForSwap=tokensForLiquidity+_marketingFeeTokens;
              if(tokensForSwap>0)
                  swapTokensForBaseToken(tokensForSwap);
              uint256 baseTokenBalance = isBaseTokenWETH ? address(this).balance - initialBaseTokenBalance
                  : IERC20(baseTokenForMarket).balanceOf(address(this)) - initialBaseTokenBalance;
              uint256 baseTokenAmountForMarketing = (baseTokenBalance *
                  _marketingFeeTokens) / tokensForSwap;
              baseTokenForLiquidity = baseTokenBalance - baseTokenAmountForMarketing;
              if(baseTokenAmountForMarketing>0){
                  if(isBaseTokenWETH){                
                      (bool success, )=address(marketingWallet).call{value: baseTokenAmountForMarketing}("");
                      if(success){
                          emit MarketingFeeTaken(0, baseTokenAmountForMarketing);
                      }
                  }else{
                      IERC20(baseTokenForMarket).safeTransfer(
                          marketingWallet,
                          baseTokenAmountForMarketing
                      );
                      emit MarketingFeeTaken(0, baseTokenAmountForMarketing);
                  }                
              }            
          } else {
              if(tokensForLiquidity>0)
                  swapTokensForBaseToken(tokensForLiquidity);
              baseTokenForLiquidity = isBaseTokenWETH ? address(this).balance - initialBaseTokenBalance
                  : IERC20(baseTokenForMarket).balanceOf(address(this)) - initialBaseTokenBalance;
              if(_marketingFeeTokens>0){
                  _transfer(address(this), marketingWallet, _marketingFeeTokens);
                  emit MarketingFeeTaken(_marketingFeeTokens, 0);                
              }            
          }
  
          if (tokensForLiquidity > 0 && baseTokenForLiquidity > 0) {
              addLiquidity(tokensForLiquidity, baseTokenForLiquidity);
              emit SwapAndLiquify(tokensForLiquidity, baseTokenForLiquidity);
          }
          _marketingFeeTokens = 0;
          _liquidityFeeTokens = 0;    
          if(balanceOf(address(this))>0){
              if(owner()!=address(0)){
                  _transfer(address(this), owner(), balanceOf(address(this)));  
              }else{
                  _transfer(address(this), address(0xdead), balanceOf(address(this)));  
              }
          } 
      }
  
      function swapTokensForBaseToken(uint256 tokenAmount) private {
          address[] memory path = new address[](2);
          path[0] = address(this);
          path[1] = baseTokenForMarket;     
          if (isBaseTokenWETH){
              IUniswapV2Router02(mainRouter).swapExactTokensForETHSupportingFeeOnTransferTokens(
                  tokenAmount,
                  0, // accept any amount of BaseToken
                  path,
                  address(this),
                  block.timestamp
              );
          }else{
              uniswapV2Caller.swapExactTokensForTokensSupportingFeeOnTransferTokens(
                      address(mainRouter),
                      tokenAmount,
                      0, // accept any amount of BaseToken
                      path,
                      block.timestamp
                  );
          }
          
      }
  
      function addLiquidity(uint256 tokenAmount, uint256 baseTokenAmount)
          private
      {
          if (isBaseTokenWETH) 
              IUniswapV2Router02(mainRouter).addLiquidityETH{value: baseTokenAmount}(
                  address(this),
                  tokenAmount,
                  0, // slippage is unavoidable
                  0, // slippage is unavoidable
                  address(0xdead),
                  block.timestamp
              );
          else{
              IUniswapV2Router02(mainRouter).addLiquidity(
                  address(this),
                  baseTokenForMarket,
                  tokenAmount,
                  baseTokenAmount,
                  0,
                  0,
                  address(0xdead),
                  block.timestamp
              );
          }  
      }
  
      function withdrawETH() external onlyOwner {
          (bool success, )=address(owner()).call{value: address(this).balance}("");
          require(success, "Failed in withdrawal");
      }
      function withdrawToken(address token) external onlyOwner{
          require(address(this) != token, "Not allowed");
          IERC20(token).safeTransfer(owner(), IERC20(token).balanceOf(address(this)));
      }
      receive() external payable {}
  }