ETH Price: $2,059.55 (+2.85%)

Transaction Decoder

Block:
22690130 at Jun-12-2025 05:59:47 PM +UTC
Transaction Fee:
0.000102673358410215 ETH $0.21
Gas Used:
48,745 Gas / 2.106336207 Gwei

Emitted Events:

154 AgentToken.Approval( owner=[Sender] 0x3754dc5700f27e2b9de7dd1a5d6372e765accac2, spender=0x0c3cf6bF...898bB5754, value=8403047292000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x3754dc57...765aCCAc2
0.003160501120528465 Eth
Nonce: 21
0.00305782776211825 Eth
Nonce: 22
0.000102673358410215
(BuilderNet)
12.741248266156604781 Eth12.741252117011604781 Eth0.000003850855
0xEA87148a...093aE8ee0

Execution Trace

AgentToken.approve( spender=0x0c3cf6bF9FAFFA39a8A9B6891e1777A898bB5754, amount=8403047292000000000000 ) => ( True )
  • AgentToken.approve( spender=0x0c3cf6bF9FAFFA39a8A9B6891e1777A898bB5754, amount=8403047292000000000000 ) => ( True )
    File 1 of 2: AgentToken
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
        struct Ownable2StepStorage {
            address _pendingOwner;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
        function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
            assembly {
                $.slot := Ownable2StepStorageLocation
            }
        }
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        function __Ownable2Step_init() internal onlyInitializing {
        }
        function __Ownable2Step_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            return $._pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            $._pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            delete $._pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // 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) (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;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (governance/IGovernor.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../interfaces/IERC165.sol";
    import {IERC6372} from "../interfaces/IERC6372.sol";
    /**
     * @dev Interface of the {Governor} core.
     */
    interface IGovernor is IERC165, IERC6372 {
        enum ProposalState {
            Pending,
            Active,
            Canceled,
            Defeated,
            Succeeded,
            Queued,
            Expired,
            Executed
        }
        /**
         * @dev Empty proposal or a mismatch between the parameters length for a proposal call.
         */
        error GovernorInvalidProposalLength(uint256 targets, uint256 calldatas, uint256 values);
        /**
         * @dev The vote was already cast.
         */
        error GovernorAlreadyCastVote(address voter);
        /**
         * @dev Token deposits are disabled in this contract.
         */
        error GovernorDisabledDeposit();
        /**
         * @dev The `account` is not a proposer.
         */
        error GovernorOnlyProposer(address account);
        /**
         * @dev The `account` is not the governance executor.
         */
        error GovernorOnlyExecutor(address account);
        /**
         * @dev The `proposalId` doesn't exist.
         */
        error GovernorNonexistentProposal(uint256 proposalId);
        /**
         * @dev The current state of a proposal is not the required for performing an operation.
         * The `expectedStates` is a bitmap with the bits enabled for each ProposalState enum position
         * counting from right to left.
         *
         * NOTE: If `expectedState` is `bytes32(0)`, the proposal is expected to not be in any state (i.e. not exist).
         * This is the case when a proposal that is expected to be unset is already initiated (the proposal is duplicated).
         *
         * See {Governor-_encodeStateBitmap}.
         */
        error GovernorUnexpectedProposalState(uint256 proposalId, ProposalState current, bytes32 expectedStates);
        /**
         * @dev The voting period set is not a valid period.
         */
        error GovernorInvalidVotingPeriod(uint256 votingPeriod);
        /**
         * @dev The `proposer` does not have the required votes to create a proposal.
         */
        error GovernorInsufficientProposerVotes(address proposer, uint256 votes, uint256 threshold);
        /**
         * @dev The `proposer` is not allowed to create a proposal.
         */
        error GovernorRestrictedProposer(address proposer);
        /**
         * @dev The vote type used is not valid for the corresponding counting module.
         */
        error GovernorInvalidVoteType();
        /**
         * @dev Queue operation is not implemented for this governor. Execute should be called directly.
         */
        error GovernorQueueNotImplemented();
        /**
         * @dev The proposal hasn't been queued yet.
         */
        error GovernorNotQueuedProposal(uint256 proposalId);
        /**
         * @dev The proposal has already been queued.
         */
        error GovernorAlreadyQueuedProposal(uint256 proposalId);
        /**
         * @dev The provided signature is not valid for the expected `voter`.
         * If the `voter` is a contract, the signature is not valid using {IERC1271-isValidSignature}.
         */
        error GovernorInvalidSignature(address voter);
        /**
         * @dev Emitted when a proposal is created.
         */
        event ProposalCreated(
            uint256 proposalId,
            address proposer,
            address[] targets,
            uint256[] values,
            string[] signatures,
            bytes[] calldatas,
            uint256 voteStart,
            uint256 voteEnd,
            string description
        );
        /**
         * @dev Emitted when a proposal is queued.
         */
        event ProposalQueued(uint256 proposalId, uint256 etaSeconds);
        /**
         * @dev Emitted when a proposal is executed.
         */
        event ProposalExecuted(uint256 proposalId);
        /**
         * @dev Emitted when a proposal is canceled.
         */
        event ProposalCanceled(uint256 proposalId);
        /**
         * @dev Emitted when a vote is cast without params.
         *
         * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
         */
        event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
        /**
         * @dev Emitted when a vote is cast with params.
         *
         * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
         * `params` are additional encoded parameters. Their interpepretation also depends on the voting module used.
         */
        event VoteCastWithParams(
            address indexed voter,
            uint256 proposalId,
            uint8 support,
            uint256 weight,
            string reason,
            bytes params
        );
        /**
         * @notice module:core
         * @dev Name of the governor instance (used in building the ERC712 domain separator).
         */
        function name() external view returns (string memory);
        /**
         * @notice module:core
         * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1"
         */
        function version() external view returns (string memory);
        /**
         * @notice module:voting
         * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
         * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of
         * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
         *
         * There are 2 standard keys: `support` and `quorum`.
         *
         * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`.
         * - `quorum=bravo` means that only For votes are counted towards quorum.
         * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
         *
         * If a counting module makes use of encoded `params`, it should  include this under a `params` key with a unique
         * name that describes the behavior. For example:
         *
         * - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
         * - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
         *
         * NOTE: The string can be decoded by the standard
         * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
         * JavaScript class.
         */
        // solhint-disable-next-line func-name-mixedcase
        function COUNTING_MODE() external view returns (string memory);
        /**
         * @notice module:core
         * @dev Hashing function used to (re)build the proposal id from the proposal details..
         */
        function hashProposal(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external pure returns (uint256);
        /**
         * @notice module:core
         * @dev Current state of a proposal, following Compound's convention
         */
        function state(uint256 proposalId) external view returns (ProposalState);
        /**
         * @notice module:core
         * @dev The number of votes required in order for a voter to become a proposer.
         */
        function proposalThreshold() external view returns (uint256);
        /**
         * @notice module:core
         * @dev Timepoint used to retrieve user's votes and quorum. If using block number (as per Compound's Comp), the
         * snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the
         * following block.
         */
        function proposalSnapshot(uint256 proposalId) external view returns (uint256);
        /**
         * @notice module:core
         * @dev Timepoint at which votes close. If using block number, votes close at the end of this block, so it is
         * possible to cast a vote during this block.
         */
        function proposalDeadline(uint256 proposalId) external view returns (uint256);
        /**
         * @notice module:core
         * @dev The account that created a proposal.
         */
        function proposalProposer(uint256 proposalId) external view returns (address);
        /**
         * @notice module:core
         * @dev The time when a queued proposal becomes executable ("ETA"). Unlike {proposalSnapshot} and
         * {proposalDeadline}, this doesn't use the governor clock, and instead relies on the executor's clock which may be
         * different. In most cases this will be a timestamp.
         */
        function proposalEta(uint256 proposalId) external view returns (uint256);
        /**
         * @notice module:core
         * @dev Whether a proposal needs to be queued before execution.
         */
        function proposalNeedsQueuing(uint256 proposalId) external view returns (bool);
        /**
         * @notice module:user-config
         * @dev Delay, between the proposal is created and the vote starts. The unit this duration is expressed in depends
         * on the clock (see EIP-6372) this contract uses.
         *
         * This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a
         * proposal starts.
         *
         * NOTE: While this interface returns a uint256, timepoints are stored as uint48 following the ERC-6372 clock type.
         * Consequently this value must fit in a uint48 (when added to the current clock). See {IERC6372-clock}.
         */
        function votingDelay() external view returns (uint256);
        /**
         * @notice module:user-config
         * @dev Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock
         * (see EIP-6372) this contract uses.
         *
         * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting
         * duration compared to the voting delay.
         *
         * NOTE: This value is stored when the proposal is submitted so that possible changes to the value do not affect
         * proposals that have already been submitted. The type used to save it is a uint32. Consequently, while this
         * interface returns a uint256, the value it returns should fit in a uint32.
         */
        function votingPeriod() external view returns (uint256);
        /**
         * @notice module:user-config
         * @dev Minimum number of cast voted required for a proposal to be successful.
         *
         * NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
         * quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
         */
        function quorum(uint256 timepoint) external view returns (uint256);
        /**
         * @notice module:reputation
         * @dev Voting power of an `account` at a specific `timepoint`.
         *
         * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
         * multiple), {ERC20Votes} tokens.
         */
        function getVotes(address account, uint256 timepoint) external view returns (uint256);
        /**
         * @notice module:reputation
         * @dev Voting power of an `account` at a specific `timepoint` given additional encoded parameters.
         */
        function getVotesWithParams(
            address account,
            uint256 timepoint,
            bytes memory params
        ) external view returns (uint256);
        /**
         * @notice module:voting
         * @dev Returns whether `account` has cast a vote on `proposalId`.
         */
        function hasVoted(uint256 proposalId, address account) external view returns (bool);
        /**
         * @dev Create a new proposal. Vote start after a delay specified by {IGovernor-votingDelay} and lasts for a
         * duration specified by {IGovernor-votingPeriod}.
         *
         * Emits a {ProposalCreated} event.
         */
        function propose(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            string memory description
        ) external returns (uint256 proposalId);
        /**
         * @dev Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing
         * is not necessary, this function may revert.
         * Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached.
         *
         * Emits a {ProposalQueued} event.
         */
        function queue(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external returns (uint256 proposalId);
        /**
         * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the
         * deadline to be reached. Depending on the governor it might also be required that the proposal was queued and
         * that some delay passed.
         *
         * Emits a {ProposalExecuted} event.
         *
         * NOTE: Some modules can modify the requirements for execution, for example by adding an additional timelock.
         */
        function execute(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external payable returns (uint256 proposalId);
        /**
         * @dev Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e.
         * before the vote starts.
         *
         * Emits a {ProposalCanceled} event.
         */
        function cancel(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external returns (uint256 proposalId);
        /**
         * @dev Cast a vote
         *
         * Emits a {VoteCast} event.
         */
        function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
        /**
         * @dev Cast a vote with a reason
         *
         * Emits a {VoteCast} event.
         */
        function castVoteWithReason(
            uint256 proposalId,
            uint8 support,
            string calldata reason
        ) external returns (uint256 balance);
        /**
         * @dev Cast a vote with a reason and additional encoded parameters
         *
         * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
         */
        function castVoteWithReasonAndParams(
            uint256 proposalId,
            uint8 support,
            string calldata reason,
            bytes memory params
        ) external returns (uint256 balance);
        /**
         * @dev Cast a vote using the voter's signature, including ERC-1271 signature support.
         *
         * Emits a {VoteCast} event.
         */
        function castVoteBySig(
            uint256 proposalId,
            uint8 support,
            address voter,
            bytes memory signature
        ) external returns (uint256 balance);
        /**
         * @dev Cast a vote with a reason and additional encoded parameters using the voter's signature,
         * including ERC-1271 signature support.
         *
         * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
         */
        function castVoteWithReasonAndParamsBySig(
            uint256 proposalId,
            uint8 support,
            address voter,
            string calldata reason,
            bytes memory params,
            bytes memory signature
        ) external returns (uint256 balance);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../utils/introspection/IERC165.sol";
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol)
    pragma solidity ^0.8.20;
    interface IERC6372 {
        /**
         * @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
         */
        function clock() external view returns (uint48);
        /**
         * @dev Description of the clock
         */
        // solhint-disable-next-line func-name-mixedcase
        function CLOCK_MODE() external view returns (string memory);
    }
    // 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/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // 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);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert FailedInnerCall();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    pragma solidity ^0.8.20;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position is the index of the value in the `values` array plus 1.
            // Position 0 is used to mean a value is not in the set.
            mapping(bytes32 value => uint256) _positions;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._positions[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We cache the value's position to prevent multiple reads from the same storage slot
            uint256 position = set._positions[value];
            if (position != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 valueIndex = position - 1;
                uint256 lastIndex = set._values.length - 1;
                if (valueIndex != lastIndex) {
                    bytes32 lastValue = set._values[lastIndex];
                    // Move the lastValue to the index where the value to delete is
                    set._values[valueIndex] = lastValue;
                    // Update the tracked position of the lastValue (that was just moved)
                    set._positions[lastValue] = position;
                }
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the tracked position for the deleted slot
                delete set._positions[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._positions[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
    }
    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.5.0;
    interface IUniswapV2Pair {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
        function MINIMUM_LIQUIDITY() external pure returns (uint);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function price0CumulativeLast() external view returns (uint);
        function price1CumulativeLast() external view returns (uint);
        function kLast() external view returns (uint);
        function mint(address to) external returns (uint liquidity);
        function burn(address to) external returns (uint amount0, uint amount1);
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
        function skim(address to) external;
        function sync() external;
        function initialize(address, address) external;
    }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: MIT
    pragma solidity ^0.8.20;
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
    import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import "../pool/IUniswapV2Router02.sol";
    import "../pool/IUniswapV2Factory.sol";
    import "../pool/IUniswapV2Pair.sol";
    import "./IAgentToken.sol";
    import "./IAgentFactory.sol";
    contract AgentToken is
        ContextUpgradeable,
        IAgentToken,
        Ownable2StepUpgradeable
    {
        using EnumerableSet for EnumerableSet.AddressSet;
        using EnumerableSet for EnumerableSet.Bytes32Set;
        using SafeERC20 for IERC20;
        uint256 internal constant BP_DENOM = 10000;
        uint256 internal constant ROUND_DEC = 100000000000;
        uint256 internal constant CALL_GAS_LIMIT = 50000;
        uint256 internal constant MAX_SWAP_THRESHOLD_MULTIPLE = 20;
        address public uniswapV2Pair;
        uint256 public botProtectionDurationInSeconds;
        bool internal _tokenHasTax;
        IUniswapV2Router02 internal _uniswapRouter;
        uint32 public fundedDate;
        uint16 public projectBuyTaxBasisPoints;
        uint16 public projectSellTaxBasisPoints;
        uint16 public swapThresholdBasisPoints;
        address public pairToken; // The token used to trade for this token
        /** @dev {_autoSwapInProgress} We start with {_autoSwapInProgress} ON, as we don't want to
         * call autoswap when processing initial liquidity from this address. We turn this OFF when
         * liquidity has been loaded, and use this bool to control processing during auto-swaps
         * from that point onwards. */
        bool private _autoSwapInProgress;
        address public projectTaxRecipient;
        uint128 public projectTaxPendingSwap;
        address public vault; // Project supply vault
        string private _name;
        string private _symbol;
        uint256 private _totalSupply;
        /** @dev {_balances} Addresses balances */
        mapping(address => uint256) private _balances;
        /** @dev {_allowances} Addresses allocance details */
        mapping(address => mapping(address => uint256)) private _allowances;
        /** @dev {_validCallerCodeHashes} Code hashes of callers we consider valid */
        EnumerableSet.Bytes32Set private _validCallerCodeHashes;
        /** @dev {_liquidityPools} Enumerable set for liquidity pool addresses */
        EnumerableSet.AddressSet private _liquidityPools;
        IAgentFactory private _factory; // Single source of truth
        /**
         * @dev {onlyOwnerOrFactory}
         *
         * Throws if called by any account other than the owner, factory or pool.
         */
        modifier onlyOwnerOrFactory() {
            if (owner() != _msgSender() && address(_factory) != _msgSender()) {
                revert CallerIsNotAdminNorFactory();
            }
            _;
        }
        constructor() {
            _disableInitializers();
        }
        function initialize(
            address[3] memory integrationAddresses_,
            bytes memory baseParams_,
            bytes memory supplyParams_,
            bytes memory taxParams_
        ) external initializer {
            _decodeBaseParams(integrationAddresses_[0], baseParams_);
            _uniswapRouter = IUniswapV2Router02(integrationAddresses_[1]);
            pairToken = integrationAddresses_[2];
            ERC20SupplyParameters memory supplyParams = abi.decode(
                supplyParams_,
                (ERC20SupplyParameters)
            );
            ERC20TaxParameters memory taxParams = abi.decode(
                taxParams_,
                (ERC20TaxParameters)
            );
            _processSupplyParams(supplyParams);
            uint256 lpSupply = supplyParams.lpSupply * (10 ** decimals());
            uint256 vaultSupply = supplyParams.vaultSupply * (10 ** decimals());
            botProtectionDurationInSeconds = supplyParams
                .botProtectionDurationInSeconds;
            _tokenHasTax = _processTaxParams(taxParams);
            swapThresholdBasisPoints = uint16(
                taxParams.taxSwapThresholdBasisPoints
            );
            projectTaxRecipient = taxParams.projectTaxRecipient;
            _mintBalances(lpSupply, vaultSupply);
            uniswapV2Pair = _createPair();
            _factory = IAgentFactory(_msgSender());
            _autoSwapInProgress = true; // We don't want to tax initial liquidity
        }
        /**
         * @dev function {_decodeBaseParams}
         *
         * Decode NFT Parameters
         *
         * @param projectOwner_ The owner of this contract
         * @param encodedBaseParams_ The base params encoded into a bytes array
         */
        function _decodeBaseParams(
            address projectOwner_,
            bytes memory encodedBaseParams_
        ) internal {
            _transferOwnership(projectOwner_);
            (_name, _symbol) = abi.decode(encodedBaseParams_, (string, string));
        }
        /**
         * @dev function {_processSupplyParams}
         *
         * Process provided supply params
         *
         * @param erc20SupplyParameters_ The supply params
         */
        function _processSupplyParams(
            ERC20SupplyParameters memory erc20SupplyParameters_
        ) internal {
            if (
                erc20SupplyParameters_.maxSupply !=
                (erc20SupplyParameters_.vaultSupply +
                    erc20SupplyParameters_.lpSupply)
            ) {
                revert SupplyTotalMismatch();
            }
            if (erc20SupplyParameters_.maxSupply > type(uint128).max) {
                revert MaxSupplyTooHigh();
            }
            vault = erc20SupplyParameters_.vault;
        }
        /**
         * @dev function {_processTaxParams}
         *
         * Process provided tax params
         *
         * @param erc20TaxParameters_ The tax params
         */
        function _processTaxParams(
            ERC20TaxParameters memory erc20TaxParameters_
        ) internal returns (bool tokenHasTax_) {
            /**
             * @dev If this
             * token does NOT have tax applied then there is no need to store or read these parameters, and we can
             * avoid this simply by checking the immutable var. Pass back the value for this var from this method.
             */
            if (
                erc20TaxParameters_.projectBuyTaxBasisPoints == 0 &&
                erc20TaxParameters_.projectSellTaxBasisPoints == 0
            ) {
                return false;
            } else {
                projectBuyTaxBasisPoints = uint16(
                    erc20TaxParameters_.projectBuyTaxBasisPoints
                );
                projectSellTaxBasisPoints = uint16(
                    erc20TaxParameters_.projectSellTaxBasisPoints
                );
                return true;
            }
        }
        /**
         * @dev function {_mintBalances}
         *
         * Mint initial balances
         *
         * @param lpMint_ The number of tokens for liquidity
         */
        function _mintBalances(uint256 lpMint_, uint256 vaultMint_) internal {
            if (lpMint_ > 0) {
                _mint(address(this), lpMint_);
            }
            if (vaultMint_ > 0) {
                _mint(vault, vaultMint_);
            }
        }
        /**
         * @dev function {_createPair}
         *
         * Create the uniswap pair
         *
         * @return uniswapV2Pair_ The pair address
         */
        function _createPair() internal returns (address uniswapV2Pair_) {
            uniswapV2Pair_ = IUniswapV2Factory(_uniswapRouter.factory()).getPair(
                address(this),
                pairToken
            );
            if (uniswapV2Pair_ == address(0)) {
                uniswapV2Pair_ = IUniswapV2Factory(_uniswapRouter.factory())
                    .createPair(address(this), pairToken);
                emit LiquidityPoolCreated(uniswapV2Pair_);
            }
            _liquidityPools.add(uniswapV2Pair_);
            return (uniswapV2Pair_);
        }
        /**
         * @dev function {addInitialLiquidity}
         *
         * Add initial liquidity to the uniswap pair
         *
         * @param lpOwner The recipient of LP tokens
         */
        function addInitialLiquidity(address lpOwner) external onlyOwnerOrFactory {
            _addInitialLiquidity(lpOwner);
        }
        /**
         * @dev function {_addInitialLiquidity}
         *
         * Add initial liquidity to the uniswap pair (internal function that does processing)
         *
         * * @param lpOwner The recipient of LP tokens
         */
        function _addInitialLiquidity(address lpOwner) internal {
            // Funded date is the date of first funding. We can only add initial liquidity once. If this date is set,
            // we cannot proceed
            if (fundedDate != 0) {
                revert InitialLiquidityAlreadyAdded();
            }
            fundedDate = uint32(block.timestamp);
            // Can only do this if this contract holds tokens:
            if (balanceOf(address(this)) == 0) {
                revert NoTokenForLiquidityPair();
            }
            // Approve the uniswap router for an inifinite amount (max uint256)
            // This means that we don't need to worry about later incrememtal
            // approvals on tax swaps, as the uniswap router allowance will never
            // be decreased (see code in decreaseAllowance for reference)
            _approve(address(this), address(_uniswapRouter), type(uint256).max);
            IERC20(pairToken).approve(address(_uniswapRouter), type(uint256).max);
            // Add the liquidity:
            address pairAddr = IUniswapV2Factory(_uniswapRouter.factory()).getPair(
                address(this),
                pairToken
            );
            uint256 amountA = balanceOf(address(this));
            uint256 amountB = IERC20(pairToken).balanceOf(address(this));
            _transfer(address(this), pairAddr, amountA, false);
            IERC20(pairToken).transfer(pairAddr, amountB);
            uint256 lpTokens = IUniswapV2Pair(pairAddr).mint(address(this));
            emit InitialLiquidityAdded(amountA, amountB, lpTokens);
            // We now set this to false so that future transactions can be eligibile for autoswaps
            _autoSwapInProgress = false;
            IERC20(uniswapV2Pair).transfer(lpOwner, lpTokens);
        }
        /**
         * @dev function {isLiquidityPool}
         *
         * Return if an address is a liquidity pool
         *
         * @param queryAddress_ The address being queried
         * @return bool The address is / isn't a liquidity pool
         */
        function isLiquidityPool(address queryAddress_) public view returns (bool) {
            /** @dev We check the uniswapV2Pair address first as this is an immutable variable and therefore does not need
             * to be fetched from storage, saving gas if this address IS the uniswapV2Pool. We also add this address
             * to the enumerated set for ease of reference (for example it is returned in the getter), and it does
             * not add gas to any other calls, that still complete in 0(1) time.
             */
            return (queryAddress_ == uniswapV2Pair ||
                _liquidityPools.contains(queryAddress_));
        }
        /**
         * @dev function {liquidityPools}
         *
         * Returns a list of all liquidity pools
         *
         * @return liquidityPools_ a list of all liquidity pools
         */
        function liquidityPools()
            external
            view
            returns (address[] memory liquidityPools_)
        {
            return (_liquidityPools.values());
        }
        /**
         * @dev function {addLiquidityPool} onlyOwnerOrFactory
         *
         * Allows the manager to add a liquidity pool to the pool enumerable set
         *
         * @param newLiquidityPool_ The address of the new liquidity pool
         */
        function addLiquidityPool(
            address newLiquidityPool_
        ) public onlyOwnerOrFactory {
            // Don't allow calls that didn't pass an address:
            if (newLiquidityPool_ == address(0)) {
                revert LiquidityPoolCannotBeAddressZero();
            }
            // Only allow smart contract addresses to be added, as only these can be pools:
            if (newLiquidityPool_.code.length == 0) {
                revert LiquidityPoolMustBeAContractAddress();
            }
            // Add this to the enumerated list:
            _liquidityPools.add(newLiquidityPool_);
            emit LiquidityPoolAdded(newLiquidityPool_);
        }
        /**
         * @dev function {removeLiquidityPool} onlyOwnerOrFactory
         *
         * Allows the manager to remove a liquidity pool
         *
         * @param removedLiquidityPool_ The address of the old removed liquidity pool
         */
        function removeLiquidityPool(
            address removedLiquidityPool_
        ) external onlyOwnerOrFactory {
            // Remove this from the enumerated list:
            _liquidityPools.remove(removedLiquidityPool_);
            emit LiquidityPoolRemoved(removedLiquidityPool_);
        }
        /**
         * @dev function {isValidCaller}
         *
         * Return if an address is a valid caller
         *
         * @param queryHash_ The code hash being queried
         * @return bool The address is / isn't a valid caller
         */
        function isValidCaller(bytes32 queryHash_) public view returns (bool) {
            return (_validCallerCodeHashes.contains(queryHash_));
        }
        /**
         * @dev function {validCallers}
         *
         * Returns a list of all valid caller code hashes
         *
         * @return validCallerHashes_ a list of all valid caller code hashes
         */
        function validCallers()
            external
            view
            returns (bytes32[] memory validCallerHashes_)
        {
            return (_validCallerCodeHashes.values());
        }
        /**
         * @dev function {addValidCaller} onlyOwnerOrFactory
         *
         * Allows the owner to add the hash of a valid caller
         *
         * @param newValidCallerHash_ The hash of the new valid caller
         */
        function addValidCaller(
            bytes32 newValidCallerHash_
        ) external onlyOwnerOrFactory {
            _validCallerCodeHashes.add(newValidCallerHash_);
            emit ValidCallerAdded(newValidCallerHash_);
        }
        /**
         * @dev function {removeValidCaller} onlyOwnerOrFactory
         *
         * Allows the owner to remove a valid caller
         *
         * @param removedValidCallerHash_ The hash of the old removed valid caller
         */
        function removeValidCaller(
            bytes32 removedValidCallerHash_
        ) external onlyOwnerOrFactory {
            // Remove this from the enumerated list:
            _validCallerCodeHashes.remove(removedValidCallerHash_);
            emit ValidCallerRemoved(removedValidCallerHash_);
        }
        /**
         * @dev function {setProjectTaxRecipient} onlyOwnerOrFactory
         *
         * Allows the manager to set the project tax recipient address
         *
         * @param projectTaxRecipient_ New recipient address
         */
        function setProjectTaxRecipient(
            address projectTaxRecipient_
        ) external onlyOwnerOrFactory {
            projectTaxRecipient = projectTaxRecipient_;
            emit ProjectTaxRecipientUpdated(projectTaxRecipient_);
        }
        /**
         * @dev function {setSwapThresholdBasisPoints} onlyOwnerOrFactory
         *
         * Allows the manager to set the autoswap threshold
         *
         * @param swapThresholdBasisPoints_ New swap threshold in basis points
         */
        function setSwapThresholdBasisPoints(
            uint16 swapThresholdBasisPoints_
        ) external onlyOwnerOrFactory {
            uint256 oldswapThresholdBasisPoints = swapThresholdBasisPoints;
            swapThresholdBasisPoints = swapThresholdBasisPoints_;
            emit AutoSwapThresholdUpdated(
                oldswapThresholdBasisPoints,
                swapThresholdBasisPoints_
            );
        }
        /**
         * @dev function {setProjectTaxRates} onlyOwnerOrFactory
         *
         * Change the tax rates, subject to only ever decreasing
         *
         * @param newProjectBuyTaxBasisPoints_ The new buy tax rate
         * @param newProjectSellTaxBasisPoints_ The new sell tax rate
         */
        function setProjectTaxRates(
            uint16 newProjectBuyTaxBasisPoints_,
            uint16 newProjectSellTaxBasisPoints_
        ) external onlyOwnerOrFactory {
            uint16 oldBuyTaxBasisPoints = projectBuyTaxBasisPoints;
            uint16 oldSellTaxBasisPoints = projectSellTaxBasisPoints;
            projectBuyTaxBasisPoints = newProjectBuyTaxBasisPoints_;
            projectSellTaxBasisPoints = newProjectSellTaxBasisPoints_;
            _tokenHasTax =
                (projectBuyTaxBasisPoints + projectSellTaxBasisPoints) > 0;
            emit ProjectTaxBasisPointsChanged(
                oldBuyTaxBasisPoints,
                newProjectBuyTaxBasisPoints_,
                oldSellTaxBasisPoints,
                newProjectSellTaxBasisPoints_
            );
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the 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 override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev totalBuyTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalBuyTaxBasisPoints() public view returns (uint256) {
            return projectBuyTaxBasisPoints;
        }
        /**
         * @dev totalSellTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalSellTaxBasisPoints() public view returns (uint256) {
            return projectSellTaxBasisPoints;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(
            address account
        ) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(
            address to,
            uint256 amount
        ) public virtual override(IERC20) returns (bool) {
            address owner = _msgSender();
            _transfer(
                owner,
                to,
                amount,
                (isLiquidityPool(owner) || isLiquidityPool(to))
            );
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(
            address owner,
            address spender
        ) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` 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 amount
        ) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * 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 `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(
                from,
                to,
                amount,
                (isLiquidityPool(from) || isLiquidityPool(to))
            );
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(
            address spender,
            uint256 addedValue
        ) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, allowance(owner, spender) + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(
            address spender,
            uint256 subtractedValue
        ) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance < subtractedValue) {
                revert AllowanceDecreasedBelowZero();
            }
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `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.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(
            address from,
            address to,
            uint256 amount,
            bool applyTax
        ) internal virtual {
            _beforeTokenTransfer(from, to, amount);
            // Perform pre-tax validation (e.g. amount doesn't exceed balance, max txn amount)
            uint256 fromBalance = _pretaxValidationAndLimits(from, to, amount);
            // Perform autoswap if eligible
            _autoSwap(from, to);
            // Process taxes
            uint256 amountMinusTax = _taxProcessing(applyTax, to, from, amount);
            _balances[from] = fromBalance - amount;
            _balances[to] += amountMinusTax;
            emit Transfer(from, to, amountMinusTax);
            _afterTokenTransfer(from, to, amount);
        }
        /**
         * @dev function {_pretaxValidationAndLimits}
         *
         * Perform validation on pre-tax amounts
         *
         * @param from_ From address for the transaction
         * @param to_ To address for the transaction
         * @param amount_ Amount of the transaction
         */
        function _pretaxValidationAndLimits(
            address from_,
            address to_,
            uint256 amount_
        ) internal view returns (uint256 fromBalance_) {
            // This can't be a transfer to the liquidity pool before the funding date
            // UNLESS the from address is this contract. This ensures that the initial
            // LP funding transaction is from this contract using the supply of tokens
            // designated for the LP pool, and therefore the initial price in the pool
            // is being set as expected.
            //
            // This protects from, for example, tokens from a team minted supply being
            // paired with ETH and added to the pool, setting the initial price, BEFORE
            // the initial liquidity is added through this contract.
            if (to_ == uniswapV2Pair && from_ != address(this) && fundedDate == 0) {
                revert InitialLiquidityNotYetAdded();
            }
            if (from_ == address(0)) {
                revert TransferFromZeroAddress();
            }
            if (to_ == address(0)) {
                revert TransferToZeroAddress();
            }
            fromBalance_ = _balances[from_];
            if (fromBalance_ < amount_) {
                revert TransferAmountExceedsBalance();
            }
            return (fromBalance_);
        }
        /**
         * @dev function {_taxProcessing}
         *
         * Perform tax processing
         *
         * @param applyTax_ Do we apply tax to this transaction?
         * @param to_ The reciever of the token
         * @param from_ The sender of the token
         * @param sentAmount_ The amount being send
         * @return amountLessTax_ The amount that will be recieved, i.e. the send amount minus tax
         */
        function _taxProcessing(
            bool applyTax_,
            address to_,
            address from_,
            uint256 sentAmount_
        ) internal returns (uint256 amountLessTax_) {
            amountLessTax_ = sentAmount_;
            unchecked {
                if (_tokenHasTax && applyTax_ && !_autoSwapInProgress) {
                    uint256 tax;
                    // on sell
                    if (isLiquidityPool(to_) && totalSellTaxBasisPoints() > 0) {
                        if (projectSellTaxBasisPoints > 0) {
                            uint256 projectTax = ((sentAmount_ *
                                projectSellTaxBasisPoints) / BP_DENOM);
                            projectTaxPendingSwap += uint128(projectTax);
                            tax += projectTax;
                        }
                    }
                    // on buy
                    else if (
                        isLiquidityPool(from_) && totalBuyTaxBasisPoints() > 0
                    ) {
                        if (projectBuyTaxBasisPoints > 0) {
                            uint256 projectTax = ((sentAmount_ *
                                projectBuyTaxBasisPoints) / BP_DENOM);
                            projectTaxPendingSwap += uint128(projectTax);
                            tax += projectTax;
                        }
                    }
                    if (tax > 0) {
                        _balances[address(this)] += tax;
                        emit Transfer(from_, address(this), tax);
                        amountLessTax_ -= tax;
                    }
                }
            }
            return (amountLessTax_);
        }
        /**
         * @dev function {_autoSwap}
         *
         * Automate the swap of accumulated tax fees to native token
         *
         * @param from_ The sender of the token
         * @param to_ The recipient of the token
         */
        function _autoSwap(address from_, address to_) internal {
            if (_tokenHasTax) {
                uint256 contractBalance = balanceOf(address(this));
                uint256 swapBalance = contractBalance;
                uint256 swapThresholdInTokens = (_totalSupply *
                    swapThresholdBasisPoints) / BP_DENOM;
                if (
                    _eligibleForSwap(from_, to_, swapBalance, swapThresholdInTokens)
                ) {
                    // Store that a swap back is in progress:
                    _autoSwapInProgress = true;
                    // Check if we need to reduce the amount of tokens for this swap:
                    if (
                        swapBalance >
                        swapThresholdInTokens * MAX_SWAP_THRESHOLD_MULTIPLE
                    ) {
                        swapBalance =
                            swapThresholdInTokens *
                            MAX_SWAP_THRESHOLD_MULTIPLE;
                    }
                    // Perform the auto swap to pair token
                    _swapTax(swapBalance, contractBalance);
                    // Flag that the autoswap is complete:
                    _autoSwapInProgress = false;
                }
            }
        }
        /**
         * @dev function {_eligibleForSwap}
         *
         * Is the current transfer eligible for autoswap
         *
         * @param from_ The sender of the token
         * @param to_ The recipient of the token
         * @param taxBalance_ The current accumulated tax balance
         * @param swapThresholdInTokens_ The swap threshold as a token amount
         */
        function _eligibleForSwap(
            address from_,
            address to_,
            uint256 taxBalance_,
            uint256 swapThresholdInTokens_
        ) internal view returns (bool) {
            return (taxBalance_ >= swapThresholdInTokens_ &&
                !_autoSwapInProgress &&
                !isLiquidityPool(from_) &&
                from_ != address(_uniswapRouter) &&
                to_ != address(_uniswapRouter) &&
                from_ != address(this));
        }
        /**
         * @dev function {_swapTax}
         *
         * Swap tokens taken as tax for pair token
         *
         * @param swapBalance_ The current accumulated tax balance to swap
         * @param contractBalance_ The current accumulated total tax balance
         */
        function _swapTax(uint256 swapBalance_, uint256 contractBalance_) internal {
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = pairToken;
            // Wrap external calls in try / catch to handle errors
            try
                _uniswapRouter
                    .swapExactTokensForTokensSupportingFeeOnTransferTokens(
                        swapBalance_,
                        0,
                        path,
                        projectTaxRecipient,
                        block.timestamp + 600
                    )
            {
                // We will not have swapped all tax tokens IF the amount was greater than the max auto swap.
                // We therefore cannot just set the pending swap counters to 0. Instead, in this scenario,
                // we must reduce them in proportion to the swap amount vs the remaining balance + swap
                // amount.
                //
                // For example:
                //  * swap Balance is 250
                //  * contract balance is 385.
                //  * projectTaxPendingSwap is 300
                //
                // The new total for the projectTaxPendingSwap is:
                //   = 300 - ((300 * 250) / 385)
                //   = 300 - 194
                //   = 106
                if (swapBalance_ < contractBalance_) {
                    projectTaxPendingSwap -= uint128(
                        (projectTaxPendingSwap * swapBalance_) / contractBalance_
                    );
                } else {
                    projectTaxPendingSwap = 0;
                }
            } catch {
                // Dont allow a failed external call (in this case to uniswap) to stop a transfer.
                // Emit that this has occured and continue.
                emit ExternalCallError(5);
            }
        }
        /**
         * @dev distributeTaxTokens
         *
         * Allows the distribution of tax tokens to the designated recipient(s)
         *
         * As part of standard processing the tax token balance being above the threshold
         * will trigger an autoswap to ETH and distribution of this ETH to the designated
         * recipients. This is automatic and there is no need for user involvement.
         *
         * As part of this swap there are a number of calculations performed, particularly
         * if the tax balance is above MAX_SWAP_THRESHOLD_MULTIPLE.
         *
         * Testing indicates that these calculations are safe. But given the data / code
         * interactions it remains possible that some edge case set of scenarios may cause
         * an issue with these calculations.
         *
         * This method is therefore provided as a 'fallback' option to safely distribute
         * accumulated taxes from the contract, with a direct transfer of the ERC20 tokens
         * themselves.
         */
        function distributeTaxTokens() external {
            if (projectTaxPendingSwap > 0) {
                uint256 projectDistribution = projectTaxPendingSwap;
                projectTaxPendingSwap = 0;
                _transfer(
                    address(this),
                    projectTaxRecipient,
                    projectDistribution,
                    false
                );
            }
        }
        /**
         * @dev function {withdrawETH} onlyOwnerOrFactory
         *
         * A withdraw function to allow ETH to be withdrawn by the manager
         *
         * This contract should never hold ETH. The only envisaged scenario where
         * it might hold ETH is a failed autoswap where the uniswap swap has completed,
         * the recipient of ETH reverts, the contract then wraps to WETH and the
         * wrap to WETH fails.
         *
         * This feels unlikely. But, for safety, we include this method.
         *
         * @param amount_ The amount to withdraw
         */
        function withdrawETH(uint256 amount_) external onlyOwnerOrFactory {
            (bool success, ) = _msgSender().call{value: amount_}("");
            if (!success) {
                revert TransferFailed();
            }
        }
        /**
         * @dev function {withdrawERC20} onlyOwnerOrFactory
         *
         * A withdraw function to allow ERC20s (except address(this)) to be withdrawn.
         *
         * This contract should never hold ERC20s other than tax tokens. The only envisaged
         * scenario where it might hold an ERC20 is a failed autoswap where the uniswap swap
         * has completed, the recipient of ETH reverts, the contract then wraps to WETH, the
         * wrap to WETH succeeds, BUT then the transfer of WETH fails.
         *
         * This feels even less likely than the scenario where ETH is held on the contract.
         * But, for safety, we include this method.
         *
         * @param token_ The ERC20 contract
         * @param amount_ The amount to withdraw
         */
        function withdrawERC20(
            address token_,
            uint256 amount_
        ) external onlyOwnerOrFactory {
            if (token_ == address(this)) {
                revert CannotWithdrawThisToken();
            }
            IERC20(token_).safeTransfer(_msgSender(), amount_);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            if (account == address(0)) {
                revert MintToZeroAddress();
            }
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += uint128(amount);
            unchecked {
                // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                _balances[account] += amount;
            }
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            if (account == address(0)) {
                revert BurnFromTheZeroAddress();
            }
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            if (accountBalance < amount) {
                revert BurnExceedsBalance();
            }
            unchecked {
                _balances[account] = accountBalance - amount;
                // Overflow not possible: amount <= accountBalance <= totalSupply.
                _totalSupply -= uint128(amount);
            }
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            if (owner == address(0)) {
                revert ApproveFromTheZeroAddress();
            }
            if (spender == address(0)) {
                revert ApproveToTheZeroAddress();
            }
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                if (currentAllowance < amount) {
                    revert InsufficientAllowance();
                }
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Destroys a `value` amount of tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 value) public virtual {
            _burn(_msgSender(), value);
        }
        /**
         * @dev Destroys a `value` amount of tokens from `account`, deducting from
         * the caller's allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `value`.
         */
        function burnFrom(address account, uint256 value) public virtual {
            _spendAllowance(account, _msgSender(), value);
            _burn(account, value);
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import "@openzeppelin/contracts/governance/IGovernor.sol";
    interface IAgentFactory {
        function proposeAgent(
            string memory name,
            string memory symbol,
            string memory tokenURI,
            uint8[] memory cores,
            bytes32 tbaSalt,
            address tbaImplementation,
            uint32 daoVotingPeriod,
            uint256 daoThreshold
        ) external returns (uint256);
        function withdraw(uint256 id) external;
        function totalAgents() external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "./IERC20Config.sol";
    import "./IErrors.sol";
    interface IAgentToken is
        IERC20,
        IERC20Config,
        IERC20Metadata,
        IErrors
    {
        event AutoSwapThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
        event ExternalCallError(uint256 identifier);
        event InitialLiquidityAdded(
            uint256 tokenA,
            uint256 tokenB,
            uint256 lpToken
        );
        event LimitsUpdated(
            uint256 oldMaxTokensPerTransaction,
            uint256 newMaxTokensPerTransaction,
            uint256 oldMaxTokensPerWallet,
            uint256 newMaxTokensPerWallet
        );
        event LiquidityPoolCreated(address addedPool);
        event LiquidityPoolAdded(address addedPool);
        event LiquidityPoolRemoved(address removedPool);
        event ProjectTaxBasisPointsChanged(
            uint256 oldBuyBasisPoints,
            uint256 newBuyBasisPoints,
            uint256 oldSellBasisPoints,
            uint256 newSellBasisPoints
        );
        event RevenueAutoSwap();
        event ProjectTaxRecipientUpdated(address treasury);
        event ValidCallerAdded(bytes32 addedValidCaller);
        event ValidCallerRemoved(bytes32 removedValidCaller);
        /**
         * @dev function {addInitialLiquidity}
         *
         * Add initial liquidity to the uniswap pair
         *
         * @param lpOwner The recipient of LP tokens
         */
        function addInitialLiquidity(address lpOwner) external;
        /**
         * @dev function {isLiquidityPool}
         *
         * Return if an address is a liquidity pool
         *
         * @param queryAddress_ The address being queried
         * @return bool The address is / isn't a liquidity pool
         */
        function isLiquidityPool(
            address queryAddress_
        ) external view returns (bool);
        /**
         * @dev function {liquidityPools}
         *
         * Returns a list of all liquidity pools
         *
         * @return liquidityPools_ a list of all liquidity pools
         */
        function liquidityPools()
            external
            view
            returns (address[] memory liquidityPools_);
        /**
         * @dev function {addLiquidityPool} onlyOwner
         *
         * Allows the manager to add a liquidity pool to the pool enumerable set
         *
         * @param newLiquidityPool_ The address of the new liquidity pool
         */
        function addLiquidityPool(address newLiquidityPool_) external;
        /**
         * @dev function {removeLiquidityPool} onlyOwner
         *
         * Allows the manager to remove a liquidity pool
         *
         * @param removedLiquidityPool_ The address of the old removed liquidity pool
         */
        function removeLiquidityPool(address removedLiquidityPool_) external;
        /**
         * @dev function {isValidCaller}
         *
         * Return if an address is a valid caller
         *
         * @param queryHash_ The code hash being queried
         * @return bool The address is / isn't a valid caller
         */
        function isValidCaller(bytes32 queryHash_) external view returns (bool);
        /**
         * @dev function {validCallers}
         *
         * Returns a list of all valid caller code hashes
         *
         * @return validCallerHashes_ a list of all valid caller code hashes
         */
        function validCallers()
            external
            view
            returns (bytes32[] memory validCallerHashes_);
        /**
         * @dev function {addValidCaller} onlyOwner
         *
         * Allows the owner to add the hash of a valid caller
         *
         * @param newValidCallerHash_ The hash of the new valid caller
         */
        function addValidCaller(bytes32 newValidCallerHash_) external;
        /**
         * @dev function {removeValidCaller} onlyOwner
         *
         * Allows the owner to remove a valid caller
         *
         * @param removedValidCallerHash_ The hash of the old removed valid caller
         */
        function removeValidCaller(bytes32 removedValidCallerHash_) external;
        /**
         * @dev function {setProjectTaxRecipient} onlyOwner
         *
         * Allows the manager to set the project tax recipient address
         *
         * @param projectTaxRecipient_ New recipient address
         */
        function setProjectTaxRecipient(address projectTaxRecipient_) external;
        /**
         * @dev function {setSwapThresholdBasisPoints} onlyOwner
         *
         * Allows the manager to set the autoswap threshold
         *
         * @param swapThresholdBasisPoints_ New swap threshold in basis points
         */
        function setSwapThresholdBasisPoints(
            uint16 swapThresholdBasisPoints_
        ) external;
        /**
         * @dev function {setProjectTaxRates} onlyOwner
         *
         * Change the tax rates, subject to only ever decreasing
         *
         * @param newProjectBuyTaxBasisPoints_ The new buy tax rate
         * @param newProjectSellTaxBasisPoints_ The new sell tax rate
         */
        function setProjectTaxRates(
            uint16 newProjectBuyTaxBasisPoints_,
            uint16 newProjectSellTaxBasisPoints_
        ) external;
        /**
         * @dev totalBuyTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalBuyTaxBasisPoints() external view returns (uint256);
        /**
         * @dev totalSellTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalSellTaxBasisPoints() external view returns (uint256);
        /**
         * @dev distributeTaxTokens
         *
         * Allows the distribution of tax tokens to the designated recipient(s)
         *
         * As part of standard processing the tax token balance being above the threshold
         * will trigger an autoswap to ETH and distribution of this ETH to the designated
         * recipients. This is automatic and there is no need for user involvement.
         *
         * As part of this swap there are a number of calculations performed, particularly
         * if the tax balance is above MAX_SWAP_THRESHOLD_MULTIPLE.
         *
         * Testing indicates that these calculations are safe. But given the data / code
         * interactions it remains possible that some edge case set of scenarios may cause
         * an issue with these calculations.
         *
         * This method is therefore provided as a 'fallback' option to safely distribute
         * accumulated taxes from the contract, with a direct transfer of the ERC20 tokens
         * themselves.
         */
        function distributeTaxTokens() external;
        /**
         * @dev function {withdrawETH} onlyOwner
         *
         * A withdraw function to allow ETH to be withdrawn by the manager
         *
         * This contract should never hold ETH. The only envisaged scenario where
         * it might hold ETH is a failed autoswap where the uniswap swap has completed,
         * the recipient of ETH reverts, the contract then wraps to WETH and the
         * wrap to WETH fails.
         *
         * This feels unlikely. But, for safety, we include this method.
         *
         * @param amount_ The amount to withdraw
         */
        function withdrawETH(uint256 amount_) external;
        /**
         * @dev function {withdrawERC20} onlyOwner
         *
         * A withdraw function to allow ERC20s (except address(this)) to be withdrawn.
         *
         * This contract should never hold ERC20s other than tax tokens. The only envisaged
         * scenario where it might hold an ERC20 is a failed autoswap where the uniswap swap
         * has completed, the recipient of ETH reverts, the contract then wraps to WETH, the
         * wrap to WETH succeeds, BUT then the transfer of WETH fails.
         *
         * This feels even less likely than the scenario where ETH is held on the contract.
         * But, for safety, we include this method.
         *
         * @param token_ The ERC20 contract
         * @param amount_ The amount to withdraw
         */
        function withdrawERC20(address token_, uint256 amount_) external;
        /**
         * @dev Destroys a `value` amount of tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 value) external;
        /**
         * @dev Destroys a `value` amount of tokens from `account`, deducting from
         * the caller's allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `value`.
         */
        function burnFrom(address account, uint256 value) external;
        /**
         * @dev {initializer}
         *
         * @param integrationAddresses_ The project owner, uniswap router, LP currency
         * @param baseParams_ configuration of this ERC20.
         * param supplyParams_ Supply configuration of this ERC20.
         * param taxParams_  Tax configuration of this ERC20
         * param taxParams_  Launch pool configuration of this ERC20
         * param lpSupply_  Initial supply to be minted for LP
         */
        function initialize(
            address[3] memory integrationAddresses_,
            bytes memory baseParams_,
            bytes memory supplyParams_,
            bytes memory taxParams_
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    interface IERC20Config {
      struct ERC20Config {
        bytes baseParameters;
        bytes supplyParameters;
        bytes taxParameters;
        bytes poolParameters;
      }
      struct ERC20BaseParameters {
        string name;
        string symbol;
      }
      struct ERC20SupplyParameters {
        uint256 maxSupply;
        uint256 lpSupply;
        uint256 vaultSupply;
        uint256 maxTokensPerWallet;
        uint256 maxTokensPerTxn;
        uint256 botProtectionDurationInSeconds;
        address vault;
      }
      struct ERC20TaxParameters {
        uint256 projectBuyTaxBasisPoints;
        uint256 projectSellTaxBasisPoints;
        uint256 taxSwapThresholdBasisPoints;
        address projectTaxRecipient;
      }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    interface IErrors {
      enum BondingCurveErrorType {
        OK, //                                                  No error
        INVALID_NUMITEMS, //                                    The numItem value is 0
        SPOT_PRICE_OVERFLOW //                                  The updated spot price doesn't fit into 128 bits
      }
      error AdapterParamsMustBeEmpty(); //                      The adapter parameters on this LZ call must be empty.
      error AdditionToPoolIsBelowPerTransactionMinimum(); //    The contribution amount is less than the minimum.
      error AdditionToPoolWouldExceedPoolCap(); //              This addition to the pool would exceed the pool cap.
      error AdditionToPoolWouldExceedPerAddressCap(); //        This addition to the pool would exceed the per address cap.
      error AddressAlreadySet(); //                             The address being set can only be set once, and is already non-0.
      error AllowanceDecreasedBelowZero(); //                   You cannot decrease the allowance below zero.
      error AlreadyInitialised(); //                            The contract is already initialised: it cannot be initialised twice!
      error ApprovalCallerNotOwnerNorApproved(); //             The caller must own the token or be an approved operator.
      error ApproveFromTheZeroAddress(); //                     Approval cannot be called from the zero address (indeed, how have you??).
      error ApproveToTheZeroAddress(); //                       Approval cannot be given to the zero address.
      error ApprovalQueryForNonexistentToken(); //              The token does not exist.
      error AuctionStatusIsNotEnded(); //                       Throw if the action required the auction to be closed, and it isn't.
      error AuctionStatusIsNotOpen(); //                        Throw if the action requires the auction to be open, and it isn't.
      error AuxCallFailed(
        address[] modules,
        uint256 value,
        bytes data,
        uint256 txGas
      ); //                                                     An auxilliary call from the drop factory failed.
      error BalanceMismatch(); //                               An error when comparing balance amounts.
      error BalanceQueryForZeroAddress(); //                    Cannot query the balance for the zero address.
      error BidMustBeBelowTheFloorWhenReducingQuantity(); //    Only bids that are below the floor can reduce the quantity of the bid.
      error BidMustBeBelowTheFloorForRefundDuringAuction(); //  Only bids that are below the floor can be refunded during the auction.
      error BondingCurveError(BondingCurveErrorType error); //  An error of the type specified has occured in bonding curve processing.
      error BurnExceedsBalance(); //                            The amount you have selected to burn exceeds the addresses balance.
      error BurnFromTheZeroAddress(); //                        Tokens cannot be burned from the zero address. (Also, how have you called this!?!)
      error CallerIsNotDepositBoxOwner(); //                    The caller is not the owner of the deposit box.
      error CallerIsNotFactory(); //                            The caller of this function must match the factory address in storage.
      error CallerIsNotFactoryOrProjectOwner(); //              The caller of this function must match the factory address OR project owner address.
      error CallerIsNotFactoryProjectOwnerOrPool(); //          The caller of this function must match the factory address, project owner or pool address.
      error CallerIsNotTheOwner(); //                           The caller is not the owner of this contract.
      error CallerIsNotTheManager(); //                         The caller is not the manager of this contract.
      error CallerMustBeLzApp(); //                             The caller must be an LZ application.
      error CallerIsNotPlatformAdmin(address caller); //        The caller of this function must be part of the platformAdmin group.
      error CallerIsNotSuperAdmin(address caller); //           The caller of this function must match the superAdmin address in storage.
      error CannotAddLiquidityOnCreateAndUseDRIPool(); //       Cannot use both liquidity added on create and a DRIPool in the same token.
      error CannotSetNewOwnerToTheZeroAddress(); //             You can't set the owner of this contract to the zero address (address(0)).
      error CannotSetToZeroAddress(); //                        The corresponding address cannot be set to the zero address (address(0)).
      error CannotSetNewManagerToTheZeroAddress(); //           Cannot transfer the manager to the zero address (address(0)).
      error CannotWithdrawThisToken(); //                       Cannot withdraw the specified token.
      error CanOnlyReduce(); //                                 The given operation can only reduce the value specified.
      error CollectionAlreadyRevealed(); //                     The collection is already revealed; you cannot call reveal again.
      error ContractIsDecommissioned(); //                      This contract is decommissioned!
      error ContractIsPaused(); //                              The call requires the contract to be unpaused, and it is paused.
      error ContractIsNotPaused(); //                           The call required the contract to be paused, and it is NOT paused.
      error DecreasedAllowanceBelowZero(); //                   The request would decrease the allowance below zero, and that is not allowed.
      error DestinationIsNotTrustedSource(); //                 The destination that is being called through LZ has not been set as trusted.
      error DeployerOnly(); //                                  This method can only be called by the deployer address.
      error DeploymentError(); //                               Error on deployment.
      error DepositBoxIsNotOpen(); //                           This action cannot complete as the deposit box is not open.
      error DriPoolAddressCannotBeAddressZero(); //             The Dri Pool address cannot be the zero address.
      error GasLimitIsTooLow(); //                              The gas limit for the LayerZero call is too low.
      error IncorrectConfirmationValue(); //                    You need to enter the right confirmation value to call this funtion (usually 69420).
      error IncorrectPayment(); //                              The function call did not include passing the correct payment.
      error InitialLiquidityAlreadyAdded(); //                  Initial liquidity has already been added. You can't do it again.
      error InitialLiquidityNotYetAdded(); //                   Initial liquidity needs to have been added for this to succedd.
      error InsufficientAllowance(); //                         There is not a high enough allowance for this operation.
      error InvalidAdapterParams(); //                          The current adapter params for LayerZero on this contract won't work :(.
      error InvalidAddress(); //                                An address being processed in the function is not valid.
      error InvalidEndpointCaller(); //                         The calling address is not a valid LZ endpoint. The LZ endpoint was set at contract creation
      //                                                        and cannot be altered after. Check the address LZ endpoint address on the contract.
      error InvalidMinGas(); //                                 The minimum gas setting for LZ in invalid.
      error InvalidOracleSignature(); //                        The signature provided with the contract call is not valid, either in format or signer.
      error InvalidPayload(); //                                The LZ payload is invalid
      error InvalidReceiver(); //                               The address used as a target for funds is not valid.
      error InvalidSourceSendingContract(); //                  The LZ message is being related from a source contract on another chain that is NOT trusted.
      error InvalidTotalShares(); //                            Total shares must equal 100 percent in basis points.
      error LimitsCanOnlyBeRaised(); //                          Limits are UP ONLY.
      error ListLengthMismatch(); //                            Two or more lists were compared and they did not match length.
      error LiquidityPoolMustBeAContractAddress(); //           Cannot add a non-contract as a liquidity pool.
      error LiquidityPoolCannotBeAddressZero(); //              Cannot add a liquidity pool from the zero address.
      error LPLockUpMustFitUint88(); //                         LP lockup is held in a uint88, so must fit.
      error NoTrustedPathRecord(); //                           LZ needs a trusted path record for this to work. What's that, you ask?
      error MachineAddressCannotBeAddressZero(); //             Cannot set the machine address to the zero address.
      error ManagerUnauthorizedAccount(); //                    The caller is not the pending manager.
      error MaxBidQuantityIs255(); //                           Validation: as we use a uint8 array to track bid positions the max bid quantity is 255.
      error MaxPublicMintAllowanceExceeded(
        uint256 requested,
        uint256 alreadyMinted,
        uint256 maxAllowance
      ); //                                                     The calling address has requested a quantity that would exceed the max allowance.
      error MaxSupplyTooHigh(); //                              Max supply must fit in a uint128.
      error MaxTokensPerWalletExceeded(); //                    The transfer would exceed the max tokens per wallet limit.
      error MaxTokensPerTxnExceeded(); //                       The transfer would exceed the max tokens per transaction limit.
      error MetadataIsLocked(); //                              The metadata on this contract is locked; it cannot be altered!
      error MinGasLimitNotSet(); //                             The minimum gas limit for LayerZero has not been set.
      error MintERC2309QuantityExceedsLimit(); //               The `quantity` minted with ERC2309 exceeds the safety limit.
      error MintingIsClosedForever(); //                        Minting is, as the error suggests, so over (and locked forever).
      error MintToZeroAddress(); //                             Cannot mint to the zero address.
      error MintZeroQuantity(); //                              The quantity of tokens minted must be more than zero.
      error NewBuyTaxBasisPointsExceedsMaximum(); //            Project owner trying to set the tax rate too high.
      error NewSellTaxBasisPointsExceedsMaximum(); //           Project owner trying to set the tax rate too high.
      error NoETHForLiquidityPair(); //                         No ETH has been provided for the liquidity pair.
      error TaxPeriodStillInForce(); //                         The minimum tax period has not yet expired.
      error NoPaymentDue(); //                                  No payment is due for this address.
      error NoRefundForCaller(); //                             Error thrown when the calling address has no refund owed.
      error NoStoredMessage(); //                               There is no stored message matching the passed parameters.
      error NothingToClaim(); //                                The calling address has nothing to claim.
      error NoTokenForLiquidityPair(); //                       There is no token to add to the LP.
      error OperationDidNotSucceed(); //                        The operation failed (vague much?).
      error OracleSignatureHasExpired(); //                     A signature has been provided but it is too old.
      error OwnershipNotInitializedForExtraData(); //           The `extraData` cannot be set on an uninitialized ownership slot.
      error OwnerQueryForNonexistentToken(); //                 The token does not exist.
      error CallerIsNotAdminNorFactory();   //                  The caller of this function must match the factory address or be an admin.
      error ParametersDoNotMatchSignedMessage(); //             The parameters passed with the signed message do not match the message itself.
      error ParamTooLargeStartDate(); //                        The passed parameter exceeds the var type max.
      error ParamTooLargeEndDate(); //                          The passed parameter exceeds the var type max.
      error ParamTooLargeMinETH(); //                           The passed parameter exceeds the var type max.
      error ParamTooLargePerAddressMax(); //                    The passed parameter exceeds the var type max.
      error ParamTooLargeVestingDays(); //                      The passed parameter exceeds the var type max.
      error ParamTooLargePoolSupply(); //                       The passed parameter exceeds the var type max.
      error ParamTooLargePoolPerTxnMinETH(); //                 The passed parameter exceeds the var type max.
      error PassedConfigDoesNotMatchApproved(); //              The config provided on the call does not match the approved config.
      error PauseCutOffHasPassed(); //                          The time period in which we can pause has passed; this contract can no longer be paused.
      error PaymentMustCoverPerMintFee(); //                    The payment passed must at least cover the per mint fee for the quantity requested.
      error PermitDidNotSucceed(); //                           The safeERC20 permit failed.
      error PlatformAdminCannotBeAddressZero(); //              We cannot use the zero address (address(0)) as a platformAdmin.
      error PlatformTreasuryCannotBeAddressZero(); //           The treasury address cannot be set to the zero address.
      error PoolIsAboveMinimum(); //                            You required the pool to be below the minimum, and it is not
      error PoolIsBelowMinimum(); //                            You required the pool to be above the minimum, and it is not
      error PoolPhaseIsClosed(); //                             The block.timestamp is either before the pool is open or after it is closed.
      error PoolPhaseIsNotAfter(); //                           The block.timestamp is either before or during the pool open phase.
      error PoolVestingNotYetComplete(); //                     Tokens in the pool are not yet vested.
      error ProjectOwnerCannotBeAddressZero(); //               The project owner has to be a non zero address.
      error ProofInvalid(); //                                  The provided proof is not valid with the provided arguments.
      error QuantityExceedsRemainingCollectionSupply(); //      The requested quantity would breach the collection supply.
      error QuantityExceedsRemainingPhaseSupply(); //           The requested quantity would breach the phase supply.
      error QuantityExceedsMaxPossibleCollectionSupply(); //    The requested quantity would breach the maximum trackable supply
      error ReferralIdAlreadyUsed(); //                         This referral ID has already been used; they are one use only.
      error RequestingMoreThanAvailableBalance(); //             The request exceeds the available balance.
      error RequestingMoreThanRemainingAllocation(
        uint256 previouslyMinted,
        uint256 requested,
        uint256 remainingAllocation
      ); //                                                     Number of tokens requested for this mint exceeds the remaining allocation (taking the
      //                                                        original allocation from the list and deducting minted tokens).
      error RoyaltyFeeWillExceedSalePrice(); //                 The ERC2981 royalty specified will exceed the sale price.
      error ShareTotalCannotBeZero(); //                        The total of all the shares cannot be nothing.
      error SliceOutOfBounds(); //                              The bytes slice operation was out of bounds.
      error SliceOverflow(); //                                 The bytes slice operation overlowed.
      error SuperAdminCannotBeAddressZero(); //                 The superAdmin cannot be the sero address (address(0)).
      error SupplyTotalMismatch(); //                           The sum of the team supply and lp supply does not match.
      error SupportWindowIsNotOpen(); //                        The project owner has not requested support within the support request expiry window.
      error TaxFreeAddressCannotBeAddressZero(); //             A tax free address cannot be address(0)
      error TemplateCannotBeAddressZero(); //                   The address for a template cannot be address zero (address(0)).
      error TemplateNotFound(); //                              There is no template that matches the passed template Id.
      error ThisMintIsClosed(); //                              It's over (well, this mint is, anyway).
      error TotalSharesMustMatchDenominator(); //               The total of all shares must equal the denominator value.
      error TransferAmountExceedsBalance(); //                  The transfer amount exceeds the accounts available balance.
      error TransferCallerNotOwnerNorApproved(); //             The caller must own the token or be an approved operator.
      error TransferFailed(); //                                The transfer has failed.
      error TransferFromIncorrectOwner(); //                    The token must be owned by `from`.
      error TransferToNonERC721ReceiverImplementer(); //        Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
      error TransferFromZeroAddress(); //                       Cannot transfer from the zero address. Indeed, this surely is impossible, and likely a waste to check??
      error TransferToZeroAddress(); //                         Cannot transfer to the zero address.
      error UnrecognisedVRFMode(); //                           Currently supported VRF modes are 0: chainlink and 1: arrng
      error URIQueryForNonexistentToken(); //                   The token does not exist.
      error ValueExceedsMaximum(); //                           The value sent exceeds the maximum allowed (super useful explanation huh?).
      error VRFCoordinatorCannotBeAddressZero(); //             The VRF coordinator cannot be the zero address (address(0)).
    }

    File 2 of 2: AgentToken
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
    pragma solidity ^0.8.20;
    import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
    import {Initializable} from "../proxy/utils/Initializable.sol";
    /**
     * @dev Contract module which provides access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is specified at deployment time in the constructor for `Ownable`. This
     * can later be changed with {transferOwnership} and {acceptOwnership}.
     *
     * This module is used through inheritance. It will make available all functions
     * from parent (Ownable).
     */
    abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
        /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
        struct Ownable2StepStorage {
            address _pendingOwner;
        }
        // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
        bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
        function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
            assembly {
                $.slot := Ownable2StepStorageLocation
            }
        }
        event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
        function __Ownable2Step_init() internal onlyInitializing {
        }
        function __Ownable2Step_init_unchained() internal onlyInitializing {
        }
        /**
         * @dev Returns the address of the pending owner.
         */
        function pendingOwner() public view virtual returns (address) {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            return $._pendingOwner;
        }
        /**
         * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual override onlyOwner {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            $._pendingOwner = newOwner;
            emit OwnershipTransferStarted(owner(), newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual override {
            Ownable2StepStorage storage $ = _getOwnable2StepStorage();
            delete $._pendingOwner;
            super._transferOwnership(newOwner);
        }
        /**
         * @dev The new owner accepts the ownership transfer.
         */
        function acceptOwnership() public virtual {
            address sender = _msgSender();
            if (pendingOwner() != sender) {
                revert OwnableUnauthorizedAccount(sender);
            }
            _transferOwnership(sender);
        }
    }
    // 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) (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;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (governance/IGovernor.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../interfaces/IERC165.sol";
    import {IERC6372} from "../interfaces/IERC6372.sol";
    /**
     * @dev Interface of the {Governor} core.
     */
    interface IGovernor is IERC165, IERC6372 {
        enum ProposalState {
            Pending,
            Active,
            Canceled,
            Defeated,
            Succeeded,
            Queued,
            Expired,
            Executed
        }
        /**
         * @dev Empty proposal or a mismatch between the parameters length for a proposal call.
         */
        error GovernorInvalidProposalLength(uint256 targets, uint256 calldatas, uint256 values);
        /**
         * @dev The vote was already cast.
         */
        error GovernorAlreadyCastVote(address voter);
        /**
         * @dev Token deposits are disabled in this contract.
         */
        error GovernorDisabledDeposit();
        /**
         * @dev The `account` is not a proposer.
         */
        error GovernorOnlyProposer(address account);
        /**
         * @dev The `account` is not the governance executor.
         */
        error GovernorOnlyExecutor(address account);
        /**
         * @dev The `proposalId` doesn't exist.
         */
        error GovernorNonexistentProposal(uint256 proposalId);
        /**
         * @dev The current state of a proposal is not the required for performing an operation.
         * The `expectedStates` is a bitmap with the bits enabled for each ProposalState enum position
         * counting from right to left.
         *
         * NOTE: If `expectedState` is `bytes32(0)`, the proposal is expected to not be in any state (i.e. not exist).
         * This is the case when a proposal that is expected to be unset is already initiated (the proposal is duplicated).
         *
         * See {Governor-_encodeStateBitmap}.
         */
        error GovernorUnexpectedProposalState(uint256 proposalId, ProposalState current, bytes32 expectedStates);
        /**
         * @dev The voting period set is not a valid period.
         */
        error GovernorInvalidVotingPeriod(uint256 votingPeriod);
        /**
         * @dev The `proposer` does not have the required votes to create a proposal.
         */
        error GovernorInsufficientProposerVotes(address proposer, uint256 votes, uint256 threshold);
        /**
         * @dev The `proposer` is not allowed to create a proposal.
         */
        error GovernorRestrictedProposer(address proposer);
        /**
         * @dev The vote type used is not valid for the corresponding counting module.
         */
        error GovernorInvalidVoteType();
        /**
         * @dev Queue operation is not implemented for this governor. Execute should be called directly.
         */
        error GovernorQueueNotImplemented();
        /**
         * @dev The proposal hasn't been queued yet.
         */
        error GovernorNotQueuedProposal(uint256 proposalId);
        /**
         * @dev The proposal has already been queued.
         */
        error GovernorAlreadyQueuedProposal(uint256 proposalId);
        /**
         * @dev The provided signature is not valid for the expected `voter`.
         * If the `voter` is a contract, the signature is not valid using {IERC1271-isValidSignature}.
         */
        error GovernorInvalidSignature(address voter);
        /**
         * @dev Emitted when a proposal is created.
         */
        event ProposalCreated(
            uint256 proposalId,
            address proposer,
            address[] targets,
            uint256[] values,
            string[] signatures,
            bytes[] calldatas,
            uint256 voteStart,
            uint256 voteEnd,
            string description
        );
        /**
         * @dev Emitted when a proposal is queued.
         */
        event ProposalQueued(uint256 proposalId, uint256 etaSeconds);
        /**
         * @dev Emitted when a proposal is executed.
         */
        event ProposalExecuted(uint256 proposalId);
        /**
         * @dev Emitted when a proposal is canceled.
         */
        event ProposalCanceled(uint256 proposalId);
        /**
         * @dev Emitted when a vote is cast without params.
         *
         * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
         */
        event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);
        /**
         * @dev Emitted when a vote is cast with params.
         *
         * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
         * `params` are additional encoded parameters. Their interpepretation also depends on the voting module used.
         */
        event VoteCastWithParams(
            address indexed voter,
            uint256 proposalId,
            uint8 support,
            uint256 weight,
            string reason,
            bytes params
        );
        /**
         * @notice module:core
         * @dev Name of the governor instance (used in building the ERC712 domain separator).
         */
        function name() external view returns (string memory);
        /**
         * @notice module:core
         * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1"
         */
        function version() external view returns (string memory);
        /**
         * @notice module:voting
         * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
         * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of
         * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
         *
         * There are 2 standard keys: `support` and `quorum`.
         *
         * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`.
         * - `quorum=bravo` means that only For votes are counted towards quorum.
         * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
         *
         * If a counting module makes use of encoded `params`, it should  include this under a `params` key with a unique
         * name that describes the behavior. For example:
         *
         * - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
         * - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
         *
         * NOTE: The string can be decoded by the standard
         * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
         * JavaScript class.
         */
        // solhint-disable-next-line func-name-mixedcase
        function COUNTING_MODE() external view returns (string memory);
        /**
         * @notice module:core
         * @dev Hashing function used to (re)build the proposal id from the proposal details..
         */
        function hashProposal(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external pure returns (uint256);
        /**
         * @notice module:core
         * @dev Current state of a proposal, following Compound's convention
         */
        function state(uint256 proposalId) external view returns (ProposalState);
        /**
         * @notice module:core
         * @dev The number of votes required in order for a voter to become a proposer.
         */
        function proposalThreshold() external view returns (uint256);
        /**
         * @notice module:core
         * @dev Timepoint used to retrieve user's votes and quorum. If using block number (as per Compound's Comp), the
         * snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the
         * following block.
         */
        function proposalSnapshot(uint256 proposalId) external view returns (uint256);
        /**
         * @notice module:core
         * @dev Timepoint at which votes close. If using block number, votes close at the end of this block, so it is
         * possible to cast a vote during this block.
         */
        function proposalDeadline(uint256 proposalId) external view returns (uint256);
        /**
         * @notice module:core
         * @dev The account that created a proposal.
         */
        function proposalProposer(uint256 proposalId) external view returns (address);
        /**
         * @notice module:core
         * @dev The time when a queued proposal becomes executable ("ETA"). Unlike {proposalSnapshot} and
         * {proposalDeadline}, this doesn't use the governor clock, and instead relies on the executor's clock which may be
         * different. In most cases this will be a timestamp.
         */
        function proposalEta(uint256 proposalId) external view returns (uint256);
        /**
         * @notice module:core
         * @dev Whether a proposal needs to be queued before execution.
         */
        function proposalNeedsQueuing(uint256 proposalId) external view returns (bool);
        /**
         * @notice module:user-config
         * @dev Delay, between the proposal is created and the vote starts. The unit this duration is expressed in depends
         * on the clock (see EIP-6372) this contract uses.
         *
         * This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a
         * proposal starts.
         *
         * NOTE: While this interface returns a uint256, timepoints are stored as uint48 following the ERC-6372 clock type.
         * Consequently this value must fit in a uint48 (when added to the current clock). See {IERC6372-clock}.
         */
        function votingDelay() external view returns (uint256);
        /**
         * @notice module:user-config
         * @dev Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock
         * (see EIP-6372) this contract uses.
         *
         * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting
         * duration compared to the voting delay.
         *
         * NOTE: This value is stored when the proposal is submitted so that possible changes to the value do not affect
         * proposals that have already been submitted. The type used to save it is a uint32. Consequently, while this
         * interface returns a uint256, the value it returns should fit in a uint32.
         */
        function votingPeriod() external view returns (uint256);
        /**
         * @notice module:user-config
         * @dev Minimum number of cast voted required for a proposal to be successful.
         *
         * NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
         * quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
         */
        function quorum(uint256 timepoint) external view returns (uint256);
        /**
         * @notice module:reputation
         * @dev Voting power of an `account` at a specific `timepoint`.
         *
         * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
         * multiple), {ERC20Votes} tokens.
         */
        function getVotes(address account, uint256 timepoint) external view returns (uint256);
        /**
         * @notice module:reputation
         * @dev Voting power of an `account` at a specific `timepoint` given additional encoded parameters.
         */
        function getVotesWithParams(
            address account,
            uint256 timepoint,
            bytes memory params
        ) external view returns (uint256);
        /**
         * @notice module:voting
         * @dev Returns whether `account` has cast a vote on `proposalId`.
         */
        function hasVoted(uint256 proposalId, address account) external view returns (bool);
        /**
         * @dev Create a new proposal. Vote start after a delay specified by {IGovernor-votingDelay} and lasts for a
         * duration specified by {IGovernor-votingPeriod}.
         *
         * Emits a {ProposalCreated} event.
         */
        function propose(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            string memory description
        ) external returns (uint256 proposalId);
        /**
         * @dev Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing
         * is not necessary, this function may revert.
         * Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached.
         *
         * Emits a {ProposalQueued} event.
         */
        function queue(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external returns (uint256 proposalId);
        /**
         * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the
         * deadline to be reached. Depending on the governor it might also be required that the proposal was queued and
         * that some delay passed.
         *
         * Emits a {ProposalExecuted} event.
         *
         * NOTE: Some modules can modify the requirements for execution, for example by adding an additional timelock.
         */
        function execute(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external payable returns (uint256 proposalId);
        /**
         * @dev Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e.
         * before the vote starts.
         *
         * Emits a {ProposalCanceled} event.
         */
        function cancel(
            address[] memory targets,
            uint256[] memory values,
            bytes[] memory calldatas,
            bytes32 descriptionHash
        ) external returns (uint256 proposalId);
        /**
         * @dev Cast a vote
         *
         * Emits a {VoteCast} event.
         */
        function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
        /**
         * @dev Cast a vote with a reason
         *
         * Emits a {VoteCast} event.
         */
        function castVoteWithReason(
            uint256 proposalId,
            uint8 support,
            string calldata reason
        ) external returns (uint256 balance);
        /**
         * @dev Cast a vote with a reason and additional encoded parameters
         *
         * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
         */
        function castVoteWithReasonAndParams(
            uint256 proposalId,
            uint8 support,
            string calldata reason,
            bytes memory params
        ) external returns (uint256 balance);
        /**
         * @dev Cast a vote using the voter's signature, including ERC-1271 signature support.
         *
         * Emits a {VoteCast} event.
         */
        function castVoteBySig(
            uint256 proposalId,
            uint8 support,
            address voter,
            bytes memory signature
        ) external returns (uint256 balance);
        /**
         * @dev Cast a vote with a reason and additional encoded parameters using the voter's signature,
         * including ERC-1271 signature support.
         *
         * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
         */
        function castVoteWithReasonAndParamsBySig(
            uint256 proposalId,
            uint8 support,
            address voter,
            string calldata reason,
            bytes memory params,
            bytes memory signature
        ) external returns (uint256 balance);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
    pragma solidity ^0.8.20;
    import {IERC165} from "../utils/introspection/IERC165.sol";
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol)
    pragma solidity ^0.8.20;
    interface IERC6372 {
        /**
         * @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
         */
        function clock() external view returns (uint48);
        /**
         * @dev Description of the clock
         */
        // solhint-disable-next-line func-name-mixedcase
        function CLOCK_MODE() external view returns (string memory);
    }
    // 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/extensions/IERC20Permit.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
     * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
     *
     * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
     * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
     * need to send a transaction, and thus is not required to hold Ether at all.
     *
     * ==== Security Considerations
     *
     * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
     * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
     * considered as an intention to spend the allowance in any specific way. The second is that because permits have
     * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
     * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
     * generally recommended is:
     *
     * ```solidity
     * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
     *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
     *     doThing(..., value);
     * }
     *
     * function doThing(..., uint256 value) public {
     *     token.safeTransferFrom(msg.sender, address(this), value);
     *     ...
     * }
     * ```
     *
     * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
     * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
     * {SafeERC20-safeTransferFrom}).
     *
     * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
     * contracts should have entry points that don't rely on permit.
     */
    interface IERC20Permit {
        /**
         * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
         * given ``owner``'s signed approval.
         *
         * IMPORTANT: The same issues {IERC20-approve} has related to transaction
         * ordering also apply here.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `deadline` must be a timestamp in the future.
         * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
         * over the EIP712-formatted function arguments.
         * - the signature must use ``owner``'s current nonce (see {nonces}).
         *
         * For more information on the signature format, see the
         * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
         * section].
         *
         * CAUTION: See Security Considerations above.
         */
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        /**
         * @dev Returns the current nonce for `owner`. This value must be
         * included whenever a signature is generated for {permit}.
         *
         * Every successful call to {permit} increases ``owner``'s nonce by one. This
         * prevents a signature from being used multiple times.
         */
        function nonces(address owner) external view returns (uint256);
        /**
         * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
         */
        // solhint-disable-next-line func-name-mixedcase
        function DOMAIN_SEPARATOR() external view returns (bytes32);
    }
    // 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);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
    pragma solidity ^0.8.20;
    import {IERC20} from "../IERC20.sol";
    import {IERC20Permit} from "../extensions/IERC20Permit.sol";
    import {Address} from "../../../utils/Address.sol";
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using Address for address;
        /**
         * @dev An operation with an ERC20 token failed.
         */
        error SafeERC20FailedOperation(address token);
        /**
         * @dev Indicates a failed `decreaseAllowance` request.
         */
        error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
        /**
         * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
        }
        /**
         * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
         * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
         */
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
        }
        /**
         * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful.
         */
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 oldAllowance = token.allowance(address(this), spender);
            forceApprove(token, spender, oldAllowance + value);
        }
        /**
         * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
         * value, non-reverting calls are assumed to be successful.
         */
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
            unchecked {
                uint256 currentAllowance = token.allowance(address(this), spender);
                if (currentAllowance < requestedDecrease) {
                    revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
                }
                forceApprove(token, spender, currentAllowance - requestedDecrease);
            }
        }
        /**
         * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
         * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
         * to be set to zero before setting it to a non-zero value, such as USDT.
         */
        function forceApprove(IERC20 token, address spender, uint256 value) internal {
            bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
            if (!_callOptionalReturnBool(token, approvalCall)) {
                _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
                _callOptionalReturn(token, approvalCall);
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data);
            if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
                revert SafeERC20FailedOperation(address(token));
            }
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         *
         * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
         */
        function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
            // and not revert is the subcall reverts.
            (bool success, bytes memory returndata) = address(token).call(data);
            return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Collection of functions related to the address type
     */
    library Address {
        /**
         * @dev The ETH balance of the account is not enough to perform the operation.
         */
        error AddressInsufficientBalance(address account);
        /**
         * @dev There's no code at `target` (it is not a contract).
         */
        error AddressEmptyCode(address target);
        /**
         * @dev A call to an address target failed. The target may have reverted.
         */
        error FailedInnerCall();
        /**
         * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
         * `recipient`, forwarding all available gas and reverting on errors.
         *
         * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
         * of certain opcodes, possibly making contracts go over the 2300 gas limit
         * imposed by `transfer`, making them unable to receive funds via
         * `transfer`. {sendValue} removes this limitation.
         *
         * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
         *
         * IMPORTANT: because control is transferred to `recipient`, care must be
         * taken to not create reentrancy vulnerabilities. Consider using
         * {ReentrancyGuard} or the
         * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
         */
        function sendValue(address payable recipient, uint256 amount) internal {
            if (address(this).balance < amount) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, ) = recipient.call{value: amount}("");
            if (!success) {
                revert FailedInnerCall();
            }
        }
        /**
         * @dev Performs a Solidity function call using a low level `call`. A
         * plain `call` is an unsafe replacement for a function call: use this
         * function instead.
         *
         * If `target` reverts with a revert reason or custom error, it is bubbled
         * up by this function (like regular Solidity function calls). However, if
         * the call reverted with no returned reason, this function reverts with a
         * {FailedInnerCall} error.
         *
         * Returns the raw returned data. To convert to the expected return value,
         * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
         *
         * Requirements:
         *
         * - `target` must be a contract.
         * - calling `target` with `data` must not revert.
         */
        function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCallWithValue(target, data, 0);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but also transferring `value` wei to `target`.
         *
         * Requirements:
         *
         * - the calling contract must have an ETH balance of at least `value`.
         * - the called Solidity function must be `payable`.
         */
        function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
            if (address(this).balance < value) {
                revert AddressInsufficientBalance(address(this));
            }
            (bool success, bytes memory returndata) = target.call{value: value}(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a static call.
         */
        function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
            (bool success, bytes memory returndata) = target.staticcall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
         * but performing a delegate call.
         */
        function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
            (bool success, bytes memory returndata) = target.delegatecall(data);
            return verifyCallResultFromTarget(target, success, returndata);
        }
        /**
         * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
         * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
         * unsuccessful call.
         */
        function verifyCallResultFromTarget(
            address target,
            bool success,
            bytes memory returndata
        ) internal view returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                // only check if target is a contract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                if (returndata.length == 0 && target.code.length == 0) {
                    revert AddressEmptyCode(target);
                }
                return returndata;
            }
        }
        /**
         * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
         * revert reason or with a default {FailedInnerCall} error.
         */
        function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
            if (!success) {
                _revert(returndata);
            } else {
                return returndata;
            }
        }
        /**
         * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
         */
        function _revert(bytes memory returndata) private pure {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert FailedInnerCall();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.20;
    /**
     * @dev Interface of the ERC165 standard, as defined in the
     * https://eips.ethereum.org/EIPS/eip-165[EIP].
     *
     * Implementers can declare support of contract interfaces, which can then be
     * queried by others ({ERC165Checker}).
     *
     * For an implementation, see {ERC165}.
     */
    interface IERC165 {
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30 000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
    // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
    pragma solidity ^0.8.20;
    /**
     * @dev Library for managing
     * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
     * types.
     *
     * Sets have the following properties:
     *
     * - Elements are added, removed, and checked for existence in constant time
     * (O(1)).
     * - Elements are enumerated in O(n). No guarantees are made on the ordering.
     *
     * ```solidity
     * contract Example {
     *     // Add the library methods
     *     using EnumerableSet for EnumerableSet.AddressSet;
     *
     *     // Declare a set state variable
     *     EnumerableSet.AddressSet private mySet;
     * }
     * ```
     *
     * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
     * and `uint256` (`UintSet`) are supported.
     *
     * [WARNING]
     * ====
     * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
     * unusable.
     * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
     *
     * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
     * array of EnumerableSet.
     * ====
     */
    library EnumerableSet {
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
            // Storage of set values
            bytes32[] _values;
            // Position is the index of the value in the `values` array plus 1.
            // Position 0 is used to mean a value is not in the set.
            mapping(bytes32 value => uint256) _positions;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
            if (!_contains(set, value)) {
                set._values.push(value);
                // The value is stored at length-1, but we add 1 to all indexes
                // and use 0 as a sentinel value
                set._positions[value] = set._values.length;
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
            // We cache the value's position to prevent multiple reads from the same storage slot
            uint256 position = set._positions[value];
            if (position != 0) {
                // Equivalent to contains(set, value)
                // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                // the array, and then remove the last element (sometimes called as 'swap and pop').
                // This modifies the order of the array, as noted in {at}.
                uint256 valueIndex = position - 1;
                uint256 lastIndex = set._values.length - 1;
                if (valueIndex != lastIndex) {
                    bytes32 lastValue = set._values[lastIndex];
                    // Move the lastValue to the index where the value to delete is
                    set._values[valueIndex] = lastValue;
                    // Update the tracked position of the lastValue (that was just moved)
                    set._positions[lastValue] = position;
                }
                // Delete the slot where the moved value was stored
                set._values.pop();
                // Delete the tracked position for the deleted slot
                delete set._positions[value];
                return true;
            } else {
                return false;
            }
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
            return set._positions[value] != 0;
        }
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
            return set._values.length;
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
            return set._values[index];
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function _values(Set storage set) private view returns (bytes32[] memory) {
            return set._values;
        }
        // Bytes32Set
        struct Bytes32Set {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _add(set._inner, value);
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
            return _remove(set._inner, value);
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
            return _contains(set._inner, value);
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(Bytes32Set storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
            return _at(set._inner, index);
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
            bytes32[] memory store = _values(set._inner);
            bytes32[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // AddressSet
        struct AddressSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
            return _add(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
            return _remove(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
            return _contains(set._inner, bytes32(uint256(uint160(value))));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
            return address(uint160(uint256(_at(set._inner, index))));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(AddressSet storage set) internal view returns (address[] memory) {
            bytes32[] memory store = _values(set._inner);
            address[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
        // UintSet
        struct UintSet {
            Set _inner;
        }
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(UintSet storage set, uint256 value) internal returns (bool) {
            return _add(set._inner, bytes32(value));
        }
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(UintSet storage set, uint256 value) internal returns (bool) {
            return _remove(set._inner, bytes32(value));
        }
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(UintSet storage set, uint256 value) internal view returns (bool) {
            return _contains(set._inner, bytes32(value));
        }
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(UintSet storage set) internal view returns (uint256) {
            return _length(set._inner);
        }
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(UintSet storage set, uint256 index) internal view returns (uint256) {
            return uint256(_at(set._inner, index));
        }
        /**
         * @dev Return the entire set in an array
         *
         * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
         * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
         * this function has an unbounded cost, and using it as part of a state-changing function may render the function
         * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
         */
        function values(UintSet storage set) internal view returns (uint256[] memory) {
            bytes32[] memory store = _values(set._inner);
            uint256[] memory result;
            /// @solidity memory-safe-assembly
            assembly {
                result := store
            }
            return result;
        }
    }
    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.5.0;
    interface IUniswapV2Pair {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
        function MINIMUM_LIQUIDITY() external pure returns (uint);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function price0CumulativeLast() external view returns (uint);
        function price1CumulativeLast() external view returns (uint);
        function kLast() external view returns (uint);
        function mint(address to) external returns (uint liquidity);
        function burn(address to) external returns (uint amount0, uint amount1);
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
        function skim(address to) external;
        function sync() external;
        function initialize(address, address) external;
    }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: MIT
    pragma solidity ^0.8.20;
    import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
    import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
    import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
    import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
    import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
    import "../pool/IUniswapV2Router02.sol";
    import "../pool/IUniswapV2Factory.sol";
    import "../pool/IUniswapV2Pair.sol";
    import "./IAgentToken.sol";
    import "./IAgentFactory.sol";
    contract AgentToken is
        ContextUpgradeable,
        IAgentToken,
        Ownable2StepUpgradeable
    {
        using EnumerableSet for EnumerableSet.AddressSet;
        using EnumerableSet for EnumerableSet.Bytes32Set;
        using SafeERC20 for IERC20;
        uint256 internal constant BP_DENOM = 10000;
        uint256 internal constant ROUND_DEC = 100000000000;
        uint256 internal constant CALL_GAS_LIMIT = 50000;
        uint256 internal constant MAX_SWAP_THRESHOLD_MULTIPLE = 20;
        address public uniswapV2Pair;
        uint256 public botProtectionDurationInSeconds;
        bool internal _tokenHasTax;
        IUniswapV2Router02 internal _uniswapRouter;
        uint32 public fundedDate;
        uint16 public projectBuyTaxBasisPoints;
        uint16 public projectSellTaxBasisPoints;
        uint16 public swapThresholdBasisPoints;
        address public pairToken; // The token used to trade for this token
        /** @dev {_autoSwapInProgress} We start with {_autoSwapInProgress} ON, as we don't want to
         * call autoswap when processing initial liquidity from this address. We turn this OFF when
         * liquidity has been loaded, and use this bool to control processing during auto-swaps
         * from that point onwards. */
        bool private _autoSwapInProgress;
        address public projectTaxRecipient;
        uint128 public projectTaxPendingSwap;
        address public vault; // Project supply vault
        string private _name;
        string private _symbol;
        uint256 private _totalSupply;
        /** @dev {_balances} Addresses balances */
        mapping(address => uint256) private _balances;
        /** @dev {_allowances} Addresses allocance details */
        mapping(address => mapping(address => uint256)) private _allowances;
        /** @dev {_validCallerCodeHashes} Code hashes of callers we consider valid */
        EnumerableSet.Bytes32Set private _validCallerCodeHashes;
        /** @dev {_liquidityPools} Enumerable set for liquidity pool addresses */
        EnumerableSet.AddressSet private _liquidityPools;
        IAgentFactory private _factory; // Single source of truth
        /**
         * @dev {onlyOwnerOrFactory}
         *
         * Throws if called by any account other than the owner, factory or pool.
         */
        modifier onlyOwnerOrFactory() {
            if (owner() != _msgSender() && address(_factory) != _msgSender()) {
                revert CallerIsNotAdminNorFactory();
            }
            _;
        }
        constructor() {
            _disableInitializers();
        }
        function initialize(
            address[3] memory integrationAddresses_,
            bytes memory baseParams_,
            bytes memory supplyParams_,
            bytes memory taxParams_
        ) external initializer {
            _decodeBaseParams(integrationAddresses_[0], baseParams_);
            _uniswapRouter = IUniswapV2Router02(integrationAddresses_[1]);
            pairToken = integrationAddresses_[2];
            ERC20SupplyParameters memory supplyParams = abi.decode(
                supplyParams_,
                (ERC20SupplyParameters)
            );
            ERC20TaxParameters memory taxParams = abi.decode(
                taxParams_,
                (ERC20TaxParameters)
            );
            _processSupplyParams(supplyParams);
            uint256 lpSupply = supplyParams.lpSupply * (10 ** decimals());
            uint256 vaultSupply = supplyParams.vaultSupply * (10 ** decimals());
            botProtectionDurationInSeconds = supplyParams
                .botProtectionDurationInSeconds;
            _tokenHasTax = _processTaxParams(taxParams);
            swapThresholdBasisPoints = uint16(
                taxParams.taxSwapThresholdBasisPoints
            );
            projectTaxRecipient = taxParams.projectTaxRecipient;
            _mintBalances(lpSupply, vaultSupply);
            uniswapV2Pair = _createPair();
            _factory = IAgentFactory(_msgSender());
            _autoSwapInProgress = true; // We don't want to tax initial liquidity
        }
        /**
         * @dev function {_decodeBaseParams}
         *
         * Decode NFT Parameters
         *
         * @param projectOwner_ The owner of this contract
         * @param encodedBaseParams_ The base params encoded into a bytes array
         */
        function _decodeBaseParams(
            address projectOwner_,
            bytes memory encodedBaseParams_
        ) internal {
            _transferOwnership(projectOwner_);
            (_name, _symbol) = abi.decode(encodedBaseParams_, (string, string));
        }
        /**
         * @dev function {_processSupplyParams}
         *
         * Process provided supply params
         *
         * @param erc20SupplyParameters_ The supply params
         */
        function _processSupplyParams(
            ERC20SupplyParameters memory erc20SupplyParameters_
        ) internal {
            if (
                erc20SupplyParameters_.maxSupply !=
                (erc20SupplyParameters_.vaultSupply +
                    erc20SupplyParameters_.lpSupply)
            ) {
                revert SupplyTotalMismatch();
            }
            if (erc20SupplyParameters_.maxSupply > type(uint128).max) {
                revert MaxSupplyTooHigh();
            }
            vault = erc20SupplyParameters_.vault;
        }
        /**
         * @dev function {_processTaxParams}
         *
         * Process provided tax params
         *
         * @param erc20TaxParameters_ The tax params
         */
        function _processTaxParams(
            ERC20TaxParameters memory erc20TaxParameters_
        ) internal returns (bool tokenHasTax_) {
            /**
             * @dev If this
             * token does NOT have tax applied then there is no need to store or read these parameters, and we can
             * avoid this simply by checking the immutable var. Pass back the value for this var from this method.
             */
            if (
                erc20TaxParameters_.projectBuyTaxBasisPoints == 0 &&
                erc20TaxParameters_.projectSellTaxBasisPoints == 0
            ) {
                return false;
            } else {
                projectBuyTaxBasisPoints = uint16(
                    erc20TaxParameters_.projectBuyTaxBasisPoints
                );
                projectSellTaxBasisPoints = uint16(
                    erc20TaxParameters_.projectSellTaxBasisPoints
                );
                return true;
            }
        }
        /**
         * @dev function {_mintBalances}
         *
         * Mint initial balances
         *
         * @param lpMint_ The number of tokens for liquidity
         */
        function _mintBalances(uint256 lpMint_, uint256 vaultMint_) internal {
            if (lpMint_ > 0) {
                _mint(address(this), lpMint_);
            }
            if (vaultMint_ > 0) {
                _mint(vault, vaultMint_);
            }
        }
        /**
         * @dev function {_createPair}
         *
         * Create the uniswap pair
         *
         * @return uniswapV2Pair_ The pair address
         */
        function _createPair() internal returns (address uniswapV2Pair_) {
            uniswapV2Pair_ = IUniswapV2Factory(_uniswapRouter.factory()).getPair(
                address(this),
                pairToken
            );
            if (uniswapV2Pair_ == address(0)) {
                uniswapV2Pair_ = IUniswapV2Factory(_uniswapRouter.factory())
                    .createPair(address(this), pairToken);
                emit LiquidityPoolCreated(uniswapV2Pair_);
            }
            _liquidityPools.add(uniswapV2Pair_);
            return (uniswapV2Pair_);
        }
        /**
         * @dev function {addInitialLiquidity}
         *
         * Add initial liquidity to the uniswap pair
         *
         * @param lpOwner The recipient of LP tokens
         */
        function addInitialLiquidity(address lpOwner) external onlyOwnerOrFactory {
            _addInitialLiquidity(lpOwner);
        }
        /**
         * @dev function {_addInitialLiquidity}
         *
         * Add initial liquidity to the uniswap pair (internal function that does processing)
         *
         * * @param lpOwner The recipient of LP tokens
         */
        function _addInitialLiquidity(address lpOwner) internal {
            // Funded date is the date of first funding. We can only add initial liquidity once. If this date is set,
            // we cannot proceed
            if (fundedDate != 0) {
                revert InitialLiquidityAlreadyAdded();
            }
            fundedDate = uint32(block.timestamp);
            // Can only do this if this contract holds tokens:
            if (balanceOf(address(this)) == 0) {
                revert NoTokenForLiquidityPair();
            }
            // Approve the uniswap router for an inifinite amount (max uint256)
            // This means that we don't need to worry about later incrememtal
            // approvals on tax swaps, as the uniswap router allowance will never
            // be decreased (see code in decreaseAllowance for reference)
            _approve(address(this), address(_uniswapRouter), type(uint256).max);
            IERC20(pairToken).approve(address(_uniswapRouter), type(uint256).max);
            // Add the liquidity:
            address pairAddr = IUniswapV2Factory(_uniswapRouter.factory()).getPair(
                address(this),
                pairToken
            );
            uint256 amountA = balanceOf(address(this));
            uint256 amountB = IERC20(pairToken).balanceOf(address(this));
            _transfer(address(this), pairAddr, amountA, false);
            IERC20(pairToken).transfer(pairAddr, amountB);
            uint256 lpTokens = IUniswapV2Pair(pairAddr).mint(address(this));
            emit InitialLiquidityAdded(amountA, amountB, lpTokens);
            // We now set this to false so that future transactions can be eligibile for autoswaps
            _autoSwapInProgress = false;
            IERC20(uniswapV2Pair).transfer(lpOwner, lpTokens);
        }
        /**
         * @dev function {isLiquidityPool}
         *
         * Return if an address is a liquidity pool
         *
         * @param queryAddress_ The address being queried
         * @return bool The address is / isn't a liquidity pool
         */
        function isLiquidityPool(address queryAddress_) public view returns (bool) {
            /** @dev We check the uniswapV2Pair address first as this is an immutable variable and therefore does not need
             * to be fetched from storage, saving gas if this address IS the uniswapV2Pool. We also add this address
             * to the enumerated set for ease of reference (for example it is returned in the getter), and it does
             * not add gas to any other calls, that still complete in 0(1) time.
             */
            return (queryAddress_ == uniswapV2Pair ||
                _liquidityPools.contains(queryAddress_));
        }
        /**
         * @dev function {liquidityPools}
         *
         * Returns a list of all liquidity pools
         *
         * @return liquidityPools_ a list of all liquidity pools
         */
        function liquidityPools()
            external
            view
            returns (address[] memory liquidityPools_)
        {
            return (_liquidityPools.values());
        }
        /**
         * @dev function {addLiquidityPool} onlyOwnerOrFactory
         *
         * Allows the manager to add a liquidity pool to the pool enumerable set
         *
         * @param newLiquidityPool_ The address of the new liquidity pool
         */
        function addLiquidityPool(
            address newLiquidityPool_
        ) public onlyOwnerOrFactory {
            // Don't allow calls that didn't pass an address:
            if (newLiquidityPool_ == address(0)) {
                revert LiquidityPoolCannotBeAddressZero();
            }
            // Only allow smart contract addresses to be added, as only these can be pools:
            if (newLiquidityPool_.code.length == 0) {
                revert LiquidityPoolMustBeAContractAddress();
            }
            // Add this to the enumerated list:
            _liquidityPools.add(newLiquidityPool_);
            emit LiquidityPoolAdded(newLiquidityPool_);
        }
        /**
         * @dev function {removeLiquidityPool} onlyOwnerOrFactory
         *
         * Allows the manager to remove a liquidity pool
         *
         * @param removedLiquidityPool_ The address of the old removed liquidity pool
         */
        function removeLiquidityPool(
            address removedLiquidityPool_
        ) external onlyOwnerOrFactory {
            // Remove this from the enumerated list:
            _liquidityPools.remove(removedLiquidityPool_);
            emit LiquidityPoolRemoved(removedLiquidityPool_);
        }
        /**
         * @dev function {isValidCaller}
         *
         * Return if an address is a valid caller
         *
         * @param queryHash_ The code hash being queried
         * @return bool The address is / isn't a valid caller
         */
        function isValidCaller(bytes32 queryHash_) public view returns (bool) {
            return (_validCallerCodeHashes.contains(queryHash_));
        }
        /**
         * @dev function {validCallers}
         *
         * Returns a list of all valid caller code hashes
         *
         * @return validCallerHashes_ a list of all valid caller code hashes
         */
        function validCallers()
            external
            view
            returns (bytes32[] memory validCallerHashes_)
        {
            return (_validCallerCodeHashes.values());
        }
        /**
         * @dev function {addValidCaller} onlyOwnerOrFactory
         *
         * Allows the owner to add the hash of a valid caller
         *
         * @param newValidCallerHash_ The hash of the new valid caller
         */
        function addValidCaller(
            bytes32 newValidCallerHash_
        ) external onlyOwnerOrFactory {
            _validCallerCodeHashes.add(newValidCallerHash_);
            emit ValidCallerAdded(newValidCallerHash_);
        }
        /**
         * @dev function {removeValidCaller} onlyOwnerOrFactory
         *
         * Allows the owner to remove a valid caller
         *
         * @param removedValidCallerHash_ The hash of the old removed valid caller
         */
        function removeValidCaller(
            bytes32 removedValidCallerHash_
        ) external onlyOwnerOrFactory {
            // Remove this from the enumerated list:
            _validCallerCodeHashes.remove(removedValidCallerHash_);
            emit ValidCallerRemoved(removedValidCallerHash_);
        }
        /**
         * @dev function {setProjectTaxRecipient} onlyOwnerOrFactory
         *
         * Allows the manager to set the project tax recipient address
         *
         * @param projectTaxRecipient_ New recipient address
         */
        function setProjectTaxRecipient(
            address projectTaxRecipient_
        ) external onlyOwnerOrFactory {
            projectTaxRecipient = projectTaxRecipient_;
            emit ProjectTaxRecipientUpdated(projectTaxRecipient_);
        }
        /**
         * @dev function {setSwapThresholdBasisPoints} onlyOwnerOrFactory
         *
         * Allows the manager to set the autoswap threshold
         *
         * @param swapThresholdBasisPoints_ New swap threshold in basis points
         */
        function setSwapThresholdBasisPoints(
            uint16 swapThresholdBasisPoints_
        ) external onlyOwnerOrFactory {
            uint256 oldswapThresholdBasisPoints = swapThresholdBasisPoints;
            swapThresholdBasisPoints = swapThresholdBasisPoints_;
            emit AutoSwapThresholdUpdated(
                oldswapThresholdBasisPoints,
                swapThresholdBasisPoints_
            );
        }
        /**
         * @dev function {setProjectTaxRates} onlyOwnerOrFactory
         *
         * Change the tax rates, subject to only ever decreasing
         *
         * @param newProjectBuyTaxBasisPoints_ The new buy tax rate
         * @param newProjectSellTaxBasisPoints_ The new sell tax rate
         */
        function setProjectTaxRates(
            uint16 newProjectBuyTaxBasisPoints_,
            uint16 newProjectSellTaxBasisPoints_
        ) external onlyOwnerOrFactory {
            uint16 oldBuyTaxBasisPoints = projectBuyTaxBasisPoints;
            uint16 oldSellTaxBasisPoints = projectSellTaxBasisPoints;
            projectBuyTaxBasisPoints = newProjectBuyTaxBasisPoints_;
            projectSellTaxBasisPoints = newProjectSellTaxBasisPoints_;
            _tokenHasTax =
                (projectBuyTaxBasisPoints + projectSellTaxBasisPoints) > 0;
            emit ProjectTaxBasisPointsChanged(
                oldBuyTaxBasisPoints,
                newProjectBuyTaxBasisPoints_,
                oldSellTaxBasisPoints,
                newProjectSellTaxBasisPoints_
            );
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view virtual override returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view virtual override returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5.05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the 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 override returns (uint8) {
            return 18;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view virtual override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev totalBuyTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalBuyTaxBasisPoints() public view returns (uint256) {
            return projectBuyTaxBasisPoints;
        }
        /**
         * @dev totalSellTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalSellTaxBasisPoints() public view returns (uint256) {
            return projectSellTaxBasisPoints;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(
            address account
        ) public view virtual override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `to` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(
            address to,
            uint256 amount
        ) public virtual override(IERC20) returns (bool) {
            address owner = _msgSender();
            _transfer(
                owner,
                to,
                amount,
                (isLiquidityPool(owner) || isLiquidityPool(to))
            );
            return true;
        }
        /**
         * @dev See {IERC20-allowance}.
         */
        function allowance(
            address owner,
            address spender
        ) public view virtual override returns (uint256) {
            return _allowances[owner][spender];
        }
        /**
         * @dev See {IERC20-approve}.
         *
         * NOTE: If `amount` 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 amount
        ) public virtual override returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, amount);
            return true;
        }
        /**
         * @dev See {IERC20-transferFrom}.
         *
         * Emits an {Approval} event indicating the updated allowance. This is not
         * required by the EIP. See the note at the beginning of {ERC20}.
         *
         * 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 `amount`.
         * - the caller must have allowance for ``from``'s tokens of at least
         * `amount`.
         */
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual override returns (bool) {
            address spender = _msgSender();
            _spendAllowance(from, spender, amount);
            _transfer(
                from,
                to,
                amount,
                (isLiquidityPool(from) || isLiquidityPool(to))
            );
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(
            address spender,
            uint256 addedValue
        ) public virtual returns (bool) {
            address owner = _msgSender();
            _approve(owner, spender, allowance(owner, spender) + addedValue);
            return true;
        }
        /**
         * @dev Atomically decreases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         * - `spender` must have allowance for the caller of at least
         * `subtractedValue`.
         */
        function decreaseAllowance(
            address spender,
            uint256 subtractedValue
        ) public virtual returns (bool) {
            address owner = _msgSender();
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance < subtractedValue) {
                revert AllowanceDecreasedBelowZero();
            }
            unchecked {
                _approve(owner, spender, currentAllowance - subtractedValue);
            }
            return true;
        }
        /**
         * @dev Moves `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.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `from` must have a balance of at least `amount`.
         */
        function _transfer(
            address from,
            address to,
            uint256 amount,
            bool applyTax
        ) internal virtual {
            _beforeTokenTransfer(from, to, amount);
            // Perform pre-tax validation (e.g. amount doesn't exceed balance, max txn amount)
            uint256 fromBalance = _pretaxValidationAndLimits(from, to, amount);
            // Perform autoswap if eligible
            _autoSwap(from, to);
            // Process taxes
            uint256 amountMinusTax = _taxProcessing(applyTax, to, from, amount);
            _balances[from] = fromBalance - amount;
            _balances[to] += amountMinusTax;
            emit Transfer(from, to, amountMinusTax);
            _afterTokenTransfer(from, to, amount);
        }
        /**
         * @dev function {_pretaxValidationAndLimits}
         *
         * Perform validation on pre-tax amounts
         *
         * @param from_ From address for the transaction
         * @param to_ To address for the transaction
         * @param amount_ Amount of the transaction
         */
        function _pretaxValidationAndLimits(
            address from_,
            address to_,
            uint256 amount_
        ) internal view returns (uint256 fromBalance_) {
            // This can't be a transfer to the liquidity pool before the funding date
            // UNLESS the from address is this contract. This ensures that the initial
            // LP funding transaction is from this contract using the supply of tokens
            // designated for the LP pool, and therefore the initial price in the pool
            // is being set as expected.
            //
            // This protects from, for example, tokens from a team minted supply being
            // paired with ETH and added to the pool, setting the initial price, BEFORE
            // the initial liquidity is added through this contract.
            if (to_ == uniswapV2Pair && from_ != address(this) && fundedDate == 0) {
                revert InitialLiquidityNotYetAdded();
            }
            if (from_ == address(0)) {
                revert TransferFromZeroAddress();
            }
            if (to_ == address(0)) {
                revert TransferToZeroAddress();
            }
            fromBalance_ = _balances[from_];
            if (fromBalance_ < amount_) {
                revert TransferAmountExceedsBalance();
            }
            return (fromBalance_);
        }
        /**
         * @dev function {_taxProcessing}
         *
         * Perform tax processing
         *
         * @param applyTax_ Do we apply tax to this transaction?
         * @param to_ The reciever of the token
         * @param from_ The sender of the token
         * @param sentAmount_ The amount being send
         * @return amountLessTax_ The amount that will be recieved, i.e. the send amount minus tax
         */
        function _taxProcessing(
            bool applyTax_,
            address to_,
            address from_,
            uint256 sentAmount_
        ) internal returns (uint256 amountLessTax_) {
            amountLessTax_ = sentAmount_;
            unchecked {
                if (_tokenHasTax && applyTax_ && !_autoSwapInProgress) {
                    uint256 tax;
                    // on sell
                    if (isLiquidityPool(to_) && totalSellTaxBasisPoints() > 0) {
                        if (projectSellTaxBasisPoints > 0) {
                            uint256 projectTax = ((sentAmount_ *
                                projectSellTaxBasisPoints) / BP_DENOM);
                            projectTaxPendingSwap += uint128(projectTax);
                            tax += projectTax;
                        }
                    }
                    // on buy
                    else if (
                        isLiquidityPool(from_) && totalBuyTaxBasisPoints() > 0
                    ) {
                        if (projectBuyTaxBasisPoints > 0) {
                            uint256 projectTax = ((sentAmount_ *
                                projectBuyTaxBasisPoints) / BP_DENOM);
                            projectTaxPendingSwap += uint128(projectTax);
                            tax += projectTax;
                        }
                    }
                    if (tax > 0) {
                        _balances[address(this)] += tax;
                        emit Transfer(from_, address(this), tax);
                        amountLessTax_ -= tax;
                    }
                }
            }
            return (amountLessTax_);
        }
        /**
         * @dev function {_autoSwap}
         *
         * Automate the swap of accumulated tax fees to native token
         *
         * @param from_ The sender of the token
         * @param to_ The recipient of the token
         */
        function _autoSwap(address from_, address to_) internal {
            if (_tokenHasTax) {
                uint256 contractBalance = balanceOf(address(this));
                uint256 swapBalance = contractBalance;
                uint256 swapThresholdInTokens = (_totalSupply *
                    swapThresholdBasisPoints) / BP_DENOM;
                if (
                    _eligibleForSwap(from_, to_, swapBalance, swapThresholdInTokens)
                ) {
                    // Store that a swap back is in progress:
                    _autoSwapInProgress = true;
                    // Check if we need to reduce the amount of tokens for this swap:
                    if (
                        swapBalance >
                        swapThresholdInTokens * MAX_SWAP_THRESHOLD_MULTIPLE
                    ) {
                        swapBalance =
                            swapThresholdInTokens *
                            MAX_SWAP_THRESHOLD_MULTIPLE;
                    }
                    // Perform the auto swap to pair token
                    _swapTax(swapBalance, contractBalance);
                    // Flag that the autoswap is complete:
                    _autoSwapInProgress = false;
                }
            }
        }
        /**
         * @dev function {_eligibleForSwap}
         *
         * Is the current transfer eligible for autoswap
         *
         * @param from_ The sender of the token
         * @param to_ The recipient of the token
         * @param taxBalance_ The current accumulated tax balance
         * @param swapThresholdInTokens_ The swap threshold as a token amount
         */
        function _eligibleForSwap(
            address from_,
            address to_,
            uint256 taxBalance_,
            uint256 swapThresholdInTokens_
        ) internal view returns (bool) {
            return (taxBalance_ >= swapThresholdInTokens_ &&
                !_autoSwapInProgress &&
                !isLiquidityPool(from_) &&
                from_ != address(_uniswapRouter) &&
                to_ != address(_uniswapRouter) &&
                from_ != address(this));
        }
        /**
         * @dev function {_swapTax}
         *
         * Swap tokens taken as tax for pair token
         *
         * @param swapBalance_ The current accumulated tax balance to swap
         * @param contractBalance_ The current accumulated total tax balance
         */
        function _swapTax(uint256 swapBalance_, uint256 contractBalance_) internal {
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = pairToken;
            // Wrap external calls in try / catch to handle errors
            try
                _uniswapRouter
                    .swapExactTokensForTokensSupportingFeeOnTransferTokens(
                        swapBalance_,
                        0,
                        path,
                        projectTaxRecipient,
                        block.timestamp + 600
                    )
            {
                // We will not have swapped all tax tokens IF the amount was greater than the max auto swap.
                // We therefore cannot just set the pending swap counters to 0. Instead, in this scenario,
                // we must reduce them in proportion to the swap amount vs the remaining balance + swap
                // amount.
                //
                // For example:
                //  * swap Balance is 250
                //  * contract balance is 385.
                //  * projectTaxPendingSwap is 300
                //
                // The new total for the projectTaxPendingSwap is:
                //   = 300 - ((300 * 250) / 385)
                //   = 300 - 194
                //   = 106
                if (swapBalance_ < contractBalance_) {
                    projectTaxPendingSwap -= uint128(
                        (projectTaxPendingSwap * swapBalance_) / contractBalance_
                    );
                } else {
                    projectTaxPendingSwap = 0;
                }
            } catch {
                // Dont allow a failed external call (in this case to uniswap) to stop a transfer.
                // Emit that this has occured and continue.
                emit ExternalCallError(5);
            }
        }
        /**
         * @dev distributeTaxTokens
         *
         * Allows the distribution of tax tokens to the designated recipient(s)
         *
         * As part of standard processing the tax token balance being above the threshold
         * will trigger an autoswap to ETH and distribution of this ETH to the designated
         * recipients. This is automatic and there is no need for user involvement.
         *
         * As part of this swap there are a number of calculations performed, particularly
         * if the tax balance is above MAX_SWAP_THRESHOLD_MULTIPLE.
         *
         * Testing indicates that these calculations are safe. But given the data / code
         * interactions it remains possible that some edge case set of scenarios may cause
         * an issue with these calculations.
         *
         * This method is therefore provided as a 'fallback' option to safely distribute
         * accumulated taxes from the contract, with a direct transfer of the ERC20 tokens
         * themselves.
         */
        function distributeTaxTokens() external {
            if (projectTaxPendingSwap > 0) {
                uint256 projectDistribution = projectTaxPendingSwap;
                projectTaxPendingSwap = 0;
                _transfer(
                    address(this),
                    projectTaxRecipient,
                    projectDistribution,
                    false
                );
            }
        }
        /**
         * @dev function {withdrawETH} onlyOwnerOrFactory
         *
         * A withdraw function to allow ETH to be withdrawn by the manager
         *
         * This contract should never hold ETH. The only envisaged scenario where
         * it might hold ETH is a failed autoswap where the uniswap swap has completed,
         * the recipient of ETH reverts, the contract then wraps to WETH and the
         * wrap to WETH fails.
         *
         * This feels unlikely. But, for safety, we include this method.
         *
         * @param amount_ The amount to withdraw
         */
        function withdrawETH(uint256 amount_) external onlyOwnerOrFactory {
            (bool success, ) = _msgSender().call{value: amount_}("");
            if (!success) {
                revert TransferFailed();
            }
        }
        /**
         * @dev function {withdrawERC20} onlyOwnerOrFactory
         *
         * A withdraw function to allow ERC20s (except address(this)) to be withdrawn.
         *
         * This contract should never hold ERC20s other than tax tokens. The only envisaged
         * scenario where it might hold an ERC20 is a failed autoswap where the uniswap swap
         * has completed, the recipient of ETH reverts, the contract then wraps to WETH, the
         * wrap to WETH succeeds, BUT then the transfer of WETH fails.
         *
         * This feels even less likely than the scenario where ETH is held on the contract.
         * But, for safety, we include this method.
         *
         * @param token_ The ERC20 contract
         * @param amount_ The amount to withdraw
         */
        function withdrawERC20(
            address token_,
            uint256 amount_
        ) external onlyOwnerOrFactory {
            if (token_ == address(this)) {
                revert CannotWithdrawThisToken();
            }
            IERC20(token_).safeTransfer(_msgSender(), amount_);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            if (account == address(0)) {
                revert MintToZeroAddress();
            }
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply += uint128(amount);
            unchecked {
                // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                _balances[account] += amount;
            }
            emit Transfer(address(0), account, amount);
            _afterTokenTransfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements:
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            if (account == address(0)) {
                revert BurnFromTheZeroAddress();
            }
            _beforeTokenTransfer(account, address(0), amount);
            uint256 accountBalance = _balances[account];
            if (accountBalance < amount) {
                revert BurnExceedsBalance();
            }
            unchecked {
                _balances[account] = accountBalance - amount;
                // Overflow not possible: amount <= accountBalance <= totalSupply.
                _totalSupply -= uint128(amount);
            }
            emit Transfer(account, address(0), amount);
            _afterTokenTransfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            if (owner == address(0)) {
                revert ApproveFromTheZeroAddress();
            }
            if (spender == address(0)) {
                revert ApproveToTheZeroAddress();
            }
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
         *
         * Does not update the allowance amount in case of infinite allowance.
         * Revert if not enough allowance is available.
         *
         * Might emit an {Approval} event.
         */
        function _spendAllowance(
            address owner,
            address spender,
            uint256 amount
        ) internal virtual {
            uint256 currentAllowance = allowance(owner, spender);
            if (currentAllowance != type(uint256).max) {
                if (currentAllowance < amount) {
                    revert InsufficientAllowance();
                }
                unchecked {
                    _approve(owner, spender, currentAllowance - amount);
                }
            }
        }
        /**
         * @dev Destroys a `value` amount of tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 value) public virtual {
            _burn(_msgSender(), value);
        }
        /**
         * @dev Destroys a `value` amount of tokens from `account`, deducting from
         * the caller's allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `value`.
         */
        function burnFrom(address account, uint256 value) public virtual {
            _spendAllowance(account, _msgSender(), value);
            _burn(account, value);
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        /**
         * @dev Hook that is called after any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * has been transferred to `to`.
         * - when `from` is zero, `amount` tokens have been minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _afterTokenTransfer(
            address from,
            address to,
            uint256 amount
        ) internal virtual {}
        receive() external payable {}
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import "@openzeppelin/contracts/governance/IGovernor.sol";
    interface IAgentFactory {
        function proposeAgent(
            string memory name,
            string memory symbol,
            string memory tokenURI,
            uint8[] memory cores,
            bytes32 tbaSalt,
            address tbaImplementation,
            uint32 daoVotingPeriod,
            uint256 daoThreshold
        ) external returns (uint256);
        function withdraw(uint256 id) external;
        function totalAgents() external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    import "./IERC20Config.sol";
    import "./IErrors.sol";
    interface IAgentToken is
        IERC20,
        IERC20Config,
        IERC20Metadata,
        IErrors
    {
        event AutoSwapThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);
        event ExternalCallError(uint256 identifier);
        event InitialLiquidityAdded(
            uint256 tokenA,
            uint256 tokenB,
            uint256 lpToken
        );
        event LimitsUpdated(
            uint256 oldMaxTokensPerTransaction,
            uint256 newMaxTokensPerTransaction,
            uint256 oldMaxTokensPerWallet,
            uint256 newMaxTokensPerWallet
        );
        event LiquidityPoolCreated(address addedPool);
        event LiquidityPoolAdded(address addedPool);
        event LiquidityPoolRemoved(address removedPool);
        event ProjectTaxBasisPointsChanged(
            uint256 oldBuyBasisPoints,
            uint256 newBuyBasisPoints,
            uint256 oldSellBasisPoints,
            uint256 newSellBasisPoints
        );
        event RevenueAutoSwap();
        event ProjectTaxRecipientUpdated(address treasury);
        event ValidCallerAdded(bytes32 addedValidCaller);
        event ValidCallerRemoved(bytes32 removedValidCaller);
        /**
         * @dev function {addInitialLiquidity}
         *
         * Add initial liquidity to the uniswap pair
         *
         * @param lpOwner The recipient of LP tokens
         */
        function addInitialLiquidity(address lpOwner) external;
        /**
         * @dev function {isLiquidityPool}
         *
         * Return if an address is a liquidity pool
         *
         * @param queryAddress_ The address being queried
         * @return bool The address is / isn't a liquidity pool
         */
        function isLiquidityPool(
            address queryAddress_
        ) external view returns (bool);
        /**
         * @dev function {liquidityPools}
         *
         * Returns a list of all liquidity pools
         *
         * @return liquidityPools_ a list of all liquidity pools
         */
        function liquidityPools()
            external
            view
            returns (address[] memory liquidityPools_);
        /**
         * @dev function {addLiquidityPool} onlyOwner
         *
         * Allows the manager to add a liquidity pool to the pool enumerable set
         *
         * @param newLiquidityPool_ The address of the new liquidity pool
         */
        function addLiquidityPool(address newLiquidityPool_) external;
        /**
         * @dev function {removeLiquidityPool} onlyOwner
         *
         * Allows the manager to remove a liquidity pool
         *
         * @param removedLiquidityPool_ The address of the old removed liquidity pool
         */
        function removeLiquidityPool(address removedLiquidityPool_) external;
        /**
         * @dev function {isValidCaller}
         *
         * Return if an address is a valid caller
         *
         * @param queryHash_ The code hash being queried
         * @return bool The address is / isn't a valid caller
         */
        function isValidCaller(bytes32 queryHash_) external view returns (bool);
        /**
         * @dev function {validCallers}
         *
         * Returns a list of all valid caller code hashes
         *
         * @return validCallerHashes_ a list of all valid caller code hashes
         */
        function validCallers()
            external
            view
            returns (bytes32[] memory validCallerHashes_);
        /**
         * @dev function {addValidCaller} onlyOwner
         *
         * Allows the owner to add the hash of a valid caller
         *
         * @param newValidCallerHash_ The hash of the new valid caller
         */
        function addValidCaller(bytes32 newValidCallerHash_) external;
        /**
         * @dev function {removeValidCaller} onlyOwner
         *
         * Allows the owner to remove a valid caller
         *
         * @param removedValidCallerHash_ The hash of the old removed valid caller
         */
        function removeValidCaller(bytes32 removedValidCallerHash_) external;
        /**
         * @dev function {setProjectTaxRecipient} onlyOwner
         *
         * Allows the manager to set the project tax recipient address
         *
         * @param projectTaxRecipient_ New recipient address
         */
        function setProjectTaxRecipient(address projectTaxRecipient_) external;
        /**
         * @dev function {setSwapThresholdBasisPoints} onlyOwner
         *
         * Allows the manager to set the autoswap threshold
         *
         * @param swapThresholdBasisPoints_ New swap threshold in basis points
         */
        function setSwapThresholdBasisPoints(
            uint16 swapThresholdBasisPoints_
        ) external;
        /**
         * @dev function {setProjectTaxRates} onlyOwner
         *
         * Change the tax rates, subject to only ever decreasing
         *
         * @param newProjectBuyTaxBasisPoints_ The new buy tax rate
         * @param newProjectSellTaxBasisPoints_ The new sell tax rate
         */
        function setProjectTaxRates(
            uint16 newProjectBuyTaxBasisPoints_,
            uint16 newProjectSellTaxBasisPoints_
        ) external;
        /**
         * @dev totalBuyTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalBuyTaxBasisPoints() external view returns (uint256);
        /**
         * @dev totalSellTaxBasisPoints
         *
         * Provide easy to view tax total:
         */
        function totalSellTaxBasisPoints() external view returns (uint256);
        /**
         * @dev distributeTaxTokens
         *
         * Allows the distribution of tax tokens to the designated recipient(s)
         *
         * As part of standard processing the tax token balance being above the threshold
         * will trigger an autoswap to ETH and distribution of this ETH to the designated
         * recipients. This is automatic and there is no need for user involvement.
         *
         * As part of this swap there are a number of calculations performed, particularly
         * if the tax balance is above MAX_SWAP_THRESHOLD_MULTIPLE.
         *
         * Testing indicates that these calculations are safe. But given the data / code
         * interactions it remains possible that some edge case set of scenarios may cause
         * an issue with these calculations.
         *
         * This method is therefore provided as a 'fallback' option to safely distribute
         * accumulated taxes from the contract, with a direct transfer of the ERC20 tokens
         * themselves.
         */
        function distributeTaxTokens() external;
        /**
         * @dev function {withdrawETH} onlyOwner
         *
         * A withdraw function to allow ETH to be withdrawn by the manager
         *
         * This contract should never hold ETH. The only envisaged scenario where
         * it might hold ETH is a failed autoswap where the uniswap swap has completed,
         * the recipient of ETH reverts, the contract then wraps to WETH and the
         * wrap to WETH fails.
         *
         * This feels unlikely. But, for safety, we include this method.
         *
         * @param amount_ The amount to withdraw
         */
        function withdrawETH(uint256 amount_) external;
        /**
         * @dev function {withdrawERC20} onlyOwner
         *
         * A withdraw function to allow ERC20s (except address(this)) to be withdrawn.
         *
         * This contract should never hold ERC20s other than tax tokens. The only envisaged
         * scenario where it might hold an ERC20 is a failed autoswap where the uniswap swap
         * has completed, the recipient of ETH reverts, the contract then wraps to WETH, the
         * wrap to WETH succeeds, BUT then the transfer of WETH fails.
         *
         * This feels even less likely than the scenario where ETH is held on the contract.
         * But, for safety, we include this method.
         *
         * @param token_ The ERC20 contract
         * @param amount_ The amount to withdraw
         */
        function withdrawERC20(address token_, uint256 amount_) external;
        /**
         * @dev Destroys a `value` amount of tokens from the caller.
         *
         * See {ERC20-_burn}.
         */
        function burn(uint256 value) external;
        /**
         * @dev Destroys a `value` amount of tokens from `account`, deducting from
         * the caller's allowance.
         *
         * See {ERC20-_burn} and {ERC20-allowance}.
         *
         * Requirements:
         *
         * - the caller must have allowance for ``accounts``'s tokens of at least
         * `value`.
         */
        function burnFrom(address account, uint256 value) external;
        /**
         * @dev {initializer}
         *
         * @param integrationAddresses_ The project owner, uniswap router, LP currency
         * @param baseParams_ configuration of this ERC20.
         * param supplyParams_ Supply configuration of this ERC20.
         * param taxParams_  Tax configuration of this ERC20
         * param taxParams_  Launch pool configuration of this ERC20
         * param lpSupply_  Initial supply to be minted for LP
         */
        function initialize(
            address[3] memory integrationAddresses_,
            bytes memory baseParams_,
            bytes memory supplyParams_,
            bytes memory taxParams_
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    interface IERC20Config {
      struct ERC20Config {
        bytes baseParameters;
        bytes supplyParameters;
        bytes taxParameters;
        bytes poolParameters;
      }
      struct ERC20BaseParameters {
        string name;
        string symbol;
      }
      struct ERC20SupplyParameters {
        uint256 maxSupply;
        uint256 lpSupply;
        uint256 vaultSupply;
        uint256 maxTokensPerWallet;
        uint256 maxTokensPerTxn;
        uint256 botProtectionDurationInSeconds;
        address vault;
      }
      struct ERC20TaxParameters {
        uint256 projectBuyTaxBasisPoints;
        uint256 projectSellTaxBasisPoints;
        uint256 taxSwapThresholdBasisPoints;
        address projectTaxRecipient;
      }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.8.20;
    interface IErrors {
      enum BondingCurveErrorType {
        OK, //                                                  No error
        INVALID_NUMITEMS, //                                    The numItem value is 0
        SPOT_PRICE_OVERFLOW //                                  The updated spot price doesn't fit into 128 bits
      }
      error AdapterParamsMustBeEmpty(); //                      The adapter parameters on this LZ call must be empty.
      error AdditionToPoolIsBelowPerTransactionMinimum(); //    The contribution amount is less than the minimum.
      error AdditionToPoolWouldExceedPoolCap(); //              This addition to the pool would exceed the pool cap.
      error AdditionToPoolWouldExceedPerAddressCap(); //        This addition to the pool would exceed the per address cap.
      error AddressAlreadySet(); //                             The address being set can only be set once, and is already non-0.
      error AllowanceDecreasedBelowZero(); //                   You cannot decrease the allowance below zero.
      error AlreadyInitialised(); //                            The contract is already initialised: it cannot be initialised twice!
      error ApprovalCallerNotOwnerNorApproved(); //             The caller must own the token or be an approved operator.
      error ApproveFromTheZeroAddress(); //                     Approval cannot be called from the zero address (indeed, how have you??).
      error ApproveToTheZeroAddress(); //                       Approval cannot be given to the zero address.
      error ApprovalQueryForNonexistentToken(); //              The token does not exist.
      error AuctionStatusIsNotEnded(); //                       Throw if the action required the auction to be closed, and it isn't.
      error AuctionStatusIsNotOpen(); //                        Throw if the action requires the auction to be open, and it isn't.
      error AuxCallFailed(
        address[] modules,
        uint256 value,
        bytes data,
        uint256 txGas
      ); //                                                     An auxilliary call from the drop factory failed.
      error BalanceMismatch(); //                               An error when comparing balance amounts.
      error BalanceQueryForZeroAddress(); //                    Cannot query the balance for the zero address.
      error BidMustBeBelowTheFloorWhenReducingQuantity(); //    Only bids that are below the floor can reduce the quantity of the bid.
      error BidMustBeBelowTheFloorForRefundDuringAuction(); //  Only bids that are below the floor can be refunded during the auction.
      error BondingCurveError(BondingCurveErrorType error); //  An error of the type specified has occured in bonding curve processing.
      error BurnExceedsBalance(); //                            The amount you have selected to burn exceeds the addresses balance.
      error BurnFromTheZeroAddress(); //                        Tokens cannot be burned from the zero address. (Also, how have you called this!?!)
      error CallerIsNotDepositBoxOwner(); //                    The caller is not the owner of the deposit box.
      error CallerIsNotFactory(); //                            The caller of this function must match the factory address in storage.
      error CallerIsNotFactoryOrProjectOwner(); //              The caller of this function must match the factory address OR project owner address.
      error CallerIsNotFactoryProjectOwnerOrPool(); //          The caller of this function must match the factory address, project owner or pool address.
      error CallerIsNotTheOwner(); //                           The caller is not the owner of this contract.
      error CallerIsNotTheManager(); //                         The caller is not the manager of this contract.
      error CallerMustBeLzApp(); //                             The caller must be an LZ application.
      error CallerIsNotPlatformAdmin(address caller); //        The caller of this function must be part of the platformAdmin group.
      error CallerIsNotSuperAdmin(address caller); //           The caller of this function must match the superAdmin address in storage.
      error CannotAddLiquidityOnCreateAndUseDRIPool(); //       Cannot use both liquidity added on create and a DRIPool in the same token.
      error CannotSetNewOwnerToTheZeroAddress(); //             You can't set the owner of this contract to the zero address (address(0)).
      error CannotSetToZeroAddress(); //                        The corresponding address cannot be set to the zero address (address(0)).
      error CannotSetNewManagerToTheZeroAddress(); //           Cannot transfer the manager to the zero address (address(0)).
      error CannotWithdrawThisToken(); //                       Cannot withdraw the specified token.
      error CanOnlyReduce(); //                                 The given operation can only reduce the value specified.
      error CollectionAlreadyRevealed(); //                     The collection is already revealed; you cannot call reveal again.
      error ContractIsDecommissioned(); //                      This contract is decommissioned!
      error ContractIsPaused(); //                              The call requires the contract to be unpaused, and it is paused.
      error ContractIsNotPaused(); //                           The call required the contract to be paused, and it is NOT paused.
      error DecreasedAllowanceBelowZero(); //                   The request would decrease the allowance below zero, and that is not allowed.
      error DestinationIsNotTrustedSource(); //                 The destination that is being called through LZ has not been set as trusted.
      error DeployerOnly(); //                                  This method can only be called by the deployer address.
      error DeploymentError(); //                               Error on deployment.
      error DepositBoxIsNotOpen(); //                           This action cannot complete as the deposit box is not open.
      error DriPoolAddressCannotBeAddressZero(); //             The Dri Pool address cannot be the zero address.
      error GasLimitIsTooLow(); //                              The gas limit for the LayerZero call is too low.
      error IncorrectConfirmationValue(); //                    You need to enter the right confirmation value to call this funtion (usually 69420).
      error IncorrectPayment(); //                              The function call did not include passing the correct payment.
      error InitialLiquidityAlreadyAdded(); //                  Initial liquidity has already been added. You can't do it again.
      error InitialLiquidityNotYetAdded(); //                   Initial liquidity needs to have been added for this to succedd.
      error InsufficientAllowance(); //                         There is not a high enough allowance for this operation.
      error InvalidAdapterParams(); //                          The current adapter params for LayerZero on this contract won't work :(.
      error InvalidAddress(); //                                An address being processed in the function is not valid.
      error InvalidEndpointCaller(); //                         The calling address is not a valid LZ endpoint. The LZ endpoint was set at contract creation
      //                                                        and cannot be altered after. Check the address LZ endpoint address on the contract.
      error InvalidMinGas(); //                                 The minimum gas setting for LZ in invalid.
      error InvalidOracleSignature(); //                        The signature provided with the contract call is not valid, either in format or signer.
      error InvalidPayload(); //                                The LZ payload is invalid
      error InvalidReceiver(); //                               The address used as a target for funds is not valid.
      error InvalidSourceSendingContract(); //                  The LZ message is being related from a source contract on another chain that is NOT trusted.
      error InvalidTotalShares(); //                            Total shares must equal 100 percent in basis points.
      error LimitsCanOnlyBeRaised(); //                          Limits are UP ONLY.
      error ListLengthMismatch(); //                            Two or more lists were compared and they did not match length.
      error LiquidityPoolMustBeAContractAddress(); //           Cannot add a non-contract as a liquidity pool.
      error LiquidityPoolCannotBeAddressZero(); //              Cannot add a liquidity pool from the zero address.
      error LPLockUpMustFitUint88(); //                         LP lockup is held in a uint88, so must fit.
      error NoTrustedPathRecord(); //                           LZ needs a trusted path record for this to work. What's that, you ask?
      error MachineAddressCannotBeAddressZero(); //             Cannot set the machine address to the zero address.
      error ManagerUnauthorizedAccount(); //                    The caller is not the pending manager.
      error MaxBidQuantityIs255(); //                           Validation: as we use a uint8 array to track bid positions the max bid quantity is 255.
      error MaxPublicMintAllowanceExceeded(
        uint256 requested,
        uint256 alreadyMinted,
        uint256 maxAllowance
      ); //                                                     The calling address has requested a quantity that would exceed the max allowance.
      error MaxSupplyTooHigh(); //                              Max supply must fit in a uint128.
      error MaxTokensPerWalletExceeded(); //                    The transfer would exceed the max tokens per wallet limit.
      error MaxTokensPerTxnExceeded(); //                       The transfer would exceed the max tokens per transaction limit.
      error MetadataIsLocked(); //                              The metadata on this contract is locked; it cannot be altered!
      error MinGasLimitNotSet(); //                             The minimum gas limit for LayerZero has not been set.
      error MintERC2309QuantityExceedsLimit(); //               The `quantity` minted with ERC2309 exceeds the safety limit.
      error MintingIsClosedForever(); //                        Minting is, as the error suggests, so over (and locked forever).
      error MintToZeroAddress(); //                             Cannot mint to the zero address.
      error MintZeroQuantity(); //                              The quantity of tokens minted must be more than zero.
      error NewBuyTaxBasisPointsExceedsMaximum(); //            Project owner trying to set the tax rate too high.
      error NewSellTaxBasisPointsExceedsMaximum(); //           Project owner trying to set the tax rate too high.
      error NoETHForLiquidityPair(); //                         No ETH has been provided for the liquidity pair.
      error TaxPeriodStillInForce(); //                         The minimum tax period has not yet expired.
      error NoPaymentDue(); //                                  No payment is due for this address.
      error NoRefundForCaller(); //                             Error thrown when the calling address has no refund owed.
      error NoStoredMessage(); //                               There is no stored message matching the passed parameters.
      error NothingToClaim(); //                                The calling address has nothing to claim.
      error NoTokenForLiquidityPair(); //                       There is no token to add to the LP.
      error OperationDidNotSucceed(); //                        The operation failed (vague much?).
      error OracleSignatureHasExpired(); //                     A signature has been provided but it is too old.
      error OwnershipNotInitializedForExtraData(); //           The `extraData` cannot be set on an uninitialized ownership slot.
      error OwnerQueryForNonexistentToken(); //                 The token does not exist.
      error CallerIsNotAdminNorFactory();   //                  The caller of this function must match the factory address or be an admin.
      error ParametersDoNotMatchSignedMessage(); //             The parameters passed with the signed message do not match the message itself.
      error ParamTooLargeStartDate(); //                        The passed parameter exceeds the var type max.
      error ParamTooLargeEndDate(); //                          The passed parameter exceeds the var type max.
      error ParamTooLargeMinETH(); //                           The passed parameter exceeds the var type max.
      error ParamTooLargePerAddressMax(); //                    The passed parameter exceeds the var type max.
      error ParamTooLargeVestingDays(); //                      The passed parameter exceeds the var type max.
      error ParamTooLargePoolSupply(); //                       The passed parameter exceeds the var type max.
      error ParamTooLargePoolPerTxnMinETH(); //                 The passed parameter exceeds the var type max.
      error PassedConfigDoesNotMatchApproved(); //              The config provided on the call does not match the approved config.
      error PauseCutOffHasPassed(); //                          The time period in which we can pause has passed; this contract can no longer be paused.
      error PaymentMustCoverPerMintFee(); //                    The payment passed must at least cover the per mint fee for the quantity requested.
      error PermitDidNotSucceed(); //                           The safeERC20 permit failed.
      error PlatformAdminCannotBeAddressZero(); //              We cannot use the zero address (address(0)) as a platformAdmin.
      error PlatformTreasuryCannotBeAddressZero(); //           The treasury address cannot be set to the zero address.
      error PoolIsAboveMinimum(); //                            You required the pool to be below the minimum, and it is not
      error PoolIsBelowMinimum(); //                            You required the pool to be above the minimum, and it is not
      error PoolPhaseIsClosed(); //                             The block.timestamp is either before the pool is open or after it is closed.
      error PoolPhaseIsNotAfter(); //                           The block.timestamp is either before or during the pool open phase.
      error PoolVestingNotYetComplete(); //                     Tokens in the pool are not yet vested.
      error ProjectOwnerCannotBeAddressZero(); //               The project owner has to be a non zero address.
      error ProofInvalid(); //                                  The provided proof is not valid with the provided arguments.
      error QuantityExceedsRemainingCollectionSupply(); //      The requested quantity would breach the collection supply.
      error QuantityExceedsRemainingPhaseSupply(); //           The requested quantity would breach the phase supply.
      error QuantityExceedsMaxPossibleCollectionSupply(); //    The requested quantity would breach the maximum trackable supply
      error ReferralIdAlreadyUsed(); //                         This referral ID has already been used; they are one use only.
      error RequestingMoreThanAvailableBalance(); //             The request exceeds the available balance.
      error RequestingMoreThanRemainingAllocation(
        uint256 previouslyMinted,
        uint256 requested,
        uint256 remainingAllocation
      ); //                                                     Number of tokens requested for this mint exceeds the remaining allocation (taking the
      //                                                        original allocation from the list and deducting minted tokens).
      error RoyaltyFeeWillExceedSalePrice(); //                 The ERC2981 royalty specified will exceed the sale price.
      error ShareTotalCannotBeZero(); //                        The total of all the shares cannot be nothing.
      error SliceOutOfBounds(); //                              The bytes slice operation was out of bounds.
      error SliceOverflow(); //                                 The bytes slice operation overlowed.
      error SuperAdminCannotBeAddressZero(); //                 The superAdmin cannot be the sero address (address(0)).
      error SupplyTotalMismatch(); //                           The sum of the team supply and lp supply does not match.
      error SupportWindowIsNotOpen(); //                        The project owner has not requested support within the support request expiry window.
      error TaxFreeAddressCannotBeAddressZero(); //             A tax free address cannot be address(0)
      error TemplateCannotBeAddressZero(); //                   The address for a template cannot be address zero (address(0)).
      error TemplateNotFound(); //                              There is no template that matches the passed template Id.
      error ThisMintIsClosed(); //                              It's over (well, this mint is, anyway).
      error TotalSharesMustMatchDenominator(); //               The total of all shares must equal the denominator value.
      error TransferAmountExceedsBalance(); //                  The transfer amount exceeds the accounts available balance.
      error TransferCallerNotOwnerNorApproved(); //             The caller must own the token or be an approved operator.
      error TransferFailed(); //                                The transfer has failed.
      error TransferFromIncorrectOwner(); //                    The token must be owned by `from`.
      error TransferToNonERC721ReceiverImplementer(); //        Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
      error TransferFromZeroAddress(); //                       Cannot transfer from the zero address. Indeed, this surely is impossible, and likely a waste to check??
      error TransferToZeroAddress(); //                         Cannot transfer to the zero address.
      error UnrecognisedVRFMode(); //                           Currently supported VRF modes are 0: chainlink and 1: arrng
      error URIQueryForNonexistentToken(); //                   The token does not exist.
      error ValueExceedsMaximum(); //                           The value sent exceeds the maximum allowed (super useful explanation huh?).
      error VRFCoordinatorCannotBeAddressZero(); //             The VRF coordinator cannot be the zero address (address(0)).
    }