ETH Price: $2,638.09 (+1.83%)

Token

ETH314 (ETH314)
 

Overview

Max Total Supply

314,000,000 ETH314

Holders

11

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
7,331,284.865180127610108214 ETH314

Value
$0.00
0x9Ca9Ca3626afd9989D699A3E6c8E379EB89Aa318
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume

Minimal Proxy Contract for 0xba8deefe84adbfbad2278665e90237a819795bc9

Contract Name:
BIFKN314

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 50 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 16 : BIFKN314.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "./BIFKNERC20.sol";
import "./BIFKN314LP.sol";
import "./PreventAutoSwap.sol";
import "./interfaces/IBIFKN314Factory.sol";
import "./interfaces/IBIFKN314CALLEE.sol";
import "./interfaces/IERC314Errors.sol";
import "./interfaces/IERC314Events.sol";
import "./interfaces/IERC314.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

/**
 * @title BIFKN314
 * @dev This is a contract that implements the core functionality of the BIFKN314 token.
 * The contract is used to create a token that can be used for liquidity provision and swapping.
 * It follows the Automated Market Maker (AMM) model using the constant product formula.
 * The contract allows users to add and remove liquidity, swap tokens, and perform flash swaps.
 * The contract also accrues fees and distributes them to the feeTo address.
 * The contract is initialized with a supply cap.
 * The contract also maintains a reference to the BIFKN314LP contract for LP token management.
 * The contract allows for a factory address of address(0) to be set, which will disable fee distribution.
 * The contract owner can set the trading fee rate, maximum wallet percentage, and metadata URI.
 * The contract owner can also enable trading, set the fee collector address, and claim accrued trading fees.
 */

contract BIFKN314 is
    BIFKNERC20,
    ReentrancyGuard,
    PreventAutoSwap,
    IERC314Errors,
    IERC314Events,
    IERC314
{
    using Math for uint256;

    /**
     * @dev Represents the address constant for the dead address.
     * The dead address is a predefined address with all zeros, used to represent
     * an address that is no longer in use or has been destroyed.
     */
    address private constant DEAD_ADDRESS =
        0x000000000000000000000000000000000000dEaD;

    /**
     * @dev The minimum liquidity required for a transaction.
     */
    uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3;

    /**
     * @dev The `FLASHSWAP_FEE_RATE` constant represents the fee rate for flash swaps.
     * It is set to 30, which corresponds to a fee rate of 0.3%.
     */
    uint256 public constant FLASHSWAP_FEE_RATE = 30; // 0.3% fee

    /**
     * @dev The base swap rate for the contract.
     * It represents a 0.3% fee for each swap.
     */
    uint256 public constant BASE_SWAP_RATE = 30; // 0.3% fee

    /**
     * @dev The SCALE_FACTOR constant represents the scaling factor used in the contract.
     * It is set to 10000.
     */
    uint256 public constant SCALE_FACTOR = 10000;

    /**
     * @dev The MAX_FEE_RATE constant represents the maximum fee rate that can be set.
     * It is set to 500, which corresponds to a fee rate of 5%.
     */
    uint256 public constant MAX_FEE_RATE = 500; // 5% fee

    /**
     * @dev Represents the metadata URI for the contract.
     */
    string public metadataURI;

    /**
     * @dev Represents the LP token contract for the BIFKN314 contract.
     */
    BIFKN314LP public liquidityToken;

    /**
     * @dev A public boolean variable that indicates whether the contract is initialized or not.
     */
    bool public isInitialized;

    /**
     * @dev A public boolean variable that indicates whether the contract factory is initialized or not.
     */
    bool public factoryInitialized;

    /**
     * @dev A boolean variable indicating whether trading is enabled or not.
     * Once trading is enabled, it cannot be disabled.
     * Trading must be enabled before users can swap tokens.
     * Trading can only be enabled by the contract owner.
     * Trading is disabled by default.
     */
    bool public tradingEnabled;

    /**
     * @dev A mapping that stores whether an address is exempt from the maximum wallet limit.
     */
    mapping(address => bool) public isMaxWalletExempt;

    /**
     * @dev Represents the last cumulative price of the native asset.
     */
    uint public price0CumulativeLast;

    /**
     * @dev Represents the last cumulative price of the token.
     */
    uint public price1CumulativeLast;

    /**
     * @dev Represents the timestamp of the last block for enabling twap
     */
    uint32 public blockTimestampLast;

    /**
     * @dev The address of the factory contract.
     */
    IBIFKN314Factory public factory;

    /**
     * @dev The maximum percentage of the total supply that a wallet can hold.
     * For example, a value of 100 represents 1% of the total supply.
     */
    uint256 public maxWalletPercent;

    /**
     * @dev A boolean variable that indicates whether the maximum wallet limit is enabled or not.
     */
    bool public maxWalletEnabled;

    /**
     * @dev Public variable to store the accrued native fees.
     */
    uint256 public accruedNativeFactoryFees;

    /**
     * @dev Public variable to store the amount of accrued token fees.
     */
    uint256 public accruedTokenFactoryFees;

    /**
     * @dev The tradingFeeRate variable represents the rate at which trading fees are charged.
     * It is a public variable, meaning it can be accessed and modified by other contracts and external accounts.
     * The value of tradingFeeRate is a uint256, which represents a non-negative integer.
     * If the value of tradingFeeRate is 0, no trading fees are charged.
     * If the value of tradingFeeRate is 500, a trading fee of 5% is charged.
     */
    uint256 public tradingFeeRate;

    /**
     * @dev Public variable to store the accrued trading fees.
     */
    uint256 public accruedNativeTradingFees;

    /**
     * @dev Public variable to store the accrued token trading fees.
     */
    uint256 public accruedTokenTradingFees;

    /**
     * @dev The address of the fee collector.
     */
    address public feeCollector;

    /**
     * @dev The address of the contract owner.
     */
    address public owner;

    /**
     * @dev Modifier that allows only the contract owner to execute the function.
     * Throws an error if the caller is not the owner.
     */
    modifier onlyOwner() {
        if (_msgSender() != owner) revert Unauthorized(_msgSender());
        _;
    }

    /**
     * @dev Modifier that allows only the fee collector to execute the function.
     * Throws an error if the caller is not the fee collector.
     */
    modifier onlyFeeCollector() {
        if (_msgSender() != feeCollector) revert Unauthorized(_msgSender());
        _;
    }

    /**
     * @dev Modifier to ensure that a transaction is executed before the specified deadline.
     * @param deadline The deadline timestamp after which the transaction is considered expired.
     * @notice This modifier reverts the transaction if the current block timestamp is greater than or equal to the deadline.
     */
    modifier ensureDeadline(uint deadline) {
        if (block.timestamp >= deadline) revert TransactionExpired();
        _;
    }

    /**
     * @dev Constructor function for the BIFKN314 contract.
     * It initializes the contract by calling the constructor of the BIFKNERC20 contract.
     * If the message sender is a contract, it sets the factory address to the message sender.
     * If the message sender is not a contract, it sets the factory address to address(0).
     * Finally, it transfers the ownership of the contract to the message sender.
     */
    constructor() BIFKNERC20() {
        address sender = _msgSender();
        _transferOwnership(sender);
    }

    /**
     * @dev Initializes the factory contract with a new owner.
     * @param newOwner The address of the new owner.
     * @notice This function can only be called once to initialize the factory contract.
     * @notice Once initialized, the ownership of the contract will be transferred to the factory contract.
     * @notice If the factory contract has already been initialized, calling this function will revert.
     */
    function initializeFactory(address newOwner) external {
        if (factoryInitialized) revert OwnerAlreadyInitialized();
        factoryInitialized = true;
        factory = IBIFKN314Factory(newOwner);
        _transferOwnership(address(factory));
    }

    /**
     * @dev Initializes the contract with the given name and symbol.
     * Only the contract owner can call this function.
     *
     * @param tokenName The name of the contract.
     * @param tokenSymbol The symbol of the contract.
     */
    function initialize(
        string memory tokenName,
        string memory tokenSymbol
    ) public override onlyOwner {
        super.initialize(tokenName, tokenSymbol);

        liquidityToken = new BIFKN314LP();
        liquidityToken.initialize(
            string(abi.encodePacked(tokenName, " LP Token")),
            string("BLP")
        );
    }

    /**
     * @dev Sets the total supply and mints tokens to the specified owner.
     * @param totalSupply_ The total supply of tokens to be minted.
     * @param owner_ The address of the owner to receive the minted tokens.
     * @param feeRate_ The trading fee rate to be set.
     * @param maxWalletPercent_ The maximum wallet percentage to be set.
     * @param metadataURI_ The metadata URI to be set.
     * @notice Only the contract owner can call this function.
     * @notice The total supply must be greater than zero.
     * @notice The total supply must not have been already minted.
     * @notice The owner address must not be the zero address.
     */
    function setSupplyAndMint(
        uint256 totalSupply_,
        address owner_,
        uint256 feeRate_,
        uint256 maxWalletPercent_,
        string memory metadataURI_
    ) public onlyOwner {
        if (totalSupply_ == 0) {
            revert AmountMustBeGreaterThanZero();
        }
        if (totalSupply() > 0) {
            revert SupplyAlreadyMinted();
        }
        if (owner_ == address(0)) {
            revert InvalidOwner();
        }

        if (maxWalletPercent_ > 0) {
            maxWalletEnabled = true;
            setMaxWalletPercent(maxWalletPercent_);
        }

        metadataURI = metadataURI_;
        setTradingFeeRate(feeRate_);
        _transferOwnership(owner_);
        feeCollector = owner_;

        super._mint(owner_, totalSupply_);
    }

    /**
     * @dev Transfers tokens from the sender to the recipient.
     * Overrides the transfer function from the inherited contract.
     * If the recipient is this contract and autoSwap is not prevented,
     * then it automatically swaps tokens to native currency.
     * Otherwise, calls the transfer function from the inherited contract.
     * @param recipient The address receiving the tokens.
     * @param amount The amount of tokens to transfer.
     * @return success A boolean indicating the success of the transfer.
     */
    function transfer(
        address recipient,
        uint256 amount
    ) public override returns (bool success) {
        if (recipient == address(this) && !_autoSwapIsPrevented()) {
            swapTokenToNative(
                amount,
                _calculateAutoSwapSlippage(amount, false),
                block.timestamp + 3 minutes
            );
            success = true;
        } else {
            _checkMaxWallet(recipient, amount);
            success = super.transfer(recipient, amount);
        }
    }

    /**
     * @dev Internal function to transfer tokens from one address to another.
     * Overrides the internal transfer function from the inherited contract.
     * Calls the transfer function from the inherited contract.
     * This function is specifically used when transferring tokens to the contract
     * for the purpose of adding liquidity, swapping, or flash swapping.
     * @param from The address to transfer tokens from.
     * @param to The address to transfer tokens to.
     * @param value The amount of tokens to transfer.
     */
    function _internalTransfer(
        address from,
        address to,
        uint256 value
    ) internal {
        super._transfer(from, to, value);
    }

    /**
     * @dev Adds liquidity to the contract by depositing tokens and native currency.
     * @param amountToken_ The amount of tokens to be deposited.
     * @param recipient The address of the recipient of the liquidity tokens.
     * @param deadline The deadline in unix time from the current timestamp for the transaction to occur.
     * @return liquidity The amount of liquidity tokens minted.
     */
    function addLiquidity(
        uint256 amountToken_,
        address recipient,
        uint256 deadline
    )
        public
        payable
        nonReentrant
        ensureDeadline(deadline)
        returns (uint256 liquidity)
    {
        address sender = _msgSender();
        // check if contract is initialized
        // only owner can add liquidity before initialization
        if (!isInitialized && sender != owner) {
            revert ContractIsNotInitialized();
        }
        if (amountToken_ == 0 || msg.value == 0) {
            revert AmountMustBeGreaterThanZero();
        }

        // get reserves
        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();
        // the native reserve is the balance of the contract minus the value sent
        nativeReserve = nativeReserve - msg.value;

        uint256 lpTotalSupply = liquidityToken.totalSupply();
        uint256 amountNative = msg.value;
        uint256 amountToken = amountToken_;

        if (lpTotalSupply == 0) {
            uint256 _amountProduct = Math.sqrt(amountNative * amountToken);
            liquidity = _amountProduct - MINIMUM_LIQUIDITY;
            // Set owner of the first MINIMUM_LIQUIDITY tokens to the zero address
            liquidityToken.mint(DEAD_ADDRESS, MINIMUM_LIQUIDITY);
            // Liquidity is initialized
            isInitialized = true;
        } else {
            if (nativeReserve == 0 || tokenReserve == 0)
                revert InvalidReserves();

            // Determine the amount of token required to add liquidity
            // according to the native amount sent
            amountToken = (amountNative * tokenReserve) / nativeReserve;
            uint256 currentKValue = _calculateKValue(
                nativeReserve,
                tokenReserve
            );

            if (amountToken_ < amountToken) {
                revert AmountOfTokensLessThanMinimumRequired(
                    amountToken_,
                    amountToken
                );
            }

            /**
             * @dev Calculates the liquidity amount based on the given amounts of native currency and token.
             * The liquidity amount is determined by taking the minimum of two calculations:
             * 1. (amountNative * lpTotalSupply) / _nativeReserve
             * 2. (amountToken * lpTotalSupply) / _tokenReserve
             */
            liquidity = Math.min(
                (amountNative * lpTotalSupply) / nativeReserve,
                (amountToken * lpTotalSupply) / tokenReserve
            );

            /**
             * @dev Updates the reserves and checks the liquidity ratio.
             * The new k value is calculated by multiplying the new token reserve by the new native reserve.
             * If the new k value is less than the current k value, the transaction is reverted.
             */
            uint256 newNativeReserve = nativeReserve + amountNative;
            uint256 newTokenReserve = tokenReserve + amountToken;
            uint256 newKValue = newTokenReserve * newNativeReserve;
            if (newKValue < currentKValue) {
                revert DecreasesK();
            }
        }

        // check if liquidity is greater than 0
        if (liquidity == 0) {
            revert InsufficientLiquidityMinted();
        }
        // mint liquidity tokens to the liquidity provider
        liquidityToken.mint(recipient, liquidity);

        // Only transfer the necessary amount of tokens
        _internalTransfer(sender, address(this), amountToken);

        _updatePrices();

        emit AddLiquidity(sender, recipient, liquidity, msg.value, amountToken);
    }

    /**
     * @dev Removes liquidity from the contract by transferring native currency and tokens back to the liquidity provider.
     * @param amount The amount of liquidity to be removed.
     * @param recipient The address of the recipient of the native currency and tokens.
     * @param deadline The deadline in unix time from the current timestamp for the transaction to occur.
     * @return nativeAmount The amount of native currency received.
     * @return tokenAmount The amount of tokens received.
     * @notice The liquidity provider must have sufficient liquidity balance.
     */
    function removeLiquidity(
        uint256 amount,
        address recipient,
        uint256 deadline
    )
        public
        nonReentrant
        ensureDeadline(deadline)
        returns (uint256 nativeAmount, uint256 tokenAmount)
    {
        address sender = _msgSender();
        if (!isInitialized) {
            revert ContractIsNotInitialized();
        }

        uint256 lpTokenBalance = liquidityToken.balanceOf(sender);

        if (lpTokenBalance == 0) {
            revert YouHaveNoLiquidity();
        }
        if (amount > lpTokenBalance) {
            revert InsufficientLiquidity();
        }

        (nativeAmount, tokenAmount) = getAmountsForLP(amount);

        liquidityToken.burnFrom(sender, amount);

        _transferNative(recipient, nativeAmount);
        super._transfer(address(this), recipient, tokenAmount);

        emit RemoveLiquidity(
            sender,
            recipient,
            amount,
            nativeAmount,
            tokenAmount
        );

        _updatePrices();
    }

    /**
     * @dev Swaps native currency to tokens.
     * @param minimumTokensOut The minimum amount of tokens to receive in the swap.
     * @param deadline The deadline in unix time from current timestamp for the swap to occur.
     */
    function swapNativeToToken(
        uint256 minimumTokensOut,
        uint256 deadline
    ) public payable nonReentrant ensureDeadline(deadline) {
        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();
        uint256 nativeIn = msg.value;
        address sender = _msgSender();

        nativeReserve = nativeReserve - nativeIn;

        (uint256 tokensBought, uint256 factoryFee, uint256 tradingFee) = _swap(
            nativeIn,
            minimumTokensOut,
            nativeReserve,
            tokenReserve
        );

        accruedNativeTradingFees += tradingFee;
        _handleFactoryFees(factoryFee, true);

        _checkMaxWallet(sender, tokensBought);
        super._transfer(address(this), sender, tokensBought);

        _updatePrices();
        emit Swap(sender, 0, nativeIn, tokensBought, 0, false);
    }

    /**
     * @dev Swaps a specified amount of tokens for native currency.
     * @param tokensSold The amount of tokens to be sold.
     * @param minimumNativeOut The minimum amount of native currency expected to be received.
     * @param deadline The deadline in unix time from current timestamp for the swap to occur.
     */
    function swapTokenToNative(
        uint256 tokensSold,
        uint256 minimumNativeOut,
        uint256 deadline
    ) public nonReentrant ensureDeadline(deadline) {
        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();

        address sender = _msgSender();

        (uint256 nativeBought, uint256 factoryFee, uint256 tradingFee) = _swap(
            tokensSold,
            minimumNativeOut,
            tokenReserve,
            nativeReserve
        );

        accruedTokenTradingFees += tradingFee;
        _handleFactoryFees(factoryFee, false);

        _internalTransfer(sender, address(this), tokensSold);
        _transferNative(sender, nativeBought);

        _updatePrices();
        emit Swap(sender, tokensSold, 0, 0, nativeBought, false);
    }

    /**
     * @dev Executes a flash swap transaction.
     * @param recipient The address of the recipient of the flash swap.
     * @param amountNativeOut The amount of native currency to be sent to the recipient.
     * @param amountTokenOut The amount of tokens to be sent to the recipient.
     * @param data Additional data to be passed to the recipient.
     */
    function flashSwap(
        address recipient,
        uint256 amountNativeOut,
        uint256 amountTokenOut,
        bytes calldata data
    ) external nonReentrant preventAutoSwap {
        if (!isInitialized) revert ContractIsNotInitialized();
        if (!tradingEnabled) revert SwapNotEnabled();

        if (amountNativeOut == 0 && amountTokenOut == 0)
            revert AmountMustBeGreaterThanZero();

        if (recipient == address(0) || recipient == address(this))
            revert InvalidRecipient();

        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();

        if (amountNativeOut > nativeReserve || amountTokenOut > tokenReserve)
            revert InsufficientLiquidity();

        address sender = _msgSender();

        if (amountNativeOut > 0) {
            // Sending native currency
            _transferNative(recipient, amountNativeOut);
        }
        if (amountTokenOut > 0) {
            // Sending token
            _checkMaxWallet(recipient, amountTokenOut);
            super._transfer(address(this), recipient, amountTokenOut);
        }

        IBIFKN314CALLEE(recipient).BIFKN314CALL(
            sender,
            amountNativeOut,
            amountTokenOut,
            data
        );

        (uint256 nativeReserveAfter, uint256 tokenReserveAfter) = getReserves();

        uint amountNativeIn = nativeReserveAfter > nativeReserve
            ? nativeReserveAfter - nativeReserve
            : 0;
        uint amountTokenIn = tokenReserveAfter > tokenReserve
            ? tokenReserveAfter - tokenReserve
            : 0;

        if (amountNativeIn == 0 && amountTokenIn == 0) {
            revert TokenRepaymentFailed();
        }

        {
            uint256 totalFees = FLASHSWAP_FEE_RATE + tradingFeeRate;

            uint256 nativeReserveAdjusted = (nativeReserveAfter *
                SCALE_FACTOR) - (amountNativeIn * totalFees);
            uint256 tokenReserveAdjusted = (tokenReserveAfter * SCALE_FACTOR) -
                (amountTokenIn * totalFees);

            if (
                nativeReserveAdjusted * tokenReserveAdjusted <
                nativeReserve * tokenReserve * (SCALE_FACTOR ** 2)
            ) {
                revert DecreasesK();
            }
        }

        accruedNativeTradingFees += _calculateTradingFee(amountNativeIn);
        accruedTokenTradingFees += _calculateTradingFee(amountTokenIn);

        _handleFactoryFees(_calculateFactoryFee(amountNativeIn), true);
        _handleFactoryFees(_calculateFactoryFee(amountTokenIn), false);

        _updatePrices();

        emit Swap(
            sender,
            amountTokenIn,
            amountNativeIn,
            amountTokenOut,
            amountNativeOut,
            true
        );
    }

    /**
     * @dev Calculates the amount of output tokens based on the input amount and reserves.
     * This accounts for all fees including the factory fee, trading fee, and base swap rate.
     * @param inputAmount The amount of input tokens.
     * @param inputReserve The amount of input tokens in the reserve.
     * @param outputReserve The amount of output tokens in the reserve.
     * @return outputAmount The amount of output tokens.
     * @return factoryFee The amount of factory fee.
     * @return tradingFee The amount of trading fee.
     */
    function getAmountOut(
        uint256 inputAmount,
        uint256 inputReserve,
        uint256 outputReserve
    )
        public
        view
        returns (uint256 outputAmount, uint256 factoryFee, uint256 tradingFee)
    {
        // Scale by 1e4 to avoid rounding errors
        // Since the SCALE_FACTOR is 1e4, the precision total is 1e8
        // This strikes a good balance between risk of overflow and precision
        uint256 precision = 1e4;
        uint256 feeFactor = SCALE_FACTOR - (BASE_SWAP_RATE + tradingFeeRate);
        uint256 inputAmountScaled = inputAmount * precision;
        // if reserves are greater than 0
        if (inputReserve > 0 && outputReserve > 0) {
            factoryFee = _calculateFactoryFee(inputAmountScaled) / precision;
            tradingFee = _calculateTradingFee(inputAmountScaled) / precision;
            uint256 inputAmountWithFee = inputAmountScaled * feeFactor;
            uint256 numerator = inputAmountWithFee * outputReserve;
            uint256 denominator = (inputReserve * SCALE_FACTOR * precision) +
                inputAmountWithFee;
            unchecked {
                outputAmount = numerator / denominator;
            }
        } else {
            revert InvalidReserves();
        }
    }

    /**
     * @dev Calculates the input amount and factory fee based on the output amount, output reserve, and input reserve.
     * This accounts for all fees including the factory fee, trading fee, and base swap rate.
     * @param outputAmount The desired output amount.
     * @param outputReserve The current output reserve.
     * @param inputReserve The current input reserve.
     * @return inputAmount The calculated input amount.
     */
    function getAmountIn(
        uint256 outputAmount,
        uint256 inputReserve,
        uint256 outputReserve
    ) public view returns (uint256 inputAmount) {
        // Scale by 1e4 to avoid rounding errors
        // Since the SCALE_FACTOR is 1e4, the precision total is 1e8
        // This strikes a good balance between risk of overflow and precision
        uint256 precision = 1e4;
        uint256 feeFactor = SCALE_FACTOR - (BASE_SWAP_RATE + tradingFeeRate);
        feeFactor = feeFactor * precision;
        // Ensure reserves are greater than 0
        if (outputReserve > 0 && inputReserve > 0) {
            uint256 numerator = inputReserve *
                outputAmount *
                SCALE_FACTOR *
                precision;
            uint256 denominator = (outputReserve - outputAmount) * feeFactor;
            unchecked {
                inputAmount = (numerator / denominator) + 1;
            }
        } else {
            revert InvalidReserves();
        }
    }

    /**
     * @dev Returns the number of tokens held by the contract.
     * @return tokenBalance The token balance of the contract.
     */
    function getTokensInContract() public view returns (uint256 tokenBalance) {
        tokenBalance = super.balanceOf(address(this));
    }

    /**
     * @dev Returns the reserves of the contract.
     * If the fees are greater than the reserves, the function returns 0 for the respective reserve.
     * @return amountNative The native reserve balance.
     * @return amountToken The token reserve balance.
     */
    function getReserves()
        public
        view
        returns (uint256 amountNative, uint256 amountToken)
    {
        uint256 totalNative = address(this).balance;
        uint256 totalNativeFees = accruedNativeTradingFees +
            accruedNativeFactoryFees;
        uint256 totalToken = getTokensInContract();
        uint256 totalTokenFees = accruedTokenTradingFees +
            accruedTokenFactoryFees;

        amountNative = totalNative >= totalNativeFees
            ? totalNative - totalNativeFees
            : 0;
        amountToken = totalToken >= totalTokenFees
            ? totalToken - totalTokenFees
            : 0;
    }

    /**
     * @dev Gets the amount of tokens held by the liquidity provider.
     * @param amount The amount of liquidity tokens to be converted.
     * @return nativeAmount The amount of native currency held by the liquidity provider.
     * @return tokenAmount The amount of tokens held by the liquidity provider.
     */
    function getAmountsForLP(
        uint256 amount
    ) public view returns (uint256 nativeAmount, uint256 tokenAmount) {
        if (amount == 0) revert AmountMustBeGreaterThanZero();
        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();

        if (nativeReserve == 0 || tokenReserve == 0) revert InvalidReserves();

        uint256 totalLPSupply = liquidityToken.totalSupply();
        if (totalLPSupply == 0) revert InsufficientLiquidity();

        nativeAmount = (amount * nativeReserve) / totalLPSupply;
        tokenAmount = (amount * tokenReserve) / totalLPSupply;

        if (nativeAmount == 0 || tokenAmount == 0)
            revert InsufficientLiquidity();
    }

    /**
     * @dev Enables trading by setting the `tradingEnabled` flag to true.
     * Can only be called by the contract owner.
     * Once trading is enabled, it cannot be disabled.
     */
    function setTradingEnabled() public onlyOwner {
        tradingEnabled = true;
    }

    /**
     * @dev Sets the fee collector address.
     * @param feeCollector_ The address of the fee collector.
     * @notice Only the contract owner can call this function.
     * @notice The fee collector address cannot be set to the zero address.
     */
    function setFeeCollector(address feeCollector_) external onlyOwner {
        if (feeCollector_ == address(0)) revert InvalidAddress();
        feeCollector = feeCollector_;
    }

    /**
     * @dev Sets the fee rate for trading.
     * @param feeRate The new fee rate to be set.
     * Requirements:
     * - `feeRate` must be less than or equal to 50 (5%).
     * Only the contract owner can call this function.
     */
    function setTradingFeeRate(uint256 feeRate) public onlyOwner {
        if (feeRate > MAX_FEE_RATE) revert InvalidFeeRate(); // 5%
        tradingFeeRate = feeRate;
    }

    /**
     * @dev Sets the maximum wallet percentage.
     * @param maxWalletPercent_ The maximum wallet percentage to be set.
     * Requirements:
     * - `maxWalletPercent_` must be less than or equal to 10000 (100%)
     * and greater than 0 if maxWalletEnabled is true.
     * Only the contract owner can call this function.
     */
    function setMaxWalletPercent(uint256 maxWalletPercent_) public onlyOwner {
        if (maxWalletPercent_ > 10000) revert InvalidMaxWalletPercent(); // 100%
        if (maxWalletEnabled && maxWalletPercent_ == 0)
            revert InvalidMaxWalletPercent();
        maxWalletPercent = maxWalletPercent_;
    }

    /**
     * @dev Enables or disables the maximum wallet limit.
     * @param enabled The boolean value to set the maximum wallet limit.
     * Requirements:
     * - Only the contract owner can call this function.
     */
    function setMaxWalletEnabled(bool enabled) public onlyOwner {
        if (enabled && maxWalletPercent == 0) revert InvalidMaxWalletPercent();
        maxWalletEnabled = enabled;
    }

    /**
     * @dev Sets the metadata URI for the token.
     * @param newURI The new metadata URI to be set.
     * Requirements:
     * - Only the contract owner can call this function.
     */
    function setMetadataURI(string memory newURI) public onlyOwner {
        metadataURI = newURI;
    }

    /**
     * @dev Sets the maximum wallet exemption status for a given address.
     * @param addressToChange The address for which the maximum wallet exemption status is to be set.
     * @param isExempt A boolean value indicating whether the address should be exempt from the maximum wallet limit.
     * Only the contract owner can call this function.
     * Requirements:
     * - The address to change cannot be the zero address, the contract address, or the dead address.
     * @notice If the address to change is the zero address, the contract address, or the dead address, the transaction will revert.
     */
    function setMaxWalletExempt(
        address addressToChange,
        bool isExempt
    ) public onlyOwner {
        if (
            addressToChange == address(0) ||
            addressToChange == address(this) ||
            addressToChange == DEAD_ADDRESS
        ) revert InvalidAddress();
        isMaxWalletExempt[addressToChange] = isExempt;
    }

    /**
     * @dev Allows the fee collector to claim accrued trading fees.
     * The function transfers the accrued native currency and token trading fees to the fee collector.
     * The accrued amounts are reset to zero after the transfer.
     * Emits a `FeesCollected` event with the fee collector's address, accrued native amount, and accrued token amount.
     *
     * Requirements:
     * - The caller must be the fee collector.
     */
    function claimFees() external onlyFeeCollector {
        uint256 accruedNativeAmount = accruedNativeTradingFees;
        uint256 accruedTokenAmount = accruedTokenTradingFees;
        address sender = _msgSender();

        if (accruedNativeAmount == 0 && accruedTokenAmount == 0)
            revert NoFeesToClaim();

        accruedNativeTradingFees = 0;

        // If the accrued token amount is greater than the balance of the contract
        // set the accrued token amount to the balance of the contract
        if (accruedTokenAmount > getTokensInContract())
            accruedTokenAmount = getTokensInContract();

        accruedTokenTradingFees = 0;

        _transferNative(sender, accruedNativeAmount);
        super._transfer(address(this), sender, accruedTokenAmount);

        emit FeesCollected(sender, accruedNativeAmount, accruedTokenAmount);
    }

    /**
     * @dev Transfers the ownership of the contract to a new address.
     * Can only be called by the current owner.
     *
     * @param newOwner The address of the new owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        if (newOwner == address(0)) revert InvalidOwner();

        _transferOwnership(newOwner);
    }

    /**
     * @dev Allows the current owner to renounce their ownership.
     * It sets the owner address to 0, effectively removing the ownership.
     */
    function renounceOwnership() external onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers the ownership of the contract to a new address.
     * Can only be called by the current owner.
     *
     * @param newOwner The address of the new owner.
     * @notice Emits an {OwnershipTransferred} event.
     */
    function _transferOwnership(address newOwner) internal {
        address oldOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev Calculates the product of two input values.
     * @param reserve1 The first input value.
     * @param reserve2 The second input value.
     * @return kValue_ The product of the two input values.
     */
    function _calculateKValue(
        uint256 reserve1,
        uint256 reserve2
    ) internal pure returns (uint256 kValue_) {
        kValue_ = reserve1 * reserve2;
    }

    /**
     * @dev Internal function to calculate the trading fee for a given amount.
     * @param amount The amount to apply the fee to.
     * @return amountForFee The amount to be deducted as a trading fee.
     * @notice If the amount is zero, the transaction will revert.
     * @notice If the trading fee rate is zero, the function will return zero.
     * @notice If the trading fee rate is 500, the function will return 5% of the amount.
     */
    function _calculateTradingFee(
        uint256 amount
    ) internal view returns (uint256 amountForFee) {
        // If the trading fee rate is 0, return 0
        if (tradingFeeRate == 0) amountForFee = 0;
        else {
            amountForFee = (amount * tradingFeeRate) / SCALE_FACTOR;
        }
    }

    /**
     * @dev Calculates the factory fee based on the input amount.
     * @param inputAmount The input amount for which the factory fee needs to be calculated.
     * @return amountForFee The amount to be deducted as a factory fee.
     * @notice If the input amount is zero, the transaction will revert.
     * @notice If the factory contract is not set, the function will return zero.
     */
    function _calculateFactoryFee(
        uint256 inputAmount
    ) internal view returns (uint256 amountForFee) {
        if (address(factory) == address(0)) {
            amountForFee = 0;
        } else {
            amountForFee = (inputAmount * factory.feeRate()) / SCALE_FACTOR;
        }
    }

    /**
     * @dev Checks if the recipient's wallet balance exceeds the maximum allowed amount.
     * @param recipient The address of the recipient.
     * @param amount The amount to be transferred.
     * @notice If the max wallet limit is exceeded, the transaction will revert.
     */
    function _checkMaxWallet(address recipient, uint256 amount) internal view {
        if (!maxWalletEnabled) return; // Skip if max wallet is not enabled
        // Only apply the max wallet check if the recipient is not (this) contract, address(0), or the dead address
        // and if the recipient is not exempt from the max wallet limit
        if (
            recipient == address(this) ||
            recipient == address(0) ||
            recipient == DEAD_ADDRESS ||
            isMaxWalletExempt[recipient]
        ) {
            return;
        }

        uint256 maxWalletAmount = ((totalSupply() * maxWalletPercent) / 10000);
        if (balanceOf(recipient) + amount > maxWalletAmount) {
            revert MaxWalletAmountExceeded();
        }
    }

    /**
     * @dev Internal function to check for swap errors.
     * @param tokensSold The number of tokens sold in the swap.
     * @param nativeReserve The native reserve balance.
     * @param tokenReserve The token reserve balance.
     * @notice If the contract is not initialized, the transaction will revert.
     * @notice If the reserves are invalid, the transaction will revert.
     * @notice If the swap is not enabled, the transaction will revert.
     * @notice If the amount of tokens sold is zero, the transaction will revert.
     */
    function _checkForSwapErrors(
        uint256 tokensSold,
        uint256 nativeReserve,
        uint256 tokenReserve
    ) internal view {
        if (!isInitialized) revert ContractIsNotInitialized();
        if (!tradingEnabled) revert SwapNotEnabled();
        if (tokensSold == 0) {
            revert AmountMustBeGreaterThanZero();
        }
        if (nativeReserve == 0 || tokenReserve == 0) revert InvalidReserves();
    }

    /**
     * @dev Performs a swap operation between two reserves.
     * @param amountIn The amount of tokens being swapped in.
     * @param minimumAmountOut The minimum amount of tokens expected to be received.
     * @param reserveIn The reserve of the input token.
     * @param reserveOut The reserve of the output token.
     * @return amountOut The amount of tokens received after the swap.
     * @return factoryFee The fee charged by the factory for the swap.
     * @return tradingFee The fee charged for the swap.
     */
    function _swap(
        uint256 amountIn,
        uint256 minimumAmountOut,
        uint256 reserveIn,
        uint256 reserveOut
    )
        internal
        view
        returns (uint256 amountOut, uint256 factoryFee, uint256 tradingFee)
    {
        _checkForSwapErrors(amountIn, reserveIn, reserveOut);

        uint256 currentKValue = _calculateKValue(reserveIn, reserveOut);

        (amountOut, factoryFee, tradingFee) = getAmountOut(
            amountIn,
            reserveIn,
            reserveOut
        );

        if (amountOut == 0) revert BoughtAmountTooLow();
        if (amountOut < minimumAmountOut) revert SlippageToleranceExceeded();

        uint256 newReserveIn = reserveIn + (amountIn - tradingFee - factoryFee);

        uint256 newReserveOut = reserveOut - amountOut;

        if (_calculateKValue(newReserveIn, newReserveOut) < currentKValue)
            revert DecreasesK();
    }

    /**
     * @dev Calculates the cumulative prices based on the provided native and token reserves.
     */
    function _updatePrices() private {
        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();

        if (nativeReserve == 0 || tokenReserve == 0) revert InvalidReserves();

        uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // Overflow is desired

        if (timeElapsed > 0 && nativeReserve != 0 && tokenReserve != 0) {
            // Simulate fixed-point precision using a scaling factor
            uint256 scalingFactor = 2 ** 112;

            // Calculate price ratios with scaling to simulate UQ112x112 precision
            // Reflects the price of token in native currency
            uint256 price0Ratio = (nativeReserve * scalingFactor) /
                tokenReserve;
            // Reflects the price of native currency in token
            uint256 price1Ratio = (tokenReserve * scalingFactor) /
                nativeReserve;

            // Update cumulative prices
            price0CumulativeLast += price0Ratio * timeElapsed;
            price1CumulativeLast += price1Ratio * timeElapsed;

            // Update last block timestamp
            blockTimestampLast = blockTimestamp;
        }

        emit PricesUpdated(
            price0CumulativeLast,
            price1CumulativeLast,
            blockTimestampLast
        );
    }

    /**
     * @dev Accrues fees to the contract.
     * @param factoryFee The amount of fees to be accrued.
     * @param native A boolean value indicating whether the fee is in native currency or not.
     */
    function _handleFactoryFees(uint256 factoryFee, bool native) internal {
        // Check if the factory contract is set
        if (address(factory) != address(0)) {
            address feeTo = factory.feeTo();
            uint256 distributionThreshold = factory.feeDistributionThreshold();

            // Accrue fees and distribute if threshold is reached
            if (feeTo != address(0)) {
                if (native) {
                    accruedNativeFactoryFees += factoryFee;
                } else {
                    accruedTokenFactoryFees += factoryFee;
                }

                _distributeFees(feeTo, distributionThreshold);
            }
        }
    }

    /**
     * @dev Distributes fees to a specified address if the distribution threshold is reached.
     * @param feeTo The address to which the fees will be distributed.
     * @param distributionThreshold The threshold at which fees will be distributed.
     */
    function _distributeFees(
        address feeTo,
        uint256 distributionThreshold
    ) internal {
        uint256 nativeFees = accruedNativeFactoryFees;
        uint256 tokenFees = accruedTokenFactoryFees;
        bool nativeDistributed = false;
        bool tokenDistributed = false;

        // Only distribute fees if either the native or token fees are greater than 0
        if (nativeFees == 0 && tokenFees == 0) return;

        // Distribute native fees if threshold is reached
        if (nativeFees > 0 && nativeFees >= distributionThreshold) {
            accruedNativeFactoryFees = 0;
            nativeDistributed = true;
        }

        // Distribute token fees if threshold is reached
        if (tokenFees > 0) {
            (uint256 nativeReserve, uint256 tokenReserve) = getReserves();

            uint256 nativeAmount = (tokenFees * nativeReserve) / tokenReserve;

            if (nativeAmount >= distributionThreshold) {
                if (tokenFees > getTokensInContract()) {
                    tokenFees = getTokensInContract();
                }
                accruedTokenFactoryFees = 0;
                tokenDistributed = true;
            }
        }

        if (nativeDistributed) _transferNative(feeTo, nativeFees);
        if (tokenDistributed) super._transfer(address(this), feeTo, tokenFees);

        // Emit event if fees are distributed
        if (nativeDistributed || tokenDistributed)
            emit FeesDistributed(feeTo, nativeFees, tokenFees);
    }

    /**
     * @dev Internal function to transfer native currency to a specified address.
     * @param to The address to transfer the native currency to.
     * @param amount The amount of native currency to transfer.
     * @notice If the transfer fails, the transaction will revert.
     */
    function _transferNative(address to, uint256 amount) internal {
        if (amount == 0) return;
        if (to == address(0)) revert InvalidAddress();

        if (amount > address(this).balance) {
            amount = address(this).balance;
        }
        (bool success, ) = payable(to).call{value: amount}("");
        if (!success) revert FailedToSendNativeCurrency();
    }

    /**
     * @dev Checks if the given address is a contract.
     * @param addressToCheck The address to check.
     * @return result A boolean value indicating whether the address is a contract.
     */
    function _isContract(
        address addressToCheck
    ) internal view returns (bool result) {
        uint32 size;
        // Inline assembly code to get the size of the code at _address
        assembly {
            size := extcodesize(addressToCheck)
        }
        // If size > 0, it's a contract
        result = (size > 0);
    }

    /**
     * @dev Calculates the fee based on the given amount.
     * @param amount The amount for which the fee needs to be calculated.
     * @return flashswapFee The calculated fee.
     */
    function _calculateFlashswapFee(
        uint256 amount
    ) internal pure returns (uint256 flashswapFee) {
        flashswapFee = (amount * FLASHSWAP_FEE_RATE) / SCALE_FACTOR; // Fee calculation
    }

    /**
     * @dev Calculates the minimum amount out with slippage for an auto swap.
     * @param amount The input amount.
     * @param isNative A boolean indicating whether the input is in the native token or not.
     * @return amountOutMin The minimum amount out with slippage.
     */
    function _calculateAutoSwapSlippage(
        uint256 amount,
        bool isNative
    ) internal view returns (uint256 amountOutMin) {
        (uint256 nativeReserve, uint256 tokenReserve) = getReserves();
        (uint256 amountOut, , ) = getAmountOut(
            amount,
            isNative ? nativeReserve : tokenReserve,
            isNative ? tokenReserve : nativeReserve
        );
        amountOutMin = amountOut - (amountOut / 20); // 5% slippage
    }

    // Function to receive native
    /**
     * @dev Fallback function to receive native currency.
     * It calls the `swapNativeToToken` function with a minimum token out amount of 0 (i.e. infinite slippage).
     */
    receive() external payable {
        if (!_autoSwapIsPrevented()) {
            swapNativeToToken(
                _calculateAutoSwapSlippage(msg.value, true),
                block.timestamp + 3 minutes
            );
        }
    }
}

File 2 of 16 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

File 3 of 16 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
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);
}

File 4 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 5 of 16 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 6 of 16 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    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 subtraction of two unsigned integers, with an overflow flag.
     */
    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.
     */
    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.
     */
    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.
     */
    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 largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    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 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                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.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            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.

            uint256 twos = denominator & (0 - denominator);
            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;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    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);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 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;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 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;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    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;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 7 of 16 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 8 of 16 : BIFKN314LP.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import "./BIFKNERC20.sol";

/**
 * @title BIFKN314LP
 * @dev Implementation of the liquidity provider (LP) token for the BIFKN314 AMM pool.
 */
contract BIFKN314LP is BIFKNERC20 {
    /**
     * @dev The address of the Automated Market Maker (AMM) contract.
     */
    address public immutable ammAddress;

    error Unauthorized(address sender);

    /**
     * @dev Modifier that allows only the owner (amm) to call the function.
     * If the caller is not the owner, it will revert with an `OnlyOwnerError` error.
     */
    modifier onlyOwner() {
        if (_msgSender() != ammAddress) revert Unauthorized(_msgSender());
        _;
    }

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor() BIFKNERC20() {
        ammAddress = _msgSender();
    }

    /**
     * @dev Initializes the contract with the given name and symbol.
     *
     * This function is called by the contract owner to initialize the contract.
     * It sets the name and symbol of the contract by calling the `initialize` function
     * of the parent contract.
     *
     * @param tokenName The name of the contract.
     * @param tokenSymbol The symbol of the contract.
     */
    function initialize(
        string memory tokenName,
        string memory tokenSymbol
    ) public override onlyOwner {
        super.initialize(tokenName, tokenSymbol);
    }

    /**
     * @dev Function to mint tokens
     *
     * Requirements:
     * - the caller must be the BIFKN314 contract.
     *
     * @param account The address that will receive the minted tokens.
     * @param amount The amount of tokens to mint.
     */
    function mint(address account, uint256 amount) public onlyOwner {
        super._mint(account, amount);
    }
}

File 9 of 16 : BIFKNERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import "./ERC20.sol";

/**
 * @title BIFKNERC20
 * @dev This contract represents the BIFKNERC20 token.
 */
contract BIFKNERC20 is ERC20 {
    /**
     * @dev The `DOMAIN_SEPARATOR` is a unique identifier for the contract domain.
     * It is used to prevent replay attacks and to ensure that the contract is interacting with the correct domain.
     */
    bytes32 public DOMAIN_SEPARATOR;

    /**
     * @dev The hash of the permit type used in the EIP-2612 permit function.
     */
    bytes32 public constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /**
     * @dev A mapping that stores the nonces for each address.
     * Nonces are used to prevent replay attacks in certain operations.
     * The key of the mapping is the address and the value is the nonce.
     */
    mapping(address => uint256) public nonces;

    /**
     * @dev Error indicating that the name and symbol must not be empty.
     */
    error NameAndSymbolMustNotBeEmpty();

    /**
     * @dev Error indicating that the name and symbol of the ERC20 token have already been set.
     */
    error NameAndSymbolAlreadySet();

    /**
     * @dev Error indicating that the signature has expired for ERC2612.
     * @param deadline The timestamp representing the expiration deadline.
     */
    error ERC2612ExpiredSignature(uint256 deadline);
    /**
     * @dev Error indicating that the signer is invalid.
     * @param signer The address of the invalid signer.
     * @param owner The address of the token owner.
     */
    error ERC2612InvalidSigner(address signer, address owner);

    /**
     * @dev Constructor function for the BIFKNERC20 contract.
     * It initializes the ERC20 contract and the EIP712 contract.
     * It also sets the DOMAIN_SEPARATOR variable using the _domainSeparatorV4 function.
     */
    constructor() ERC20() {}

    /**
     * @dev Initializes the ERC20 token with the given name and symbol.
     * @param tokenName The name of the token.
     * @param tokenSymbol The symbol of the token.
     */
    function initialize(
        string memory tokenName,
        string memory tokenSymbol
    ) public virtual {
        if (bytes(tokenName).length == 0 || bytes(tokenSymbol).length == 0) {
            revert NameAndSymbolMustNotBeEmpty();
        }
        if (bytes(_name).length != 0 || bytes(_symbol).length != 0) {
            revert NameAndSymbolAlreadySet();
        }
        _name = tokenName;
        _symbol = tokenSymbol;

        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes(_name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
    }

    /**
     * @dev Allows `owner` to approve `spender` to spend `value` tokens on their behalf using a signed permit.
     * @param owner The address of the token owner.
     * @param spender The address of the spender.
     * @param value The amount of tokens to be approved.
     * @param deadline The deadline timestamp for the permit.
     * @param v The recovery id of the permit signature.
     * @param r The r value of the permit signature.
     * @param s The s value of the permit signature.
     * Requirements:
     * - The permit must not be expired (deadline not reached).
     * - The permit signature must be valid.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        if (deadline < block.timestamp) {
            revert ERC2612ExpiredSignature(deadline);
        }
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        PERMIT_TYPEHASH,
                        owner,
                        spender,
                        value,
                        nonces[owner]++,
                        deadline
                    )
                )
            )
        );
        address recoveredAddress = ecrecover(digest, v, r, s);
        if (recoveredAddress == address(0) || recoveredAddress != owner) {
            revert ERC2612InvalidSigner(recoveredAddress, owner);
        }

        _approve(owner, spender, value);
    }

    /**
     * @dev Burns a specific amount of tokens from the caller's balance.
     * @param value The amount of tokens to be burned.
     */
    function burn(uint256 value) public virtual {
        _burn(_msgSender(), value);
    }

    /**
     * @dev Burns a specific amount of tokens from the specified account.
     *
     * Requirements:
     * - The caller must have an allowance for `account`'s tokens of at least `value`.
     *
     * Emits a {Transfer} event with `from` set to `account`.
     */
    function burnFrom(address account, uint256 value) public virtual {
        _spendAllowance(account, _msgSender(), value);
        _burn(account, value);
    }
}

File 10 of 16 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity 0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.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}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * 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.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256))
        private _allowances;

    uint256 private _totalSupply;

    string internal _name;
    string internal _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor() {}

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual 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 default value returned by this function, unless
     * it's 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 returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(
        address owner,
        address spender
    ) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(
        address spender,
        uint256 value
    ) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        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}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 value
    ) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * 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.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` 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.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(
        address owner,
        address spender,
        uint256 value,
        bool emitEvent
    ) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 value
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(
                    spender,
                    currentAllowance,
                    value
                );
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

File 11 of 16 : IBIFKN314CALLEE.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IBIFKN314CALLEE {
    function BIFKN314CALL(
        address sender,
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external;
}

File 12 of 16 : IBIFKN314Factory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IBIFKN314Factory {
    function feeTo() external view returns (address);

    function feeRate() external view returns (uint256);

    function feeToSetter() external view returns (address);

    function feeDistributionThreshold() external view returns (uint256);

    event TokenCreated(
        address indexed deployer,
        string name,
        string symbol,
        address ammAddress,
        address lpAddress,
        uint256 allAMMLength
    );

    event FeeDistributed(address indexed feeTo, uint256 nativeAmount);

    error InvalidAddress();
    error NameMustNotBeEmpty();
    error SymbolMustNotBeEmpty();
    error NameTooLong();
    error SymbolTooLong();
    error OnlyFeeToSetter(address sender);
    error InvalidTradingFee();
    error SupplyMustBeGreaterThanZero();
    error InsufficientDeploymentFee();
    error InvalidFeeRate();
    error InvalidMaxWalletPercent();
    error DistributionFailed();
    error ImplementationAlreadyDeployed();
}

File 13 of 16 : IERC314.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IERC314 {
    function addLiquidity(
        uint256 amountToken,
        address recipient,
        uint256 deadline
    ) external payable returns (uint256 liquidity);

    function removeLiquidity(
        uint256 amount,
        address recipient,
        uint256 deadline
    ) external returns (uint256 nativeAmount, uint256 tokenAmount);

    function swapNativeToToken(
        uint256 minimumTokensOut,
        uint256 deadline
    ) external payable;

    function swapTokenToNative(
        uint256 tokensSold,
        uint256 minimumNativeOut,
        uint256 deadline
    ) external;

    // function flashSwap(
    //     address recipient,
    //     uint256 amountNativeOut,
    //     uint256 amountTokenOut,
    //     bytes calldata data
    // ) external;

    function getAmountOut(
        uint256 inputAmount,
        uint256 inputReserve,
        uint256 outputReserve
    )
        external
        view
        returns (uint256 outputAmount, uint256 factoryFee, uint256 tradingFee);

    function getAmountIn(
        uint256 outputAmount,
        uint256 inputReserve,
        uint256 outputReserve
    ) external view returns (uint256 inputAmount);

    function getTokensInContract() external view returns (uint256);

    function getReserves()
        external
        view
        returns (uint256 amountNative, uint256 amountERC20);

    function getAmountsForLP(
        uint256 amount
    ) external view returns (uint256 nativeAmount, uint256 tokenAmount);

    function setFeeCollector(address feeCollector) external;

    function setTradingFeeRate(uint256 feeRate) external;

    function claimFees() external;

    function transferOwnership(address newOwner) external;

    function renounceOwnership() external;
}

File 14 of 16 : IERC314Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IERC314Errors {
    error AmountOfTokensLessThanMinimumRequired(
        uint256 amount,
        uint256 minimumAmount
    );
    error AmountMustBeGreaterThanZero();
    error YouHaveNoLiquidity();
    error InsufficientLiquidity();
    error InvalidReserves();
    error ContractIsNotInitialized();
    error InsufficientLiquidityMinted();
    error SwapNotEnabled();
    error DecreasesK();
    error TransactionExpired();
    error SlippageToleranceExceeded();
    error InvalidRecipient();
    error FailedToSendNativeCurrency();
    error NativeRepaymentFailed();
    error TokenRepaymentFailed();
    error Unauthorized(address sender);
    error SupplyAlreadyMinted();
    error InvalidOwner();
    error InvalidAddress();
    error InvalidFeeRate();
    error BoughtAmountTooLow();
    error NoFeesToClaim();
    error InvalidMaxWalletPercent();
    error MaxWalletAmountExceeded();
    error OwnerAlreadyInitialized();
}

File 15 of 16 : IERC314Events.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface IERC314Events {
    event AddLiquidity(
        address indexed provider,
        address indexed toAddress,
        uint256 liquidityMinted,
        uint256 nativeAmount,
        uint256 tokenAmount
    );
    event RemoveLiquidity(
        address indexed provider,
        address indexed toAddress,
        uint256 liquidityBurned,
        uint256 nativeAmount,
        uint256 tokenAmount
    );
    event Swap(
        address indexed sender,
        uint256 amountTokenIn,
        uint256 amountNativeIn,
        uint256 amountTokenOut,
        uint256 amountNativeOut,
        bool flashSwap
    );
    event PricesUpdated(
        uint256 tokenPriceInNative,
        uint256 nativePriceInToken,
        uint32 blockTimestampLast
    );
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );
    event FeesCollected(
        address indexed recipient,
        uint256 amountNative,
        uint256 amountToken
    );
    event FeesDistributed(
        address indexed feeTo,
        uint256 nativeAmount,
        uint256 tokenAmount
    );
}

File 16 of 16 : PreventAutoSwap.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @dev Contract module that helps prevent automatic swapping within a function call
 * for specific callers, allowing for finer control over when swapping should be prevented.
 *
 * This version uses a mapping to track the prevention status for each caller,
 * making it context-sensitive and allowing for certain operations to not affect others.
 */
abstract contract PreventAutoSwap {
    mapping(address => bool) private _autoSwapPreventedFor;

    /**
     * @dev Thrown when an operation tries to perform an auto-swap and it is prevented for the caller.
     */
    error AutoSwapPrevented();

    /**
     * @dev Prevents auto-swap for the caller of the function this modifier is applied to.
     * This approach allows differentiating between various operations and callers,
     * giving more control over the swapping mechanism.
     */
    modifier preventAutoSwap() {
        _preventAutoSwapBefore();
        _;
        _preventAutoSwapAfter();
    }

    /**
     * @dev Prevents automatic swapping before executing a transaction.
     * If the msg.sender has already prevented auto swapping, it reverts with an `AutoSwapPrevented` error.
     * Otherwise, it marks the transaction origin as prevented for auto swapping.
     */
    function _preventAutoSwapBefore() private {
        if (_autoSwapPreventedFor[msg.sender]) {
            revert AutoSwapPrevented();
        }
        _autoSwapPreventedFor[msg.sender] = true;
    }

    /**
     * @dev Internal function to prevent auto swap after a transaction.
     * @notice This function sets the `_autoSwapPreventedFor` mapping value for the `msg.sender` address to `false`.
     * @notice Auto swap refers to an automatic swapping of tokens that may occur during a transaction.
     * @notice By calling this function, the auto swap is prevented for the `msg.sender` address.
     * @notice This function is private and can only be called from within the contract.
     */
    function _preventAutoSwapAfter() private {
        _autoSwapPreventedFor[msg.sender] = false;
    }

    /**
     * @dev Returns true if auto swap is currently prevented for the caller.
     */
    function _autoSwapIsPrevented() internal view returns (bool) {
        return _autoSwapPreventedFor[msg.sender];
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 50
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountMustBeGreaterThanZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minimumAmount","type":"uint256"}],"name":"AmountOfTokensLessThanMinimumRequired","type":"error"},{"inputs":[],"name":"AutoSwapPrevented","type":"error"},{"inputs":[],"name":"BoughtAmountTooLow","type":"error"},{"inputs":[],"name":"ContractIsNotInitialized","type":"error"},{"inputs":[],"name":"DecreasesK","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"ERC2612ExpiredSignature","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC2612InvalidSigner","type":"error"},{"inputs":[],"name":"FailedToSendNativeCurrency","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientLiquidityMinted","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidFeeRate","type":"error"},{"inputs":[],"name":"InvalidMaxWalletPercent","type":"error"},{"inputs":[],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"MaxWalletAmountExceeded","type":"error"},{"inputs":[],"name":"NameAndSymbolAlreadySet","type":"error"},{"inputs":[],"name":"NameAndSymbolMustNotBeEmpty","type":"error"},{"inputs":[],"name":"NativeRepaymentFailed","type":"error"},{"inputs":[],"name":"NoFeesToClaim","type":"error"},{"inputs":[],"name":"OwnerAlreadyInitialized","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"SlippageToleranceExceeded","type":"error"},{"inputs":[],"name":"SupplyAlreadyMinted","type":"error"},{"inputs":[],"name":"SwapNotEnabled","type":"error"},{"inputs":[],"name":"TokenRepaymentFailed","type":"error"},{"inputs":[],"name":"TransactionExpired","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"YouHaveNoLiquidity","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidityMinted","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountNative","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountToken","type":"uint256"}],"name":"FeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"nativeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"FeesDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenPriceInNative","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativePriceInToken","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"blockTimestampLast","type":"uint32"}],"name":"PricesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"liquidityBurned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountTokenIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountNativeIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountTokenOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountNativeOut","type":"uint256"},{"indexed":false,"internalType":"bool","name":"flashSwap","type":"bool"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"BASE_SWAP_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FLASHSWAP_FEE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SCALE_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedNativeFactoryFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedNativeTradingFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedTokenFactoryFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedTokenTradingFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToken_","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blockTimestampLast","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IBIFKN314Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountNativeOut","type":"uint256"},{"internalType":"uint256","name":"amountTokenOut","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"uint256","name":"inputReserve","type":"uint256"},{"internalType":"uint256","name":"outputReserve","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"uint256","name":"inputReserve","type":"uint256"},{"internalType":"uint256","name":"outputReserve","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"outputAmount","type":"uint256"},{"internalType":"uint256","name":"factoryFee","type":"uint256"},{"internalType":"uint256","name":"tradingFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getAmountsForLP","outputs":[{"internalType":"uint256","name":"nativeAmount","type":"uint256"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint256","name":"amountNative","type":"uint256"},{"internalType":"uint256","name":"amountToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokensInContract","outputs":[{"internalType":"uint256","name":"tokenBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"initializeFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isMaxWalletExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityToken","outputs":[{"internalType":"contract BIFKN314LP","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxWalletEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxWalletPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"price0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"nativeAmount","type":"uint256"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feeCollector_","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"setMaxWalletEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addressToChange","type":"address"},{"internalType":"bool","name":"isExempt","type":"bool"}],"name":"setMaxWalletExempt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxWalletPercent_","type":"uint256"}],"name":"setMaxWalletPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalSupply_","type":"uint256"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"feeRate_","type":"uint256"},{"internalType":"uint256","name":"maxWalletPercent_","type":"uint256"},{"internalType":"string","name":"metadataURI_","type":"string"}],"name":"setSupplyAndMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setTradingEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeRate","type":"uint256"}],"name":"setTradingFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minimumTokensOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapNativeToToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokensSold","type":"uint256"},{"internalType":"uint256","name":"minimumNativeOut","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokenToNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradingFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.