ETH Price: $1,975.50 (+0.08%)

Transaction Decoder

Block:
23567787 at Oct-13-2025 09:05:11 AM +UTC
Transaction Fee:
0.00003818435830657 ETH $0.08
Gas Used:
26,659 Gas / 1.43232523 Gwei

Emitted Events:

Account State Difference:

  Address   Before After State Difference Code
0x3230F230...B058865e9
0.003375789378808411 Eth
Nonce: 2717
0.003337605020501841 Eth
Nonce: 2718
0.00003818435830657
0xD8A2f109...d9D1929Fd
(BuilderNet)
348.113481732544171553 Eth348.113510790854171553 Eth0.00002905831

Execution Trace

Token.approve( spender=0x000000000022D473030F116dDEE9F6B43aC78BA3, value=0 ) => ( True )
  • Token.approve( spender=0x000000000022D473030F116dDEE9F6B43aC78BA3, value=0 ) => ( True )
    File 1 of 2: Token
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
        struct OwnableStorage {
            address _owner;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
        function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
            assembly {
                $.slot := OwnableStorageLocation
            }
        }
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        function __Ownable_init(address initialOwner) internal onlyInitializing {
            __Ownable_init_unchained(initialOwner);
        }
        function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            OwnableStorage storage $ = _getOwnableStorage();
            return $._owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            OwnableStorage storage $ = _getOwnableStorage();
            address oldOwner = $._owner;
            $._owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```solidity
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     *
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Storage of the initializable contract.
         *
         * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
         * when using with upgradeable contracts.
         *
         * @custom:storage-location erc7201:openzeppelin.storage.Initializable
         */
        struct InitializableStorage {
            /**
             * @dev Indicates that the contract has been initialized.
             */
            uint64 _initialized;
            /**
             * @dev Indicates that the contract is in the process of being initialized.
             */
            bool _initializing;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
        /**
         * @dev The contract is already initialized.
         */
        error InvalidInitialization();
        /**
         * @dev The contract is not initializing.
         */
        error NotInitializing();
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint64 version);
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts.
         *
         * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
         * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
         * production.
         *
         * Emits an {Initialized} event.
         */
        modifier initializer() {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            // Cache values to avoid duplicated sloads
            bool isTopLevelCall = !$._initializing;
            uint64 initialized = $._initialized;
            // Allowed calls:
            // - initialSetup: the contract is not in the initializing state and no previous version was
            //                 initialized
            // - construction: the contract is initialized at version 1 (no reininitialization) and the
            //                 current contract is just being deployed
            bool initialSetup = initialized == 0 && isTopLevelCall;
            bool construction = initialized == 1 && address(this).code.length == 0;
            if (!initialSetup && !construction) {
                revert InvalidInitialization();
            }
            $._initialized = 1;
            if (isTopLevelCall) {
                $._initializing = true;
            }
            _;
            if (isTopLevelCall) {
                $._initializing = false;
                emit Initialized(1);
            }
        }
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * A reinitializer may be used after the original initialization step. This is essential to configure modules that
         * are added through upgrades and that require initialization.
         *
         * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
         * cannot be nested. If one is invoked in the context of another, execution will revert.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         *
         * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
         *
         * Emits an {Initialized} event.
         */
        modifier reinitializer(uint64 version) {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            if ($._initializing || $._initialized >= version) {
                revert InvalidInitialization();
            }
            $._initialized = version;
            $._initializing = true;
            _;
            $._initializing = false;
            emit Initialized(version);
        }
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            _checkInitializing();
            _;
        }
        /**
         * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
         */
        function _checkInitializing() internal view virtual {
            if (!_isInitializing()) {
                revert NotInitializing();
            }
        }
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         *
         * Emits an {Initialized} event the first time it is successfully executed.
         */
        function _disableInitializers() internal virtual {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            if ($._initializing) {
                revert InvalidInitialization();
            }
            if ($._initialized != type(uint64).max) {
                $._initialized = type(uint64).max;
                emit Initialized(type(uint64).max);
            }
        }
        /**
         * @dev Returns the highest version that has been initialized. See {reinitializer}.
         */
        function _getInitializedVersion() internal view returns (uint64) {
            return _getInitializableStorage()._initialized;
        }
        /**
         * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
         */
        function _isInitializing() internal view returns (bool) {
            return _getInitializableStorage()._initializing;
        }
        /**
         * @dev Returns a pointer to the storage namespace.
         */
        // solhint-disable-next-line var-name-mixedcase
        function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
            assembly {
                $.slot := INITIALIZABLE_STORAGE
            }
        }
    }
    // 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 {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
    import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
    import {Initializable} from "../../proxy/utils/Initializable.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 ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
        /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
        struct ERC20Storage {
            mapping(address account => uint256) _balances;
            mapping(address account => mapping(address spender => uint256)) _allowances;
            uint256 _totalSupply;
            string _name;
            string _symbol;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
        function _getERC20Storage() private pure returns (ERC20Storage storage $) {
            assembly {
                $.slot := ERC20StorageLocation
            }
        }
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
            __ERC20_init_unchained(name_, symbol_);
        }
        function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
            ERC20Storage storage $ = _getERC20Storage();
            $._name = name_;
            $._symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            ERC20Storage storage $ = _getERC20Storage();
            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) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual returns (uint256) {
            ERC20Storage storage $ = _getERC20Storage();
            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) {
            ERC20Storage storage $ = _getERC20Storage();
            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 {
            ERC20Storage storage $ = _getERC20Storage();
            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 {
            ERC20Storage storage $ = _getERC20Storage();
            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);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @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 ContextUpgradeable is Initializable {
        function __Context_init() internal onlyInitializing {
        }
        function __Context_init_unchained() internal onlyInitializing {
        }
        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;
        }
    }
    // 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);
    }
    // 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);
    }
    // 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);
    }
    pragma solidity >=0.5.0;
    interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);
        function createPair(address tokenA, address tokenB) external returns (address pair);
        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    }
    pragma solidity >=0.6.2;
    interface IUniswapV2Router01 {
        function factory() external pure returns (address);
        function WETH() external pure returns (address);
        function addLiquidity(
            address tokenA,
            address tokenB,
            uint amountADesired,
            uint amountBDesired,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB, uint liquidity);
        function addLiquidityETH(
            address token,
            uint amountTokenDesired,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
        function removeLiquidity(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB);
        function removeLiquidityETH(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external returns (uint amountToken, uint amountETH);
        function removeLiquidityWithPermit(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountA, uint amountB);
        function removeLiquidityETHWithPermit(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountToken, uint amountETH);
        function swapExactTokensForTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external returns (uint[] memory amounts);
        function swapTokensForExactTokens(
            uint amountOut,
            uint amountInMax,
            address[] calldata path,
            address to,
            uint deadline
        ) external returns (uint[] memory amounts);
        function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
            external
            payable
            returns (uint[] memory amounts);
        function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
            external
            returns (uint[] memory amounts);
        function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
            external
            returns (uint[] memory amounts);
        function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
            external
            payable
            returns (uint[] memory amounts);
        function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
        function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
        function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
        function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
        function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
    }
    pragma solidity >=0.6.2;
    import './IUniswapV2Router01.sol';
    interface IUniswapV2Router02 is IUniswapV2Router01 {
        function removeLiquidityETHSupportingFeeOnTransferTokens(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external returns (uint amountETH);
        function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountETH);
        function swapExactTokensForTokensSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
        function swapExactETHForTokensSupportingFeeOnTransferTokens(
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external payable;
        function swapExactTokensForETHSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
    }
    // SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    interface IDataStoreResponse {
        struct DataStoreAddressResponse {
            address locker;
            address payable proofWallet;
            address payable proofStaking;
            address proofPassNFT;
            address router;
        }
        struct DataStoreLimitsResponse {
            uint initMaxTx;
            uint swapTokensAtAmount;
            uint maxTxUpper;
            uint maxTxLower;
            uint maxWalletUpper;
            uint maxWalletLower;
            uint maxBuyFee;
            uint maxSellFee;
            uint denominator;
        }
    }
    interface IDataStore is IDataStoreResponse {
        function getAddresses(string[] memory addrKeys) external view returns (address[] memory);
        function getUints(string[] memory uintKeys) external view returns (uint256[] memory);
        function getPlatformAddresses() external view returns (DataStoreAddressResponse memory);
        function getLimits() external view returns (DataStoreLimitsResponse memory);
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    interface IERC721A {
        function totalSupply() external returns (uint256);
        function ownerOf(uint256) external returns (address);
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    interface ITeamFinanceLocker {
        function lockToken(
            address _tokenAddress,
            address _withdrawalAddress,
            uint256 _amount,
            uint256 _unlockTime,
            bool _mintNFT, 
            address referrer
        ) external payable returns (uint256 _id);
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    import "./IDataStore.sol";
    interface ITokenWhitelist is IDataStoreResponse {
        
        struct TokenInfo {
            string name;
            string symbol;
            address owner;
            address mainWallet;
            address secondaryWallet;
            uint256 totalSupply;
            uint256 percentToLP;
            uint256 lpLockDuration;
            uint256 initMaxWallet;
            FeeInfo buyFees;
            FeeInfo sellFees;
            address[] whitelist;
            uint256 whitelistDuration;
            address rewardToken;
        }
        struct FeeInfo {
            uint256 main;
            uint256 secondary;
            uint256 liquidity;
            uint256 proof;
            uint256 total;
        }
        error ExceedsMaxTxAmount();
        error ExceedsMaxWalletAmount();
        error InvalidConfiguration();
        error TradingNotEnabled();
        error NotWhitelisted();
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
    import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
    import "./interfaces/IERC721A.sol";
    import "./interfaces/ITeamFinanceLocker.sol";
    import "./interfaces/ITokenWhitelist.sol";
    /// This token was incubated and launched by PROOF: https://proofplatform.io/projects. The smart contract is audited by SourceHat: https://sourcehat.com/
    contract Token is ITokenWhitelist, Initializable, ERC20Upgradeable, OwnableUpgradeable {
        
        struct UserInfo {
            bool isFeeExempt;
            bool isTxLimitExempt;
            bool isWhitelisted;
        }
        IUniswapV2Router02 public uniswapV2Router;
        address public pair;
        address payable public mainWallet;
        address payable public secondaryWallet;
        IERC721A public proofPassNFT;
        bool public isWhitelistActive;
        uint256 public whitelistEndTime;
        uint256 public whitelistDuration;
        uint256 public launchedAt;
        uint256 public maxTxAmount;
        uint256 public maxWallet;
        uint256 public initMaxWallet;
        bool public checkMaxHoldings = true;
        bool public maxWalletChanged;
        uint256 public swapping;
        bool public swapEnabled;
        uint256 public swapTokensAtAmount;
        FeeInfo public feeTokens;
        FeeInfo public buyFees;
        FeeInfo public sellFees;
        uint256 public restingBuyTotal;
        uint256 public restingSellTotal;
        bool public buyTaxesSettled;
        bool public sellTaxesSettled;
        bool public proofFeeReduced;
        bool public proofFeeRemoved;
        bool public cancelled;
        uint256 public lockID;
        uint256 public lpLockDuration;
        mapping (address => UserInfo) public userInfo;
        mapping (uint256 => uint256) public swapThrottle;
        uint256 public maxSwapsPerBlock;
        IDataStore public immutable DATA_STORE;
        DataStoreAddressResponse public addresses;
        DataStoreLimitsResponse public limits;
        event SwapAndLiquify(uint256 tokensAutoLiq, uint256 ethAutoLiq);
        event SwapAndLiquifyEnabledUpdated(bool enabled);
        event TokenCancelled(uint256 returnedETH);
        constructor(IDataStore dataStore) {
            DATA_STORE = dataStore;
            _disableInitializers();
        }
        
        function initialize(bytes calldata params) initializer public payable lockTheSwap {
            TokenInfo memory token = abi.decode(params, (TokenInfo));
            __ERC20_init(token.name, token.symbol);
            __Ownable_init(token.owner);
            DataStoreLimitsResponse memory _limits = DATA_STORE.getLimits();
            limits = _limits;
            
            (token.buyFees.proof, token.sellFees.proof) = (2,2);
            _validateFees(token.buyFees, token.sellFees);
            restingBuyTotal = token.buyFees.total;
            restingSellTotal = token.sellFees.total;
            token.buyFees.main = 15 - token.buyFees.proof - token.buyFees.secondary - token.buyFees.liquidity;
            token.buyFees.total = 15;
            token.sellFees.main = 20 - token.sellFees.proof - token.sellFees.secondary - token.sellFees.liquidity;
            token.sellFees.total = 20;
            buyFees = token.buyFees;
            sellFees = token.sellFees;
            // set addresses
            mainWallet = payable(token.mainWallet);
            secondaryWallet = payable(token.secondaryWallet);
            DataStoreAddressResponse memory _addresses = DATA_STORE.getPlatformAddresses();
            addresses = _addresses;
            proofPassNFT = IERC721A(_addresses.proofPassNFT);
            IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(_addresses.router);
            uniswapV2Router = _uniswapV2Router;
            pair = IUniswapV2Factory(_uniswapV2Router.factory())
                .createPair(address(this), _uniswapV2Router.WETH());
            // set basic data
            lpLockDuration = token.lpLockDuration;
            swapTokensAtAmount = token.totalSupply * _limits.swapTokensAtAmount / _limits.denominator; // 125 / 100000
            maxTxAmount = token.totalSupply * _limits.initMaxTx / _limits.denominator;
            initMaxWallet = token.initMaxWallet;
            maxWallet = token.totalSupply * token.initMaxWallet / 100000; // 100 = .1%
            maxSwapsPerBlock = 4;
            userInfo[address(this)] = UserInfo(true, true, true);
            userInfo[pair].isTxLimitExempt = true;
            userInfo[pair].isWhitelisted = true;
            whitelistDuration = token.whitelistDuration;
            _setWhitelisted(token.whitelist);
            uint256 amountToPair = token.totalSupply * token.percentToLP / 100;
            super._update(address(0), address(this), amountToPair); // mint to contract for liquidity
            super._update(address(0), owner(), token.totalSupply - amountToPair); // mint to owner
            _approve(address(this), address(uniswapV2Router), type(uint256).max);
            addLiquidity(amountToPair, msg.value, address(this));
        }
        function launch(uint256 bundleBuyAmount) external payable onlyOwner lockTheSwap {
            if (launchedAt != 0 || cancelled) {
                revert InvalidConfiguration();
            }
            // enable trading
            checkMaxHoldings = true;
            swapEnabled = true;
            whitelistEndTime = block.timestamp + whitelistDuration;
            isWhitelistActive = true;
            launchedAt = block.timestamp;
            if (bundleBuyAmount != 0) {
                //execute bundle buy
                address[] memory path = new address[](2);
                path[0] = uniswapV2Router.WETH();
                path[1] = address(this);
                uniswapV2Router.swapExactETHForTokens{ value: bundleBuyAmount }(
                    0, 
                    path, 
                    msg.sender, 
                    block.timestamp
                );
            }
            // add NFT snapshot
            uint256 len = proofPassNFT.totalSupply() + 1;
            for (uint256 i = 1; i < len; ) {
                userInfo[proofPassNFT.ownerOf(i)].isWhitelisted = true;
                unchecked { ++i; }
            }
            // lock liquidity
            uint256 lpBalance = IERC20(pair).balanceOf(address(this));
            IERC20(pair).approve(addresses.locker, lpBalance);
            
            lockID = ITeamFinanceLocker(addresses.locker).lockToken{value: address(this).balance}(pair, msg.sender, lpBalance, block.timestamp + lpLockDuration, false, address(0));
        }
        function cancel() external onlyOwner lockTheSwap {
            if (launchedAt != 0) {
                revert InvalidConfiguration();
            }
            IERC20(pair).approve(address(uniswapV2Router), IERC20(pair).balanceOf(address(this)));
            (uint256 ethAmt) = uniswapV2Router.removeLiquidityETHSupportingFeeOnTransferTokens(
                address(this),
                IERC20(pair).balanceOf(address(this)),
                0, // liq pool should be untouchable
                0, // liq pool should be untouchable
                msg.sender,
                block.timestamp
            );
            emit TokenCancelled(ethAmt);
            
            cancelled = true;
            // send the tokens and eth back to the owner
            uint256 bal = address(this).balance;
            if (bal > 0) {
                address(msg.sender).call{value: bal}("");
            }
        }
        function _update(
            address from,
            address to,
            uint256 amount
        ) internal override {
            if (swapping == 2 || from == owner() || to == owner() || 
              from == address(this) || to == address(this) || amount == 0) {
                super._update(from, to, amount);
                return;
            }
            if (launchedAt == 0) {
                revert TradingNotEnabled();
            }
            UserInfo storage sender = userInfo[from];
            UserInfo storage recipient = userInfo[to];
            if (isWhitelistActive) {
                if (block.timestamp < whitelistEndTime) {
                    if (!sender.isWhitelisted || !recipient.isWhitelisted)
                    {
                        revert NotWhitelisted();
                    }
                } else {
                    isWhitelistActive = false;
                }
            }
            //start at anywhere from 0.1% to 0.5%, increase by 0.1%, every 10 blocks, until it reaches 1%
            if (!maxWalletChanged) {
                uint256 secondsPassed = block.timestamp - launchedAt;
                uint256 percentage = initMaxWallet + (100 * (secondsPassed / 120));
                if (percentage > 950) {
                    percentage = 1000;
                    maxWalletChanged = true;
                }
                uint256 newMax = totalSupply() * percentage / 100000;
                if (newMax != maxWallet) {
                    maxWallet = newMax;
                }
            }
            if (checkMaxHoldings) {
                if (!recipient.isTxLimitExempt && amount + balanceOf(to) > maxWallet) {
                    revert ExceedsMaxWalletAmount();
                }
            }
            uint256 total = feeTokens.total;
            bool canSwap = total >= swapTokensAtAmount;
            if (
                canSwap &&
                swapEnabled &&
                from != pair &&
                swapThrottle[block.number] < maxSwapsPerBlock
            ) {
                ++swapThrottle[block.number];
                processFees(total, swapTokensAtAmount);
            }
            
            if (!sender.isFeeExempt && !recipient.isFeeExempt) {
                FeeInfo storage _buyFees = buyFees;
                FeeInfo storage _sellFees = sellFees;
                if (!proofFeeRemoved) {
                    uint256 secondsPassed = block.timestamp - launchedAt;
                    if (!proofFeeReduced && secondsPassed > 1 days) {
                        uint256 totalBuy = _buyFees.total - _buyFees.proof;
                        if (totalBuy == 0) {
                            _buyFees.total = 0;
                            _buyFees.proof = 0;
                        } else {
                            buyFees.main = _buyFees.main + 1; //move proof fee to main fee, total doesn't change
                            _buyFees.proof = 1; //decrementing proof fee by 1%
                        }
                        uint256 totalSell = _sellFees.total - _sellFees.proof;
                        if (totalSell == 0) {
                            _sellFees.total = 0;
                            _sellFees.proof = 0;
                        } else {
                            _sellFees.main = _sellFees.main + 1; //same as the buy fee logic
                            _sellFees.proof = 1;
                        }
                        proofFeeReduced = true;
                    } else if (secondsPassed > 31 days) {
                        //move proof fee to main fee
                        _buyFees.main += _buyFees.proof; 
                        _sellFees.main += _sellFees.proof; 
                        _buyFees.proof = 0;
                        _sellFees.proof = 0;
                        proofFeeRemoved = true;
                    } else {
                        if (!buyTaxesSettled) {
                            uint256 restingTotal = restingBuyTotal;
                            uint256 feeTotal = restingTotal;
                            if (secondsPassed < 1801) {
                                //fee starts at 15%, decreases by 1% every 2 minutes until we reach the restingTotal.
                                feeTotal = 15 - (secondsPassed / 120);
                            }
                            if (feeTotal <= restingTotal) {
                                _buyFees.total = restingTotal;
                                _buyFees.main = restingTotal - _buyFees.liquidity - _buyFees.secondary - _buyFees.proof;
                                buyTaxesSettled = true;
                            } else if (feeTotal != _buyFees.total) {
                                _buyFees.total = feeTotal;
                                //extra fees get sent to the main wallet
                                _buyFees.main = feeTotal - _buyFees.liquidity - _buyFees.secondary - _buyFees.proof;
                            }
                        }
                        if (!sellTaxesSettled) {
                            uint256 restingTotal = restingSellTotal;
                            uint256 feeTotal = restingTotal;
                            if (secondsPassed < 2401) {
                                feeTotal = 20 - (secondsPassed / 120);
                            }
                            if (feeTotal <= restingTotal) {
                                _sellFees.total = restingTotal;
                                _sellFees.main = restingTotal - _sellFees.liquidity - _sellFees.secondary - _sellFees.proof;
                                sellTaxesSettled = true;
                            } else if (feeTotal != _sellFees.total) {
                                _sellFees.total = feeTotal;
                                _sellFees.main = feeTotal - _sellFees.liquidity - _sellFees.secondary - _sellFees.proof;
                            }
                        }
                    }
                }
                uint256 fees;
                if (to == pair) { //sell
                    fees = _calculateFees(_sellFees, amount);
                } else if (from == pair) { //buy
                    fees = _calculateFees(_buyFees, amount);
                }
                if (fees > 0) {
                    amount -= fees;
                    super._update(from, address(this), fees);
                }
            }
            super._update(from, to, amount);
        }
        function _calculateFees(FeeInfo memory feeRate, uint256 amount) internal returns (uint256 fees) {
            if (feeRate.total != 0) {
                fees = amount * feeRate.total / 100;
                
                FeeInfo storage _feeTokens = feeTokens;
                _feeTokens.main += fees * feeRate.main / feeRate.total;
                _feeTokens.secondary += fees * feeRate.secondary / feeRate.total;
                _feeTokens.liquidity += fees * feeRate.liquidity / feeRate.total;
                _feeTokens.proof += fees * feeRate.proof / feeRate.total;
                _feeTokens.total += fees;
            }
        }
        function processFees(uint256 total, uint256 amountToSwap) internal lockTheSwap {
            FeeInfo storage _feeTokens = feeTokens;
            FeeInfo memory swapTokens;
            swapTokens.main = amountToSwap * _feeTokens.main / total;
            swapTokens.secondary = amountToSwap * _feeTokens.secondary / total;
            swapTokens.liquidity = amountToSwap * _feeTokens.liquidity / total;
            swapTokens.proof = amountToSwap * _feeTokens.proof / total;
            uint256 amountToPair = swapTokens.liquidity / 2;
            swapTokens.total = amountToSwap - amountToPair;
            uint256 ethBalance = swapTokensForETH(swapTokens.total);
            FeeInfo memory ethSplit;
            ethSplit.main = ethBalance * swapTokens.main / swapTokens.total;
            if (ethSplit.main > 0) {
               address(mainWallet).call{value: ethSplit.main}("");
            }
            ethSplit.secondary = ethBalance * swapTokens.secondary / swapTokens.total;
            if (ethSplit.secondary > 0) {
                address(secondaryWallet).call{value: ethSplit.secondary}("");
            }
            ethSplit.proof = ethBalance * swapTokens.proof / swapTokens.total;
            if (ethSplit.proof > 0) {
                uint256 revenueSplit = ethSplit.proof / 2;
                address(addresses.proofStaking).call{value: revenueSplit}("");
                address(addresses.proofWallet).call{value: ethSplit.proof - revenueSplit}("");
            }
            uint256 amountPaired;
            ethSplit.liquidity = address(this).balance;
            if (amountToPair > 0 && ethSplit.liquidity > 0) {
                amountPaired = addLiquidity(amountToPair, ethSplit.liquidity, address(0xdead));
                emit SwapAndLiquify(amountToPair, ethSplit.liquidity);
            }
            uint256 liquidityAdjustment = swapTokens.liquidity - (amountToPair - amountPaired);
            _feeTokens.main -= swapTokens.main;
            _feeTokens.secondary -= swapTokens.secondary;
            _feeTokens.liquidity -= liquidityAdjustment;
            _feeTokens.proof -= swapTokens.proof;
            _feeTokens.total -= swapTokens.main + swapTokens.secondary + swapTokens.proof + liquidityAdjustment;
        }
        function swapTokensForETH(uint256 tokenAmount) internal returns (uint256 ethBalance) {
            uint256 ethBalBefore = address(this).balance;
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = uniswapV2Router.WETH();
            uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                tokenAmount,
                0, // accept any amount of ETH
                path,
                address(this),
                block.timestamp
            );
            ethBalance = address(this).balance - ethBalBefore;
        }
        function addLiquidity(uint256 tokenAmount, uint256 ethAmount, address recipient) private returns (uint256) {
            (uint256 amountA,,) = uniswapV2Router.addLiquidityETH{value: ethAmount}(
                address(this),
                tokenAmount,
                0, // slippage is unavoidable
                0, // slippage is unavoidable
                recipient,
                block.timestamp
            );
            return amountA;
        }
        function changeFees(
            uint256 liquidityBuy,
            uint256 mainBuy,
            uint256 secondaryBuy,
            uint256 liquiditySell,
            uint256 mainSell,
            uint256 secondarySell
        ) external onlyOwner {
            if (!buyTaxesSettled || !sellTaxesSettled) {
                revert InvalidConfiguration();
            }
            FeeInfo memory _buyFees;
            _buyFees.liquidity = liquidityBuy;
            _buyFees.main = mainBuy;
            _buyFees.secondary = secondaryBuy;
            FeeInfo memory _sellFees;
            _sellFees.liquidity = liquiditySell;
            _sellFees.main = mainSell;
            _sellFees.secondary = secondarySell;
            (_buyFees.proof, _sellFees.proof) = launchedAt != 0 ? _calculateProofFee() : (2,2);
            _validateFees(_buyFees, _sellFees);
            buyFees = _buyFees;
            sellFees = _sellFees;
        }
        function _calculateProofFee() internal returns (uint256, uint256) {
            uint256 secondsPassed = block.timestamp - launchedAt;
            if (secondsPassed > 31 days) {
                proofFeeRemoved = true;
                return (0,0);
            } else if (secondsPassed > 1 days) {
                proofFeeReduced = true;
                return (1,1);
            } else {
                return (2,2);
            }
        }
        function _validateFees(FeeInfo memory _buyFees, FeeInfo memory _sellFees) internal view {
            _buyFees.total = _buyFees.liquidity + _buyFees.main + _buyFees.secondary;
            if (_buyFees.total == 0) {
                _buyFees.proof = 0;
            } else {
                 _buyFees.total += _buyFees.proof;
            }
            _sellFees.total = _sellFees.liquidity + _sellFees.main + _sellFees.secondary;
            if (_sellFees.total == 0) {
                _sellFees.proof = 0;
            } else {
                _sellFees.total += _sellFees.proof;
            }
            if (_buyFees.total > limits.maxBuyFee || _sellFees.total > limits.maxSellFee) {
                revert InvalidConfiguration();
            }
        }
        function setCheckMaxHoldingsEnabled(bool _enabled) external onlyOwner{
            checkMaxHoldings = _enabled;
        }
        function setFeeExempt(address account, bool value) public onlyOwner {
            userInfo[account].isFeeExempt = value;
        }
        function setFeeExempt(address[] memory accounts) public onlyOwner {
            uint256 len = accounts.length;
            for (uint256 i; i < len; i++) {
                userInfo[accounts[i]].isFeeExempt = true;
            }
        }
        function setMainWallet(address newWallet) external onlyOwner {
            mainWallet = payable(newWallet);
        }
        function setSecondaryWallet(address newWallet) external onlyOwner {
            secondaryWallet = payable(newWallet);
        }
        function setSwapAndLiquifyEnabled(bool _enabled) external onlyOwner {
            swapEnabled = _enabled;
            emit SwapAndLiquifyEnabledUpdated(_enabled);
        }
        function setSwapAtAmount(uint256 amount) external onlyOwner {
            swapTokensAtAmount = amount;
        }
        function setMaxSwapsPerBlock(uint256 _maxSwaps) external onlyOwner {
            maxSwapsPerBlock = _maxSwaps;
        }
        function _setWhitelisted(address[] memory accounts) internal {
            uint256 len = accounts.length;
            for (uint256 i; i < len; i++) {
                userInfo[accounts[i]].isWhitelisted = true;
            }
        }
        function withdrawStuckTokens() external onlyOwner {
            super._update(address(this), _msgSender(), balanceOf(address(this)) - feeTokens.total);
        }
        function getCirculatingSupply() external view returns (uint256) {
            return totalSupply() - balanceOf(address(0xdead));
        }
        modifier lockTheSwap() {
            swapping = 2;
            _;
            swapping = 1;
        }
        function decimals() public view virtual override returns (uint8) {
            return 9;
        }
        function version() public pure returns (uint8) {
            return 3;
        }
        receive() external payable {}
     
    }

    File 2 of 2: Token
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    pragma solidity ^0.8.20;
    import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
        struct OwnableStorage {
            address _owner;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
        function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
            assembly {
                $.slot := OwnableStorageLocation
            }
        }
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        function __Ownable_init(address initialOwner) internal onlyInitializing {
            __Ownable_init_unchained(initialOwner);
        }
        function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            OwnableStorage storage $ = _getOwnableStorage();
            return $._owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            OwnableStorage storage $ = _getOwnableStorage();
            address oldOwner = $._owner;
            $._owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
     * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
     * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
     * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
     *
     * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
     * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
     * case an upgrade adds a module that needs to be initialized.
     *
     * For example:
     *
     * [.hljs-theme-light.nopadding]
     * ```solidity
     * contract MyToken is ERC20Upgradeable {
     *     function initialize() initializer public {
     *         __ERC20_init("MyToken", "MTK");
     *     }
     * }
     *
     * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
     *     function initializeV2() reinitializer(2) public {
     *         __ERC20Permit_init("MyToken");
     *     }
     * }
     * ```
     *
     * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
     * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
     *
     * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
     * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
     *
     * [CAUTION]
     * ====
     * Avoid leaving a contract uninitialized.
     *
     * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
     * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
     * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
     *
     * [.hljs-theme-light.nopadding]
     * ```
     * /// @custom:oz-upgrades-unsafe-allow constructor
     * constructor() {
     *     _disableInitializers();
     * }
     * ```
     * ====
     */
    abstract contract Initializable {
        /**
         * @dev Storage of the initializable contract.
         *
         * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
         * when using with upgradeable contracts.
         *
         * @custom:storage-location erc7201:openzeppelin.storage.Initializable
         */
        struct InitializableStorage {
            /**
             * @dev Indicates that the contract has been initialized.
             */
            uint64 _initialized;
            /**
             * @dev Indicates that the contract is in the process of being initialized.
             */
            bool _initializing;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
        /**
         * @dev The contract is already initialized.
         */
        error InvalidInitialization();
        /**
         * @dev The contract is not initializing.
         */
        error NotInitializing();
        /**
         * @dev Triggered when the contract has been initialized or reinitialized.
         */
        event Initialized(uint64 version);
        /**
         * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
         * `onlyInitializing` functions can be used to initialize parent contracts.
         *
         * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
         * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
         * production.
         *
         * Emits an {Initialized} event.
         */
        modifier initializer() {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            // Cache values to avoid duplicated sloads
            bool isTopLevelCall = !$._initializing;
            uint64 initialized = $._initialized;
            // Allowed calls:
            // - initialSetup: the contract is not in the initializing state and no previous version was
            //                 initialized
            // - construction: the contract is initialized at version 1 (no reininitialization) and the
            //                 current contract is just being deployed
            bool initialSetup = initialized == 0 && isTopLevelCall;
            bool construction = initialized == 1 && address(this).code.length == 0;
            if (!initialSetup && !construction) {
                revert InvalidInitialization();
            }
            $._initialized = 1;
            if (isTopLevelCall) {
                $._initializing = true;
            }
            _;
            if (isTopLevelCall) {
                $._initializing = false;
                emit Initialized(1);
            }
        }
        /**
         * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
         * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
         * used to initialize parent contracts.
         *
         * A reinitializer may be used after the original initialization step. This is essential to configure modules that
         * are added through upgrades and that require initialization.
         *
         * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
         * cannot be nested. If one is invoked in the context of another, execution will revert.
         *
         * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
         * a contract, executing them in the right order is up to the developer or operator.
         *
         * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
         *
         * Emits an {Initialized} event.
         */
        modifier reinitializer(uint64 version) {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            if ($._initializing || $._initialized >= version) {
                revert InvalidInitialization();
            }
            $._initialized = version;
            $._initializing = true;
            _;
            $._initializing = false;
            emit Initialized(version);
        }
        /**
         * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
         * {initializer} and {reinitializer} modifiers, directly or indirectly.
         */
        modifier onlyInitializing() {
            _checkInitializing();
            _;
        }
        /**
         * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
         */
        function _checkInitializing() internal view virtual {
            if (!_isInitializing()) {
                revert NotInitializing();
            }
        }
        /**
         * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
         * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
         * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
         * through proxies.
         *
         * Emits an {Initialized} event the first time it is successfully executed.
         */
        function _disableInitializers() internal virtual {
            // solhint-disable-next-line var-name-mixedcase
            InitializableStorage storage $ = _getInitializableStorage();
            if ($._initializing) {
                revert InvalidInitialization();
            }
            if ($._initialized != type(uint64).max) {
                $._initialized = type(uint64).max;
                emit Initialized(type(uint64).max);
            }
        }
        /**
         * @dev Returns the highest version that has been initialized. See {reinitializer}.
         */
        function _getInitializedVersion() internal view returns (uint64) {
            return _getInitializableStorage()._initialized;
        }
        /**
         * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
         */
        function _isInitializing() internal view returns (bool) {
            return _getInitializableStorage()._initializing;
        }
        /**
         * @dev Returns a pointer to the storage namespace.
         */
        // solhint-disable-next-line var-name-mixedcase
        function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
            assembly {
                $.slot := INITIALIZABLE_STORAGE
            }
        }
    }
    // 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 {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
    import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
    import {Initializable} from "../../proxy/utils/Initializable.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 ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
        /// @custom:storage-location erc7201:openzeppelin.storage.ERC20
        struct ERC20Storage {
            mapping(address account => uint256) _balances;
            mapping(address account => mapping(address spender => uint256)) _allowances;
            uint256 _totalSupply;
            string _name;
            string _symbol;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
        function _getERC20Storage() private pure returns (ERC20Storage storage $) {
            assembly {
                $.slot := ERC20StorageLocation
            }
        }
        /**
         * @dev Sets the values for {name} and {symbol}.
         *
         * All two of these values are immutable: they can only be set once during
         * construction.
         */
        function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
            __ERC20_init_unchained(name_, symbol_);
        }
        function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
            ERC20Storage storage $ = _getERC20Storage();
            $._name = name_;
            $._symbol = symbol_;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual returns (string memory) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual returns (string memory) {
            ERC20Storage storage $ = _getERC20Storage();
            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) {
            ERC20Storage storage $ = _getERC20Storage();
            return $._totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view virtual returns (uint256) {
            ERC20Storage storage $ = _getERC20Storage();
            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) {
            ERC20Storage storage $ = _getERC20Storage();
            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 {
            ERC20Storage storage $ = _getERC20Storage();
            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 {
            ERC20Storage storage $ = _getERC20Storage();
            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);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
    pragma solidity ^0.8.20;
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @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 ContextUpgradeable is Initializable {
        function __Context_init() internal onlyInitializing {
        }
        function __Context_init_unchained() internal onlyInitializing {
        }
        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;
        }
    }
    // 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);
    }
    // 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);
    }
    // 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);
    }
    pragma solidity >=0.5.0;
    interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);
        function createPair(address tokenA, address tokenB) external returns (address pair);
        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    }
    pragma solidity >=0.6.2;
    interface IUniswapV2Router01 {
        function factory() external pure returns (address);
        function WETH() external pure returns (address);
        function addLiquidity(
            address tokenA,
            address tokenB,
            uint amountADesired,
            uint amountBDesired,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB, uint liquidity);
        function addLiquidityETH(
            address token,
            uint amountTokenDesired,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
        function removeLiquidity(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline
        ) external returns (uint amountA, uint amountB);
        function removeLiquidityETH(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external returns (uint amountToken, uint amountETH);
        function removeLiquidityWithPermit(
            address tokenA,
            address tokenB,
            uint liquidity,
            uint amountAMin,
            uint amountBMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountA, uint amountB);
        function removeLiquidityETHWithPermit(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountToken, uint amountETH);
        function swapExactTokensForTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external returns (uint[] memory amounts);
        function swapTokensForExactTokens(
            uint amountOut,
            uint amountInMax,
            address[] calldata path,
            address to,
            uint deadline
        ) external returns (uint[] memory amounts);
        function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
            external
            payable
            returns (uint[] memory amounts);
        function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
            external
            returns (uint[] memory amounts);
        function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
            external
            returns (uint[] memory amounts);
        function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
            external
            payable
            returns (uint[] memory amounts);
        function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
        function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
        function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
        function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
        function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
    }
    pragma solidity >=0.6.2;
    import './IUniswapV2Router01.sol';
    interface IUniswapV2Router02 is IUniswapV2Router01 {
        function removeLiquidityETHSupportingFeeOnTransferTokens(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external returns (uint amountETH);
        function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
            address token,
            uint liquidity,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline,
            bool approveMax, uint8 v, bytes32 r, bytes32 s
        ) external returns (uint amountETH);
        function swapExactTokensForTokensSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
        function swapExactETHForTokensSupportingFeeOnTransferTokens(
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external payable;
        function swapExactTokensForETHSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
    }
    // SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    interface IDataStoreResponse {
        struct DataStoreAddressResponse {
            address locker;
            address payable proofWallet;
            address payable proofStaking;
            address proofPassNFT;
            address router;
        }
        struct DataStoreLimitsResponse {
            uint initMaxTx;
            uint swapTokensAtAmount;
            uint maxTxUpper;
            uint maxTxLower;
            uint maxWalletUpper;
            uint maxWalletLower;
            uint maxBuyFee;
            uint maxSellFee;
            uint denominator;
        }
    }
    interface IDataStore is IDataStoreResponse {
        function getAddresses(string[] memory addrKeys) external view returns (address[] memory);
        function getUints(string[] memory uintKeys) external view returns (uint256[] memory);
        function getPlatformAddresses() external view returns (DataStoreAddressResponse memory);
        function getLimits() external view returns (DataStoreLimitsResponse memory);
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    interface IERC721A {
        function totalSupply() external returns (uint256);
        function ownerOf(uint256) external returns (address);
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    interface ITeamFinanceLocker {
        function lockToken(
            address _tokenAddress,
            address _withdrawalAddress,
            uint256 _amount,
            uint256 _unlockTime,
            bool _mintNFT, 
            address referrer
        ) external payable returns (uint256 _id);
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    import "./IDataStore.sol";
    interface ITokenWhitelist is IDataStoreResponse {
        
        struct TokenInfo {
            string name;
            string symbol;
            address owner;
            address mainWallet;
            address secondaryWallet;
            uint256 totalSupply;
            uint256 percentToLP;
            uint256 lpLockDuration;
            uint256 initMaxWallet;
            FeeInfo buyFees;
            FeeInfo sellFees;
            address[] whitelist;
            uint256 whitelistDuration;
            address rewardToken;
        }
        struct FeeInfo {
            uint256 main;
            uint256 secondary;
            uint256 liquidity;
            uint256 proof;
            uint256 total;
        }
        error ExceedsMaxTxAmount();
        error ExceedsMaxWalletAmount();
        error InvalidConfiguration();
        error TradingNotEnabled();
        error NotWhitelisted();
    }// SPDX-License-Identifier: None
    pragma solidity ^0.8.24;
    import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
    import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
    import "./interfaces/IERC721A.sol";
    import "./interfaces/ITeamFinanceLocker.sol";
    import "./interfaces/ITokenWhitelist.sol";
    /// This token was incubated and launched by PROOF: https://proofplatform.io/projects. The smart contract is audited by SourceHat: https://sourcehat.com/
    contract Token is ITokenWhitelist, Initializable, ERC20Upgradeable, OwnableUpgradeable {
        
        struct UserInfo {
            bool isFeeExempt;
            bool isTxLimitExempt;
            bool isWhitelisted;
        }
        IUniswapV2Router02 public uniswapV2Router;
        address public pair;
        address payable public mainWallet;
        address payable public secondaryWallet;
        IERC721A public proofPassNFT;
        bool public isWhitelistActive;
        uint256 public whitelistEndTime;
        uint256 public whitelistDuration;
        uint256 public launchedAt;
        uint256 public maxTxAmount;
        uint256 public maxWallet;
        uint256 public initMaxWallet;
        bool public checkMaxHoldings = true;
        bool public maxWalletChanged;
        uint256 public swapping;
        bool public swapEnabled;
        uint256 public swapTokensAtAmount;
        FeeInfo public feeTokens;
        FeeInfo public buyFees;
        FeeInfo public sellFees;
        uint256 public restingBuyTotal;
        uint256 public restingSellTotal;
        bool public buyTaxesSettled;
        bool public sellTaxesSettled;
        bool public proofFeeReduced;
        bool public proofFeeRemoved;
        bool public cancelled;
        uint256 public lockID;
        uint256 public lpLockDuration;
        mapping (address => UserInfo) public userInfo;
        mapping (uint256 => uint256) public swapThrottle;
        uint256 public maxSwapsPerBlock;
        IDataStore public immutable DATA_STORE;
        DataStoreAddressResponse public addresses;
        DataStoreLimitsResponse public limits;
        event SwapAndLiquify(uint256 tokensAutoLiq, uint256 ethAutoLiq);
        event SwapAndLiquifyEnabledUpdated(bool enabled);
        event TokenCancelled(uint256 returnedETH);
        constructor(IDataStore dataStore) {
            DATA_STORE = dataStore;
            _disableInitializers();
        }
        
        function initialize(bytes calldata params) initializer public payable lockTheSwap {
            TokenInfo memory token = abi.decode(params, (TokenInfo));
            __ERC20_init(token.name, token.symbol);
            __Ownable_init(token.owner);
            DataStoreLimitsResponse memory _limits = DATA_STORE.getLimits();
            limits = _limits;
            
            (token.buyFees.proof, token.sellFees.proof) = (2,2);
            _validateFees(token.buyFees, token.sellFees);
            restingBuyTotal = token.buyFees.total;
            restingSellTotal = token.sellFees.total;
            token.buyFees.main = 15 - token.buyFees.proof - token.buyFees.secondary - token.buyFees.liquidity;
            token.buyFees.total = 15;
            token.sellFees.main = 20 - token.sellFees.proof - token.sellFees.secondary - token.sellFees.liquidity;
            token.sellFees.total = 20;
            buyFees = token.buyFees;
            sellFees = token.sellFees;
            // set addresses
            mainWallet = payable(token.mainWallet);
            secondaryWallet = payable(token.secondaryWallet);
            DataStoreAddressResponse memory _addresses = DATA_STORE.getPlatformAddresses();
            addresses = _addresses;
            proofPassNFT = IERC721A(_addresses.proofPassNFT);
            IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(_addresses.router);
            uniswapV2Router = _uniswapV2Router;
            pair = IUniswapV2Factory(_uniswapV2Router.factory())
                .createPair(address(this), _uniswapV2Router.WETH());
            // set basic data
            lpLockDuration = token.lpLockDuration;
            swapTokensAtAmount = token.totalSupply * _limits.swapTokensAtAmount / _limits.denominator; // 125 / 100000
            maxTxAmount = token.totalSupply * _limits.initMaxTx / _limits.denominator;
            initMaxWallet = token.initMaxWallet;
            maxWallet = token.totalSupply * token.initMaxWallet / 100000; // 100 = .1%
            maxSwapsPerBlock = 4;
            userInfo[address(this)] = UserInfo(true, true, true);
            userInfo[pair].isTxLimitExempt = true;
            userInfo[pair].isWhitelisted = true;
            whitelistDuration = token.whitelistDuration;
            _setWhitelisted(token.whitelist);
            uint256 amountToPair = token.totalSupply * token.percentToLP / 100;
            super._update(address(0), address(this), amountToPair); // mint to contract for liquidity
            super._update(address(0), owner(), token.totalSupply - amountToPair); // mint to owner
            _approve(address(this), address(uniswapV2Router), type(uint256).max);
            addLiquidity(amountToPair, msg.value, address(this));
        }
        function launch(uint256 bundleBuyAmount) external payable onlyOwner lockTheSwap {
            if (launchedAt != 0 || cancelled) {
                revert InvalidConfiguration();
            }
            // enable trading
            checkMaxHoldings = true;
            swapEnabled = true;
            whitelistEndTime = block.timestamp + whitelistDuration;
            isWhitelistActive = true;
            launchedAt = block.timestamp;
            if (bundleBuyAmount != 0) {
                //execute bundle buy
                address[] memory path = new address[](2);
                path[0] = uniswapV2Router.WETH();
                path[1] = address(this);
                uniswapV2Router.swapExactETHForTokens{ value: bundleBuyAmount }(
                    0, 
                    path, 
                    msg.sender, 
                    block.timestamp
                );
            }
            // add NFT snapshot
            uint256 len = proofPassNFT.totalSupply() + 1;
            for (uint256 i = 1; i < len; ) {
                userInfo[proofPassNFT.ownerOf(i)].isWhitelisted = true;
                unchecked { ++i; }
            }
            // lock liquidity
            uint256 lpBalance = IERC20(pair).balanceOf(address(this));
            IERC20(pair).approve(addresses.locker, lpBalance);
            
            lockID = ITeamFinanceLocker(addresses.locker).lockToken{value: address(this).balance}(pair, msg.sender, lpBalance, block.timestamp + lpLockDuration, false, address(0));
        }
        function cancel() external onlyOwner lockTheSwap {
            if (launchedAt != 0) {
                revert InvalidConfiguration();
            }
            IERC20(pair).approve(address(uniswapV2Router), IERC20(pair).balanceOf(address(this)));
            (uint256 ethAmt) = uniswapV2Router.removeLiquidityETHSupportingFeeOnTransferTokens(
                address(this),
                IERC20(pair).balanceOf(address(this)),
                0, // liq pool should be untouchable
                0, // liq pool should be untouchable
                msg.sender,
                block.timestamp
            );
            emit TokenCancelled(ethAmt);
            
            cancelled = true;
            // send the tokens and eth back to the owner
            uint256 bal = address(this).balance;
            if (bal > 0) {
                address(msg.sender).call{value: bal}("");
            }
        }
        function _update(
            address from,
            address to,
            uint256 amount
        ) internal override {
            if (swapping == 2 || from == owner() || to == owner() || 
              from == address(this) || to == address(this) || amount == 0) {
                super._update(from, to, amount);
                return;
            }
            if (launchedAt == 0) {
                revert TradingNotEnabled();
            }
            UserInfo storage sender = userInfo[from];
            UserInfo storage recipient = userInfo[to];
            if (isWhitelistActive) {
                if (block.timestamp < whitelistEndTime) {
                    if (!sender.isWhitelisted || !recipient.isWhitelisted)
                    {
                        revert NotWhitelisted();
                    }
                } else {
                    isWhitelistActive = false;
                }
            }
            //start at anywhere from 0.1% to 0.5%, increase by 0.1%, every 10 blocks, until it reaches 1%
            if (!maxWalletChanged) {
                uint256 secondsPassed = block.timestamp - launchedAt;
                uint256 percentage = initMaxWallet + (100 * (secondsPassed / 120));
                if (percentage > 950) {
                    percentage = 1000;
                    maxWalletChanged = true;
                }
                uint256 newMax = totalSupply() * percentage / 100000;
                if (newMax != maxWallet) {
                    maxWallet = newMax;
                }
            }
            if (checkMaxHoldings) {
                if (!recipient.isTxLimitExempt && amount + balanceOf(to) > maxWallet) {
                    revert ExceedsMaxWalletAmount();
                }
            }
            uint256 total = feeTokens.total;
            bool canSwap = total >= swapTokensAtAmount;
            if (
                canSwap &&
                swapEnabled &&
                from != pair &&
                swapThrottle[block.number] < maxSwapsPerBlock
            ) {
                ++swapThrottle[block.number];
                processFees(total, swapTokensAtAmount);
            }
            
            if (!sender.isFeeExempt && !recipient.isFeeExempt) {
                FeeInfo storage _buyFees = buyFees;
                FeeInfo storage _sellFees = sellFees;
                if (!proofFeeRemoved) {
                    uint256 secondsPassed = block.timestamp - launchedAt;
                    if (!proofFeeReduced && secondsPassed > 1 days) {
                        uint256 totalBuy = _buyFees.total - _buyFees.proof;
                        if (totalBuy == 0) {
                            _buyFees.total = 0;
                            _buyFees.proof = 0;
                        } else {
                            buyFees.main = _buyFees.main + 1; //move proof fee to main fee, total doesn't change
                            _buyFees.proof = 1; //decrementing proof fee by 1%
                        }
                        uint256 totalSell = _sellFees.total - _sellFees.proof;
                        if (totalSell == 0) {
                            _sellFees.total = 0;
                            _sellFees.proof = 0;
                        } else {
                            _sellFees.main = _sellFees.main + 1; //same as the buy fee logic
                            _sellFees.proof = 1;
                        }
                        proofFeeReduced = true;
                    } else if (secondsPassed > 31 days) {
                        //move proof fee to main fee
                        _buyFees.main += _buyFees.proof; 
                        _sellFees.main += _sellFees.proof; 
                        _buyFees.proof = 0;
                        _sellFees.proof = 0;
                        proofFeeRemoved = true;
                    } else {
                        if (!buyTaxesSettled) {
                            uint256 restingTotal = restingBuyTotal;
                            uint256 feeTotal = restingTotal;
                            if (secondsPassed < 1801) {
                                //fee starts at 15%, decreases by 1% every 2 minutes until we reach the restingTotal.
                                feeTotal = 15 - (secondsPassed / 120);
                            }
                            if (feeTotal <= restingTotal) {
                                _buyFees.total = restingTotal;
                                _buyFees.main = restingTotal - _buyFees.liquidity - _buyFees.secondary - _buyFees.proof;
                                buyTaxesSettled = true;
                            } else if (feeTotal != _buyFees.total) {
                                _buyFees.total = feeTotal;
                                //extra fees get sent to the main wallet
                                _buyFees.main = feeTotal - _buyFees.liquidity - _buyFees.secondary - _buyFees.proof;
                            }
                        }
                        if (!sellTaxesSettled) {
                            uint256 restingTotal = restingSellTotal;
                            uint256 feeTotal = restingTotal;
                            if (secondsPassed < 2401) {
                                feeTotal = 20 - (secondsPassed / 120);
                            }
                            if (feeTotal <= restingTotal) {
                                _sellFees.total = restingTotal;
                                _sellFees.main = restingTotal - _sellFees.liquidity - _sellFees.secondary - _sellFees.proof;
                                sellTaxesSettled = true;
                            } else if (feeTotal != _sellFees.total) {
                                _sellFees.total = feeTotal;
                                _sellFees.main = feeTotal - _sellFees.liquidity - _sellFees.secondary - _sellFees.proof;
                            }
                        }
                    }
                }
                uint256 fees;
                if (to == pair) { //sell
                    fees = _calculateFees(_sellFees, amount);
                } else if (from == pair) { //buy
                    fees = _calculateFees(_buyFees, amount);
                }
                if (fees > 0) {
                    amount -= fees;
                    super._update(from, address(this), fees);
                }
            }
            super._update(from, to, amount);
        }
        function _calculateFees(FeeInfo memory feeRate, uint256 amount) internal returns (uint256 fees) {
            if (feeRate.total != 0) {
                fees = amount * feeRate.total / 100;
                
                FeeInfo storage _feeTokens = feeTokens;
                _feeTokens.main += fees * feeRate.main / feeRate.total;
                _feeTokens.secondary += fees * feeRate.secondary / feeRate.total;
                _feeTokens.liquidity += fees * feeRate.liquidity / feeRate.total;
                _feeTokens.proof += fees * feeRate.proof / feeRate.total;
                _feeTokens.total += fees;
            }
        }
        function processFees(uint256 total, uint256 amountToSwap) internal lockTheSwap {
            FeeInfo storage _feeTokens = feeTokens;
            FeeInfo memory swapTokens;
            swapTokens.main = amountToSwap * _feeTokens.main / total;
            swapTokens.secondary = amountToSwap * _feeTokens.secondary / total;
            swapTokens.liquidity = amountToSwap * _feeTokens.liquidity / total;
            swapTokens.proof = amountToSwap * _feeTokens.proof / total;
            uint256 amountToPair = swapTokens.liquidity / 2;
            swapTokens.total = amountToSwap - amountToPair;
            uint256 ethBalance = swapTokensForETH(swapTokens.total);
            FeeInfo memory ethSplit;
            ethSplit.main = ethBalance * swapTokens.main / swapTokens.total;
            if (ethSplit.main > 0) {
               address(mainWallet).call{value: ethSplit.main}("");
            }
            ethSplit.secondary = ethBalance * swapTokens.secondary / swapTokens.total;
            if (ethSplit.secondary > 0) {
                address(secondaryWallet).call{value: ethSplit.secondary}("");
            }
            ethSplit.proof = ethBalance * swapTokens.proof / swapTokens.total;
            if (ethSplit.proof > 0) {
                uint256 revenueSplit = ethSplit.proof / 2;
                address(addresses.proofStaking).call{value: revenueSplit}("");
                address(addresses.proofWallet).call{value: ethSplit.proof - revenueSplit}("");
            }
            uint256 amountPaired;
            ethSplit.liquidity = address(this).balance;
            if (amountToPair > 0 && ethSplit.liquidity > 0) {
                amountPaired = addLiquidity(amountToPair, ethSplit.liquidity, address(0xdead));
                emit SwapAndLiquify(amountToPair, ethSplit.liquidity);
            }
            uint256 liquidityAdjustment = swapTokens.liquidity - (amountToPair - amountPaired);
            _feeTokens.main -= swapTokens.main;
            _feeTokens.secondary -= swapTokens.secondary;
            _feeTokens.liquidity -= liquidityAdjustment;
            _feeTokens.proof -= swapTokens.proof;
            _feeTokens.total -= swapTokens.main + swapTokens.secondary + swapTokens.proof + liquidityAdjustment;
        }
        function swapTokensForETH(uint256 tokenAmount) internal returns (uint256 ethBalance) {
            uint256 ethBalBefore = address(this).balance;
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = uniswapV2Router.WETH();
            uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
                tokenAmount,
                0, // accept any amount of ETH
                path,
                address(this),
                block.timestamp
            );
            ethBalance = address(this).balance - ethBalBefore;
        }
        function addLiquidity(uint256 tokenAmount, uint256 ethAmount, address recipient) private returns (uint256) {
            (uint256 amountA,,) = uniswapV2Router.addLiquidityETH{value: ethAmount}(
                address(this),
                tokenAmount,
                0, // slippage is unavoidable
                0, // slippage is unavoidable
                recipient,
                block.timestamp
            );
            return amountA;
        }
        function changeFees(
            uint256 liquidityBuy,
            uint256 mainBuy,
            uint256 secondaryBuy,
            uint256 liquiditySell,
            uint256 mainSell,
            uint256 secondarySell
        ) external onlyOwner {
            if (!buyTaxesSettled || !sellTaxesSettled) {
                revert InvalidConfiguration();
            }
            FeeInfo memory _buyFees;
            _buyFees.liquidity = liquidityBuy;
            _buyFees.main = mainBuy;
            _buyFees.secondary = secondaryBuy;
            FeeInfo memory _sellFees;
            _sellFees.liquidity = liquiditySell;
            _sellFees.main = mainSell;
            _sellFees.secondary = secondarySell;
            (_buyFees.proof, _sellFees.proof) = launchedAt != 0 ? _calculateProofFee() : (2,2);
            _validateFees(_buyFees, _sellFees);
            buyFees = _buyFees;
            sellFees = _sellFees;
        }
        function _calculateProofFee() internal returns (uint256, uint256) {
            uint256 secondsPassed = block.timestamp - launchedAt;
            if (secondsPassed > 31 days) {
                proofFeeRemoved = true;
                return (0,0);
            } else if (secondsPassed > 1 days) {
                proofFeeReduced = true;
                return (1,1);
            } else {
                return (2,2);
            }
        }
        function _validateFees(FeeInfo memory _buyFees, FeeInfo memory _sellFees) internal view {
            _buyFees.total = _buyFees.liquidity + _buyFees.main + _buyFees.secondary;
            if (_buyFees.total == 0) {
                _buyFees.proof = 0;
            } else {
                 _buyFees.total += _buyFees.proof;
            }
            _sellFees.total = _sellFees.liquidity + _sellFees.main + _sellFees.secondary;
            if (_sellFees.total == 0) {
                _sellFees.proof = 0;
            } else {
                _sellFees.total += _sellFees.proof;
            }
            if (_buyFees.total > limits.maxBuyFee || _sellFees.total > limits.maxSellFee) {
                revert InvalidConfiguration();
            }
        }
        function setCheckMaxHoldingsEnabled(bool _enabled) external onlyOwner{
            checkMaxHoldings = _enabled;
        }
        function setFeeExempt(address account, bool value) public onlyOwner {
            userInfo[account].isFeeExempt = value;
        }
        function setFeeExempt(address[] memory accounts) public onlyOwner {
            uint256 len = accounts.length;
            for (uint256 i; i < len; i++) {
                userInfo[accounts[i]].isFeeExempt = true;
            }
        }
        function setMainWallet(address newWallet) external onlyOwner {
            mainWallet = payable(newWallet);
        }
        function setSecondaryWallet(address newWallet) external onlyOwner {
            secondaryWallet = payable(newWallet);
        }
        function setSwapAndLiquifyEnabled(bool _enabled) external onlyOwner {
            swapEnabled = _enabled;
            emit SwapAndLiquifyEnabledUpdated(_enabled);
        }
        function setSwapAtAmount(uint256 amount) external onlyOwner {
            swapTokensAtAmount = amount;
        }
        function setMaxSwapsPerBlock(uint256 _maxSwaps) external onlyOwner {
            maxSwapsPerBlock = _maxSwaps;
        }
        function _setWhitelisted(address[] memory accounts) internal {
            uint256 len = accounts.length;
            for (uint256 i; i < len; i++) {
                userInfo[accounts[i]].isWhitelisted = true;
            }
        }
        function withdrawStuckTokens() external onlyOwner {
            super._update(address(this), _msgSender(), balanceOf(address(this)) - feeTokens.total);
        }
        function getCirculatingSupply() external view returns (uint256) {
            return totalSupply() - balanceOf(address(0xdead));
        }
        modifier lockTheSwap() {
            swapping = 2;
            _;
            swapping = 1;
        }
        function decimals() public view virtual override returns (uint8) {
            return 9;
        }
        function version() public pure returns (uint8) {
            return 3;
        }
        receive() external payable {}
     
    }