ETH Price: $2,521.48 (-0.29%)

Transaction Decoder

Block:
16765860 at Mar-06-2023 12:49:35 AM +UTC
Transaction Fee:
0.001107503113167864 ETH $2.79
Gas Used:
40,644 Gas / 27.248871006 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Coinbase: MEV Builder)
7.668470181775426911 Eth7.668484084784861559 Eth0.000013903009434648
0x5798d5e2...1A0181255
0.008931807549604181 Eth
Nonce: 3
0.007824304436436317 Eth
Nonce: 4
0.001107503113167864

Execution Trace

BearCave.claim( )
  • Gatekeeper.claim( ) => ( claimAmount=0 )
    claim[BearCave (ln:3801)]
    File 1 of 2: BearCave
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.3
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    /**
     * @dev Interface of ERC721A.
     */
    interface IERC721A {
        /**
         * The caller must own the token or be an approved operator.
         */
        error ApprovalCallerNotOwnerNorApproved();
        /**
         * The token does not exist.
         */
        error ApprovalQueryForNonexistentToken();
        /**
         * Cannot query the balance for the zero address.
         */
        error BalanceQueryForZeroAddress();
        /**
         * Cannot mint to the zero address.
         */
        error MintToZeroAddress();
        /**
         * The quantity of tokens minted must be more than zero.
         */
        error MintZeroQuantity();
        /**
         * The token does not exist.
         */
        error OwnerQueryForNonexistentToken();
        /**
         * The caller must own the token or be an approved operator.
         */
        error TransferCallerNotOwnerNorApproved();
        /**
         * The token must be owned by `from`.
         */
        error TransferFromIncorrectOwner();
        /**
         * Cannot safely transfer to a contract that does not implement the
         * ERC721Receiver interface.
         */
        error TransferToNonERC721ReceiverImplementer();
        /**
         * Cannot transfer to the zero address.
         */
        error TransferToZeroAddress();
        /**
         * The token does not exist.
         */
        error URIQueryForNonexistentToken();
        /**
         * The `quantity` minted with ERC2309 exceeds the safety limit.
         */
        error MintERC2309QuantityExceedsLimit();
        /**
         * The `extraData` cannot be set on an unintialized ownership slot.
         */
        error OwnershipNotInitializedForExtraData();
        // =============================================================
        //                            STRUCTS
        // =============================================================
        struct TokenOwnership {
            // The address of the owner.
            address addr;
            // Stores the start time of ownership with minimal overhead for tokenomics.
            uint64 startTimestamp;
            // Whether the token has been burned.
            bool burned;
            // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
            uint24 extraData;
        }
        // =============================================================
        //                         TOKEN COUNTERS
        // =============================================================
        /**
         * @dev Returns the total number of tokens in existence.
         * Burned tokens will reduce the count.
         * To get the total number of tokens minted, please see {_totalMinted}.
         */
        function totalSupply() external view returns (uint256);
        // =============================================================
        //                            IERC165
        // =============================================================
        /**
         * @dev Returns true if this contract implements the interface defined by
         * `interfaceId`. See the corresponding
         * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
         * to learn more about how these ids are created.
         *
         * This function call must use less than 30000 gas.
         */
        function supportsInterface(bytes4 interfaceId) external view returns (bool);
        // =============================================================
        //                            IERC721
        // =============================================================
        /**
         * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
         */
        event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
         */
        event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
        /**
         * @dev Emitted when `owner` enables or disables
         * (`approved`) `operator` to manage all of its assets.
         */
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /**
         * @dev Returns the number of tokens in `owner`'s account.
         */
        function balanceOf(address owner) external view returns (uint256 balance);
        /**
         * @dev Returns the owner of the `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function ownerOf(uint256 tokenId) external view returns (address owner);
        /**
         * @dev Safely transfers `tokenId` token from `from` to `to`,
         * checking first that contract recipients are aware of the ERC721 protocol
         * to prevent tokens from being forever locked.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must exist and be owned by `from`.
         * - If the caller is not `from`, it must be have been allowed to move
         * this token by either {approve} or {setApprovalForAll}.
         * - If `to` refers to a smart contract, it must implement
         * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
         *
         * Emits a {Transfer} event.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId,
            bytes calldata data
        ) external payable;
        /**
         * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
         */
        function safeTransferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external payable;
        /**
         * @dev Transfers `tokenId` from `from` to `to`.
         *
         * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
         * whenever possible.
         *
         * Requirements:
         *
         * - `from` cannot be the zero address.
         * - `to` cannot be the zero address.
         * - `tokenId` token must be owned by `from`.
         * - If the caller is not `from`, it must be approved to move this token
         * by either {approve} or {setApprovalForAll}.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(
            address from,
            address to,
            uint256 tokenId
        ) external payable;
        /**
         * @dev Gives permission to `to` to transfer `tokenId` token to another account.
         * The approval is cleared when the token is transferred.
         *
         * Only a single account can be approved at a time, so approving the
         * zero address clears previous approvals.
         *
         * Requirements:
         *
         * - The caller must own the token or be an approved operator.
         * - `tokenId` must exist.
         *
         * Emits an {Approval} event.
         */
        function approve(address to, uint256 tokenId) external payable;
        /**
         * @dev Approve or remove `operator` as an operator for the caller.
         * Operators can call {transferFrom} or {safeTransferFrom}
         * for any token owned by the caller.
         *
         * Requirements:
         *
         * - The `operator` cannot be the caller.
         *
         * Emits an {ApprovalForAll} event.
         */
        function setApprovalForAll(address operator, bool _approved) external;
        /**
         * @dev Returns the account approved for `tokenId` token.
         *
         * Requirements:
         *
         * - `tokenId` must exist.
         */
        function getApproved(uint256 tokenId) external view returns (address operator);
        /**
         * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
         *
         * See {setApprovalForAll}.
         */
        function isApprovedForAll(address owner, address operator) external view returns (bool);
        // =============================================================
        //                        IERC721Metadata
        // =============================================================
        /**
         * @dev Returns the token collection name.
         */
        function name() external view returns (string memory);
        /**
         * @dev Returns the token collection symbol.
         */
        function symbol() external view returns (string memory);
        /**
         * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
         */
        function tokenURI(uint256 tokenId) external view returns (string memory);
        // =============================================================
        //                           IERC2309
        // =============================================================
        /**
         * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
         * (inclusive) is transferred from `from` to `to`, as defined in the
         * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
         *
         * See {_mintERC2309} for more details.
         */
        event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
    }
    // SPDX-License-Identifier: MIT
    // ERC721A Contracts v4.2.3
    // Creator: Chiru Labs
    pragma solidity ^0.8.4;
    import '../IERC721A.sol';
    /**
     * @dev Interface of ERC721AQueryable.
     */
    interface IERC721AQueryable is IERC721A {
        /**
         * Invalid query range (`start` >= `stop`).
         */
        error InvalidQueryRange();
        /**
         * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
         *
         * If the `tokenId` is out of bounds:
         *
         * - `addr = address(0)`
         * - `startTimestamp = 0`
         * - `burned = false`
         * - `extraData = 0`
         *
         * If the `tokenId` is burned:
         *
         * - `addr = <Address of owner before token was burned>`
         * - `startTimestamp = <Timestamp when token was burned>`
         * - `burned = true`
         * - `extraData = <Extra data when token was burned>`
         *
         * Otherwise:
         *
         * - `addr = <Address of owner>`
         * - `startTimestamp = <Timestamp of start of ownership>`
         * - `burned = false`
         * - `extraData = <Extra data at start of ownership>`
         */
        function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
        /**
         * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
         * See {ERC721AQueryable-explicitOwnershipOf}
         */
        function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`,
         * in the range [`start`, `stop`)
         * (i.e. `start <= tokenId < stop`).
         *
         * This function allows for tokens to be queried if the collection
         * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
         *
         * Requirements:
         *
         * - `start < stop`
         */
        function tokensOfOwnerIn(
            address owner,
            uint256 start,
            uint256 stop
        ) external view returns (uint256[] memory);
        /**
         * @dev Returns an array of token IDs owned by `owner`.
         *
         * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
         * It is meant to be called off-chain.
         *
         * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
         * multiple smaller scans if the collection is large enough to cause
         * an out-of-gas error (10K collections should be fine).
         */
        function tokensOfOwner(address owner) external view returns (uint256[] memory);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.4;
    /** ****************************************************************************
     * @notice Interface for contracts using VRF randomness
     * *****************************************************************************
     * @dev PURPOSE
     *
     * @dev Reggie the Random Oracle (not his real job) wants to provide randomness
     * @dev to Vera the verifier in such a way that Vera can be sure he's not
     * @dev making his output up to suit himself. Reggie provides Vera a public key
     * @dev to which he knows the secret key. Each time Vera provides a seed to
     * @dev Reggie, he gives back a value which is computed completely
     * @dev deterministically from the seed and the secret key.
     *
     * @dev Reggie provides a proof by which Vera can verify that the output was
     * @dev correctly computed once Reggie tells it to her, but without that proof,
     * @dev the output is indistinguishable to her from a uniform random sample
     * @dev from the output space.
     *
     * @dev The purpose of this contract is to make it easy for unrelated contracts
     * @dev to talk to Vera the verifier about the work Reggie is doing, to provide
     * @dev simple access to a verifiable source of randomness. It ensures 2 things:
     * @dev 1. The fulfillment came from the VRFCoordinator
     * @dev 2. The consumer contract implements fulfillRandomWords.
     * *****************************************************************************
     * @dev USAGE
     *
     * @dev Calling contracts must inherit from VRFConsumerBase, and can
     * @dev initialize VRFConsumerBase's attributes in their constructor as
     * @dev shown:
     *
     * @dev   contract VRFConsumer {
     * @dev     constructor(<other arguments>, address _vrfCoordinator, address _link)
     * @dev       VRFConsumerBase(_vrfCoordinator) public {
     * @dev         <initialization with other arguments goes here>
     * @dev       }
     * @dev   }
     *
     * @dev The oracle will have given you an ID for the VRF keypair they have
     * @dev committed to (let's call it keyHash). Create subscription, fund it
     * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
     * @dev subscription management functions).
     * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
     * @dev callbackGasLimit, numWords),
     * @dev see (VRFCoordinatorInterface for a description of the arguments).
     *
     * @dev Once the VRFCoordinator has received and validated the oracle's response
     * @dev to your request, it will call your contract's fulfillRandomWords method.
     *
     * @dev The randomness argument to fulfillRandomWords is a set of random words
     * @dev generated from your requestId and the blockHash of the request.
     *
     * @dev If your contract could have concurrent requests open, you can use the
     * @dev requestId returned from requestRandomWords to track which response is associated
     * @dev with which randomness request.
     * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
     * @dev if your contract could have multiple requests in flight simultaneously.
     *
     * @dev Colliding `requestId`s are cryptographically impossible as long as seeds
     * @dev differ.
     *
     * *****************************************************************************
     * @dev SECURITY CONSIDERATIONS
     *
     * @dev A method with the ability to call your fulfillRandomness method directly
     * @dev could spoof a VRF response with any random value, so it's critical that
     * @dev it cannot be directly called by anything other than this base contract
     * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
     *
     * @dev For your users to trust that your contract's random behavior is free
     * @dev from malicious interference, it's best if you can write it so that all
     * @dev behaviors implied by a VRF response are executed *during* your
     * @dev fulfillRandomness method. If your contract must store the response (or
     * @dev anything derived from it) and use it later, you must ensure that any
     * @dev user-significant behavior which depends on that stored value cannot be
     * @dev manipulated by a subsequent VRF request.
     *
     * @dev Similarly, both miners and the VRF oracle itself have some influence
     * @dev over the order in which VRF responses appear on the blockchain, so if
     * @dev your contract could have multiple VRF requests in flight simultaneously,
     * @dev you must ensure that the order in which the VRF responses arrive cannot
     * @dev be used to manipulate your contract's user-significant behavior.
     *
     * @dev Since the block hash of the block which contains the requestRandomness
     * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
     * @dev miner could, in principle, fork the blockchain to evict the block
     * @dev containing the request, forcing the request to be included in a
     * @dev different block with a different hash, and therefore a different input
     * @dev to the VRF. However, such an attack would incur a substantial economic
     * @dev cost. This cost scales with the number of blocks the VRF oracle waits
     * @dev until it calls responds to a request. It is for this reason that
     * @dev that you can signal to an oracle you'd like them to wait longer before
     * @dev responding to the request (however this is not enforced in the contract
     * @dev and so remains effective only in the case of unmodified oracle software).
     */
    abstract contract VRFConsumerBaseV2 {
      error OnlyCoordinatorCanFulfill(address have, address want);
      address private immutable vrfCoordinator;
      /**
       * @param _vrfCoordinator address of VRFCoordinator contract
       */
      constructor(address _vrfCoordinator) {
        vrfCoordinator = _vrfCoordinator;
      }
      /**
       * @notice fulfillRandomness handles the VRF response. Your contract must
       * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
       * @notice principles to keep in mind when implementing your fulfillRandomness
       * @notice method.
       *
       * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this
       * @dev signature, and will call it once it has verified the proof
       * @dev associated with the randomness. (It is triggered via a call to
       * @dev rawFulfillRandomness, below.)
       *
       * @param requestId The Id initially returned by requestRandomness
       * @param randomWords the VRF output expanded to the requested number of words
       */
      function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;
      // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
      // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
      // the origin of the call
      function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
        if (msg.sender != vrfCoordinator) {
          revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
        }
        fulfillRandomWords(requestId, randomWords);
      }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    interface VRFCoordinatorV2Interface {
      /**
       * @notice Get configuration relevant for making requests
       * @return minimumRequestConfirmations global min for request confirmations
       * @return maxGasLimit global max for request gas limit
       * @return s_provingKeyHashes list of registered key hashes
       */
      function getRequestConfig()
        external
        view
        returns (
          uint16,
          uint32,
          bytes32[] memory
        );
      /**
       * @notice Request a set of random words.
       * @param keyHash - Corresponds to a particular oracle job which uses
       * that key for generating the VRF proof. Different keyHash's have different gas price
       * ceilings, so you can select a specific one to bound your maximum per request cost.
       * @param subId  - The ID of the VRF subscription. Must be funded
       * with the minimum subscription balance required for the selected keyHash.
       * @param minimumRequestConfirmations - How many blocks you'd like the
       * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
       * for why you may want to request more. The acceptable range is
       * [minimumRequestBlockConfirmations, 200].
       * @param callbackGasLimit - How much gas you'd like to receive in your
       * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
       * may be slightly less than this amount because of gas used calling the function
       * (argument decoding etc.), so you may need to request slightly more than you expect
       * to have inside fulfillRandomWords. The acceptable range is
       * [0, maxGasLimit]
       * @param numWords - The number of uint256 random values you'd like to receive
       * in your fulfillRandomWords callback. Note these numbers are expanded in a
       * secure way by the VRFCoordinator from a single random value supplied by the oracle.
       * @return requestId - A unique identifier of the request. Can be used to match
       * a request to a response in fulfillRandomWords.
       */
      function requestRandomWords(
        bytes32 keyHash,
        uint64 subId,
        uint16 minimumRequestConfirmations,
        uint32 callbackGasLimit,
        uint32 numWords
      ) external returns (uint256 requestId);
      /**
       * @notice Create a VRF subscription.
       * @return subId - A unique subscription id.
       * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
       * @dev Note to fund the subscription, use transferAndCall. For example
       * @dev  LINKTOKEN.transferAndCall(
       * @dev    address(COORDINATOR),
       * @dev    amount,
       * @dev    abi.encode(subId));
       */
      function createSubscription() external returns (uint64 subId);
      /**
       * @notice Get a VRF subscription.
       * @param subId - ID of the subscription
       * @return balance - LINK balance of the subscription in juels.
       * @return reqCount - number of requests for this subscription, determines fee tier.
       * @return owner - owner of the subscription.
       * @return consumers - list of consumer address which are able to use this subscription.
       */
      function getSubscription(uint64 subId)
        external
        view
        returns (
          uint96 balance,
          uint64 reqCount,
          address owner,
          address[] memory consumers
        );
      /**
       * @notice Request subscription owner transfer.
       * @param subId - ID of the subscription
       * @param newOwner - proposed new owner of the subscription
       */
      function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;
      /**
       * @notice Request subscription owner transfer.
       * @param subId - ID of the subscription
       * @dev will revert if original owner of subId has
       * not requested that msg.sender become the new owner.
       */
      function acceptSubscriptionOwnerTransfer(uint64 subId) external;
      /**
       * @notice Add a consumer to a VRF subscription.
       * @param subId - ID of the subscription
       * @param consumer - New consumer which can use the subscription
       */
      function addConsumer(uint64 subId, address consumer) external;
      /**
       * @notice Remove a consumer from a VRF subscription.
       * @param subId - ID of the subscription
       * @param consumer - Consumer to remove from the subscription
       */
      function removeConsumer(uint64 subId, address consumer) external;
      /**
       * @notice Cancel a subscription
       * @param subId - ID of the subscription
       * @param to - Where to send the remaining LINK to
       */
      function cancelSubscription(uint64 subId, address to) external;
      /*
       * @notice Check to see if there exists a request commitment consumers
       * for all consumers and keyhashes for a given sub.
       * @param subId - ID of the subscription
       * @return true if there exists at least one unfulfilled request for the subscription, false
       * otherwise.
       */
      function pendingRequestExists(uint64 subId) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.4.22 <0.9.0;
    /// @dev The original console.sol uses `int` and `uint` for computing function selectors, but it should
    /// use `int256` and `uint256`. This modified version fixes that. This version is recommended
    /// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in
    /// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`.
    /// Reference: https://github.com/NomicFoundation/hardhat/issues/2178
    library console2 {
        address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);
        function _sendLogPayload(bytes memory payload) private view {
            uint256 payloadLength = payload.length;
            address consoleAddress = CONSOLE_ADDRESS;
            /// @solidity memory-safe-assembly
            assembly {
                let payloadStart := add(payload, 32)
                let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
            }
        }
        function log() internal view {
            _sendLogPayload(abi.encodeWithSignature("log()"));
        }
        function logInt(int256 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
        }
        function logUint(uint256 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
        }
        function logString(string memory p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
        }
        function logBool(bool p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
        }
        function logAddress(address p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
        }
        function logBytes(bytes memory p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
        }
        function logBytes1(bytes1 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
        }
        function logBytes2(bytes2 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
        }
        function logBytes3(bytes3 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
        }
        function logBytes4(bytes4 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
        }
        function logBytes5(bytes5 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
        }
        function logBytes6(bytes6 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
        }
        function logBytes7(bytes7 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
        }
        function logBytes8(bytes8 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
        }
        function logBytes9(bytes9 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
        }
        function logBytes10(bytes10 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
        }
        function logBytes11(bytes11 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
        }
        function logBytes12(bytes12 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
        }
        function logBytes13(bytes13 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
        }
        function logBytes14(bytes14 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
        }
        function logBytes15(bytes15 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
        }
        function logBytes16(bytes16 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
        }
        function logBytes17(bytes17 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
        }
        function logBytes18(bytes18 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
        }
        function logBytes19(bytes19 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
        }
        function logBytes20(bytes20 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
        }
        function logBytes21(bytes21 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
        }
        function logBytes22(bytes22 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
        }
        function logBytes23(bytes23 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
        }
        function logBytes24(bytes24 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
        }
        function logBytes25(bytes25 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
        }
        function logBytes26(bytes26 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
        }
        function logBytes27(bytes27 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
        }
        function logBytes28(bytes28 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
        }
        function logBytes29(bytes29 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
        }
        function logBytes30(bytes30 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
        }
        function logBytes31(bytes31 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
        }
        function logBytes32(bytes32 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
        }
        function log(uint256 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
        }
        function log(int256 p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
        }
        function log(string memory p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
        }
        function log(bool p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
        }
        function log(address p0) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
        }
        function log(uint256 p0, uint256 p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1));
        }
        function log(uint256 p0, string memory p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1));
        }
        function log(uint256 p0, bool p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1));
        }
        function log(uint256 p0, address p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1));
        }
        function log(string memory p0, uint256 p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
        }
        function log(string memory p0, int256 p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,int256)", p0, p1));
        }
        function log(string memory p0, string memory p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
        }
        function log(string memory p0, bool p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
        }
        function log(string memory p0, address p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
        }
        function log(bool p0, uint256 p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1));
        }
        function log(bool p0, string memory p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
        }
        function log(bool p0, bool p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
        }
        function log(bool p0, address p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
        }
        function log(address p0, uint256 p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1));
        }
        function log(address p0, string memory p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
        }
        function log(address p0, bool p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
        }
        function log(address p0, address p1) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
        }
        function log(uint256 p0, uint256 p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2));
        }
        function log(uint256 p0, uint256 p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2));
        }
        function log(uint256 p0, uint256 p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2));
        }
        function log(uint256 p0, uint256 p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2));
        }
        function log(uint256 p0, string memory p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2));
        }
        function log(uint256 p0, string memory p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2));
        }
        function log(uint256 p0, string memory p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2));
        }
        function log(uint256 p0, string memory p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2));
        }
        function log(uint256 p0, bool p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2));
        }
        function log(uint256 p0, bool p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2));
        }
        function log(uint256 p0, bool p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2));
        }
        function log(uint256 p0, bool p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2));
        }
        function log(uint256 p0, address p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2));
        }
        function log(uint256 p0, address p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2));
        }
        function log(uint256 p0, address p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2));
        }
        function log(uint256 p0, address p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2));
        }
        function log(string memory p0, uint256 p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
        }
        function log(string memory p0, uint256 p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2));
        }
        function log(string memory p0, uint256 p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2));
        }
        function log(string memory p0, uint256 p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2));
        }
        function log(string memory p0, string memory p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2));
        }
        function log(string memory p0, string memory p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
        }
        function log(string memory p0, string memory p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
        }
        function log(string memory p0, string memory p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
        }
        function log(string memory p0, bool p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2));
        }
        function log(string memory p0, bool p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
        }
        function log(string memory p0, bool p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
        }
        function log(string memory p0, bool p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
        }
        function log(string memory p0, address p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2));
        }
        function log(string memory p0, address p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
        }
        function log(string memory p0, address p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
        }
        function log(string memory p0, address p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
        }
        function log(bool p0, uint256 p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2));
        }
        function log(bool p0, uint256 p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2));
        }
        function log(bool p0, uint256 p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2));
        }
        function log(bool p0, uint256 p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2));
        }
        function log(bool p0, string memory p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2));
        }
        function log(bool p0, string memory p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
        }
        function log(bool p0, string memory p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
        }
        function log(bool p0, string memory p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
        }
        function log(bool p0, bool p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2));
        }
        function log(bool p0, bool p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
        }
        function log(bool p0, bool p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
        }
        function log(bool p0, bool p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
        }
        function log(bool p0, address p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2));
        }
        function log(bool p0, address p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
        }
        function log(bool p0, address p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
        }
        function log(bool p0, address p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
        }
        function log(address p0, uint256 p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2));
        }
        function log(address p0, uint256 p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2));
        }
        function log(address p0, uint256 p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2));
        }
        function log(address p0, uint256 p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2));
        }
        function log(address p0, string memory p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2));
        }
        function log(address p0, string memory p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
        }
        function log(address p0, string memory p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
        }
        function log(address p0, string memory p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
        }
        function log(address p0, bool p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2));
        }
        function log(address p0, bool p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
        }
        function log(address p0, bool p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
        }
        function log(address p0, bool p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
        }
        function log(address p0, address p1, uint256 p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2));
        }
        function log(address p0, address p1, string memory p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
        }
        function log(address p0, address p1, bool p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
        }
        function log(address p0, address p1, address p2) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
        }
        function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, uint256 p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, string memory p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, bool p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3));
        }
        function log(uint256 p0, address p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, uint256 p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, string memory p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, bool p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
        }
        function log(string memory p0, address p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, uint256 p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, string memory p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, bool p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
        }
        function log(bool p0, address p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3));
        }
        function log(address p0, uint256 p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
        }
        function log(address p0, string memory p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
        }
        function log(address p0, bool p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, uint256 p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, uint256 p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, uint256 p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, uint256 p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, string memory p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, string memory p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, string memory p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, string memory p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, bool p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, bool p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, bool p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, bool p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, address p2, uint256 p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, address p2, string memory p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, address p2, bool p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
        }
        function log(address p0, address p1, address p2, address p3) internal view {
            _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
        }
    }// SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `_msgSender()` is missing `role`.
         * Overriding this function changes the behavior of the {onlyRole} modifier.
         *
         * Format of the revert message is described in {_checkRole}.
         *
         * _Available since v4.6._
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(account),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * May emit a {RoleGranted} event.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev 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) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    import "./math/Math.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 v4.8.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds up instead
         * of rounding down.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
         * with further edits by Uniswap Labs also under MIT license.
         */
        function mulDiv(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod0 := mul(x, y)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1);
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                // See https://cs.stackexchange.com/q/138556/92363.
                // Does not overflow because the denominator cannot be zero at this stage in the function.
                uint256 twos = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(
            uint256 x,
            uint256 y,
            uint256 denominator,
            Rounding rounding
        ) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10**64) {
                    value /= 10**64;
                    result += 64;
                }
                if (value >= 10**32) {
                    value /= 10**32;
                    result += 32;
                }
                if (value >= 10**16) {
                    value /= 10**16;
                    result += 16;
                }
                if (value >= 10**8) {
                    value /= 10**8;
                    result += 8;
                }
                if (value >= 10**4) {
                    value /= 10**4;
                    result += 4;
                }
                if (value >= 10**2) {
                    value /= 10**2;
                    result += 2;
                }
                if (value >= 10**1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256, rounded down, of a positive value.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
            }
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Minimalist and gas efficient standard ERC1155 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
    abstract contract ERC1155 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event TransferSingle(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256 id,
            uint256 amount
        );
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] amounts
        );
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        event URI(string value, uint256 indexed id);
        /*//////////////////////////////////////////////////////////////
                                 ERC1155 STORAGE
        //////////////////////////////////////////////////////////////*/
        mapping(address => mapping(uint256 => uint256)) public balanceOf;
        mapping(address => mapping(address => bool)) public isApprovedForAll;
        /*//////////////////////////////////////////////////////////////
                                 METADATA LOGIC
        //////////////////////////////////////////////////////////////*/
        function uri(uint256 id) public view virtual returns (string memory);
        /*//////////////////////////////////////////////////////////////
                                  ERC1155 LOGIC
        //////////////////////////////////////////////////////////////*/
        function setApprovalForAll(address operator, bool approved) public virtual {
            isApprovedForAll[msg.sender][operator] = approved;
            emit ApprovalForAll(msg.sender, operator, approved);
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) public virtual {
            require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
            balanceOf[from][id] -= amount;
            balanceOf[to][id] += amount;
            emit TransferSingle(msg.sender, from, to, id, amount);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
                        ERC1155TokenReceiver.onERC1155Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) public virtual {
            require(ids.length == amounts.length, "LENGTH_MISMATCH");
            require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
            // Storing these outside the loop saves ~15 gas per iteration.
            uint256 id;
            uint256 amount;
            for (uint256 i = 0; i < ids.length; ) {
                id = ids[i];
                amount = amounts[i];
                balanceOf[from][id] -= amount;
                balanceOf[to][id] += amount;
                // An array can't have a total length
                // larger than the max uint256 value.
                unchecked {
                    ++i;
                }
            }
            emit TransferBatch(msg.sender, from, to, ids, amounts);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
                        ERC1155TokenReceiver.onERC1155BatchReceived.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
            public
            view
            virtual
            returns (uint256[] memory balances)
        {
            require(owners.length == ids.length, "LENGTH_MISMATCH");
            balances = new uint256[](owners.length);
            // Unchecked because the only math done is incrementing
            // the array index counter which cannot possibly overflow.
            unchecked {
                for (uint256 i = 0; i < owners.length; ++i) {
                    balances[i] = balanceOf[owners[i]][ids[i]];
                }
            }
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC165 LOGIC
        //////////////////////////////////////////////////////////////*/
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
                interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            balanceOf[to][id] += amount;
            emit TransferSingle(msg.sender, address(0), to, id, amount);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
                        ERC1155TokenReceiver.onERC1155Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _batchMint(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            uint256 idsLength = ids.length; // Saves MLOADs.
            require(idsLength == amounts.length, "LENGTH_MISMATCH");
            for (uint256 i = 0; i < idsLength; ) {
                balanceOf[to][ids[i]] += amounts[i];
                // An array can't have a total length
                // larger than the max uint256 value.
                unchecked {
                    ++i;
                }
            }
            emit TransferBatch(msg.sender, address(0), to, ids, amounts);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
                        ERC1155TokenReceiver.onERC1155BatchReceived.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _batchBurn(
            address from,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual {
            uint256 idsLength = ids.length; // Saves MLOADs.
            require(idsLength == amounts.length, "LENGTH_MISMATCH");
            for (uint256 i = 0; i < idsLength; ) {
                balanceOf[from][ids[i]] -= amounts[i];
                // An array can't have a total length
                // larger than the max uint256 value.
                unchecked {
                    ++i;
                }
            }
            emit TransferBatch(msg.sender, from, address(0), ids, amounts);
        }
        function _burn(
            address from,
            uint256 id,
            uint256 amount
        ) internal virtual {
            balanceOf[from][id] -= amount;
            emit TransferSingle(msg.sender, from, address(0), id, amount);
        }
    }
    /// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
    abstract contract ERC1155TokenReceiver {
        function onERC1155Received(
            address,
            address,
            uint256,
            uint256,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC1155TokenReceiver.onERC1155Received.selector;
        }
        function onERC1155BatchReceived(
            address,
            address,
            uint256[] calldata,
            uint256[] calldata,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    abstract contract ERC20 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 amount);
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /*//////////////////////////////////////////////////////////////
                                METADATA STORAGE
        //////////////////////////////////////////////////////////////*/
        string public name;
        string public symbol;
        uint8 public immutable decimals;
        /*//////////////////////////////////////////////////////////////
                                  ERC20 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 public totalSupply;
        mapping(address => uint256) public balanceOf;
        mapping(address => mapping(address => uint256)) public allowance;
        /*//////////////////////////////////////////////////////////////
                                EIP-2612 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 internal immutable INITIAL_CHAIN_ID;
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
        mapping(address => uint256) public nonces;
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
            INITIAL_CHAIN_ID = block.chainid;
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        }
        /*//////////////////////////////////////////////////////////////
                                   ERC20 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            allowance[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);
            return true;
        }
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            balanceOf[msg.sender] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(msg.sender, to, amount);
            return true;
        }
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
            balanceOf[from] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(from, to, amount);
            return true;
        }
        /*//////////////////////////////////////////////////////////////
                                 EIP-2612 LOGIC
        //////////////////////////////////////////////////////////////*/
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
            // Unchecked because the only math done is incrementing
            // the owner's nonce which cannot realistically overflow.
            unchecked {
                address recoveredAddress = ecrecover(
                    keccak256(
                        abi.encodePacked(
                            "\\x19\\x01",
                            DOMAIN_SEPARATOR(),
                            keccak256(
                                abi.encode(
                                    keccak256(
                                        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                    ),
                                    owner,
                                    spender,
                                    value,
                                    nonces[owner]++,
                                    deadline
                                )
                            )
                        )
                    ),
                    v,
                    r,
                    s
                );
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                allowance[recoveredAddress][spender] = value;
            }
            emit Approval(owner, spender, value);
        }
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
        }
        function computeDomainSeparator() internal view virtual returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name)),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 amount) internal virtual {
            totalSupply += amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(address(0), to, amount);
        }
        function _burn(address from, uint256 amount) internal virtual {
            balanceOf[from] -= amount;
            // Cannot underflow because a user's balance
            // will never be larger than the total supply.
            unchecked {
                totalSupply -= amount;
            }
            emit Transfer(from, address(0), amount);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
    abstract contract ERC721 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 indexed id);
        event Approval(address indexed owner, address indexed spender, uint256 indexed id);
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /*//////////////////////////////////////////////////////////////
                             METADATA STORAGE/LOGIC
        //////////////////////////////////////////////////////////////*/
        string public name;
        string public symbol;
        function tokenURI(uint256 id) public view virtual returns (string memory);
        /*//////////////////////////////////////////////////////////////
                          ERC721 BALANCE/OWNER STORAGE
        //////////////////////////////////////////////////////////////*/
        mapping(uint256 => address) internal _ownerOf;
        mapping(address => uint256) internal _balanceOf;
        function ownerOf(uint256 id) public view virtual returns (address owner) {
            require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
        }
        function balanceOf(address owner) public view virtual returns (uint256) {
            require(owner != address(0), "ZERO_ADDRESS");
            return _balanceOf[owner];
        }
        /*//////////////////////////////////////////////////////////////
                             ERC721 APPROVAL STORAGE
        //////////////////////////////////////////////////////////////*/
        mapping(uint256 => address) public getApproved;
        mapping(address => mapping(address => bool)) public isApprovedForAll;
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(string memory _name, string memory _symbol) {
            name = _name;
            symbol = _symbol;
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC721 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 id) public virtual {
            address owner = _ownerOf[id];
            require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
            getApproved[id] = spender;
            emit Approval(owner, spender, id);
        }
        function setApprovalForAll(address operator, bool approved) public virtual {
            isApprovedForAll[msg.sender][operator] = approved;
            emit ApprovalForAll(msg.sender, operator, approved);
        }
        function transferFrom(
            address from,
            address to,
            uint256 id
        ) public virtual {
            require(from == _ownerOf[id], "WRONG_FROM");
            require(to != address(0), "INVALID_RECIPIENT");
            require(
                msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                "NOT_AUTHORIZED"
            );
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            unchecked {
                _balanceOf[from]--;
                _balanceOf[to]++;
            }
            _ownerOf[id] = to;
            delete getApproved[id];
            emit Transfer(from, to, id);
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id
        ) public virtual {
            transferFrom(from, to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            bytes calldata data
        ) public virtual {
            transferFrom(from, to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC165 LOGIC
        //////////////////////////////////////////////////////////////*/
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 id) internal virtual {
            require(to != address(0), "INVALID_RECIPIENT");
            require(_ownerOf[id] == address(0), "ALREADY_MINTED");
            // Counter overflow is incredibly unrealistic.
            unchecked {
                _balanceOf[to]++;
            }
            _ownerOf[id] = to;
            emit Transfer(address(0), to, id);
        }
        function _burn(uint256 id) internal virtual {
            address owner = _ownerOf[id];
            require(owner != address(0), "NOT_MINTED");
            // Ownership check above ensures no underflow.
            unchecked {
                _balanceOf[owner]--;
            }
            delete _ownerOf[id];
            delete getApproved[id];
            emit Transfer(owner, address(0), id);
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL SAFE MINT LOGIC
        //////////////////////////////////////////////////////////////*/
        function _safeMint(address to, uint256 id) internal virtual {
            _mint(to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _safeMint(
            address to,
            uint256 id,
            bytes memory data
        ) internal virtual {
            _mint(to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
    }
    /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
    abstract contract ERC721TokenReceiver {
        function onERC721Received(
            address,
            address,
            uint256,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC721TokenReceiver.onERC721Received.selector;
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Arithmetic library with operations for fixed-point numbers.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
    /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
    library FixedPointMathLib {
        /*//////////////////////////////////////////////////////////////
                        SIMPLIFIED FIXED POINT OPERATIONS
        //////////////////////////////////////////////////////////////*/
        uint256 internal constant MAX_UINT256 = 2**256 - 1;
        uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
        function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
            return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
        }
        function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
            return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
        }
        function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
            return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
        }
        function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
            return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
        }
        /*//////////////////////////////////////////////////////////////
                        LOW LEVEL FIXED POINT OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function mulDivDown(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 z) {
            /// @solidity memory-safe-assembly
            assembly {
                // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                    revert(0, 0)
                }
                // Divide x * y by the denominator.
                z := div(mul(x, y), denominator)
            }
        }
        function mulDivUp(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 z) {
            /// @solidity memory-safe-assembly
            assembly {
                // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                    revert(0, 0)
                }
                // If x * y modulo the denominator is strictly greater than 0,
                // 1 is added to round up the division of x * y by the denominator.
                z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
            }
        }
        function rpow(
            uint256 x,
            uint256 n,
            uint256 scalar
        ) internal pure returns (uint256 z) {
            /// @solidity memory-safe-assembly
            assembly {
                switch x
                case 0 {
                    switch n
                    case 0 {
                        // 0 ** 0 = 1
                        z := scalar
                    }
                    default {
                        // 0 ** n = 0
                        z := 0
                    }
                }
                default {
                    switch mod(n, 2)
                    case 0 {
                        // If n is even, store scalar in z for now.
                        z := scalar
                    }
                    default {
                        // If n is odd, store x in z for now.
                        z := x
                    }
                    // Shifting right by 1 is like dividing by 2.
                    let half := shr(1, scalar)
                    for {
                        // Shift n right by 1 before looping to halve it.
                        n := shr(1, n)
                    } n {
                        // Shift n right by 1 each iteration to halve it.
                        n := shr(1, n)
                    } {
                        // Revert immediately if x ** 2 would overflow.
                        // Equivalent to iszero(eq(div(xx, x), x)) here.
                        if shr(128, x) {
                            revert(0, 0)
                        }
                        // Store x squared.
                        let xx := mul(x, x)
                        // Round to the nearest number.
                        let xxRound := add(xx, half)
                        // Revert if xx + half overflowed.
                        if lt(xxRound, xx) {
                            revert(0, 0)
                        }
                        // Set x to scaled xxRound.
                        x := div(xxRound, scalar)
                        // If n is even:
                        if mod(n, 2) {
                            // Compute z * x.
                            let zx := mul(z, x)
                            // If z * x overflowed:
                            if iszero(eq(div(zx, x), z)) {
                                // Revert if x is non-zero.
                                if iszero(iszero(x)) {
                                    revert(0, 0)
                                }
                            }
                            // Round to the nearest number.
                            let zxRound := add(zx, half)
                            // Revert if zx + half overflowed.
                            if lt(zxRound, zx) {
                                revert(0, 0)
                            }
                            // Return properly scaled zxRound.
                            z := div(zxRound, scalar)
                        }
                    }
                }
            }
        }
        /*//////////////////////////////////////////////////////////////
                            GENERAL NUMBER UTILITIES
        //////////////////////////////////////////////////////////////*/
        function sqrt(uint256 x) internal pure returns (uint256 z) {
            /// @solidity memory-safe-assembly
            assembly {
                let y := x // We start y at x, which will help us make our initial estimate.
                z := 181 // The "correct" value is 1, but this saves a multiplication later.
                // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
                // We check y >= 2^(k + 8) but shift right by k bits
                // each branch to ensure that if x >= 256, then y >= 256.
                if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                    y := shr(128, y)
                    z := shl(64, z)
                }
                if iszero(lt(y, 0x1000000000000000000)) {
                    y := shr(64, y)
                    z := shl(32, z)
                }
                if iszero(lt(y, 0x10000000000)) {
                    y := shr(32, y)
                    z := shl(16, z)
                }
                if iszero(lt(y, 0x1000000)) {
                    y := shr(16, y)
                    z := shl(8, z)
                }
                // Goal was to get z*z*y within a small factor of x. More iterations could
                // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                // That's not possible if x < 256 but we can just verify those cases exhaustively.
                // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
                // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
                // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
                // There is no overflow risk here since y < 2^136 after the first branch above.
                z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
                // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                z := shr(1, add(z, div(x, z)))
                // If x+1 is a perfect square, the Babylonian method cycles between
                // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                z := sub(z, lt(div(x, z), z))
            }
        }
        function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
            /// @solidity memory-safe-assembly
            assembly {
                // Mod x by y. Note this will return
                // 0 instead of reverting if y is zero.
                z := mod(x, y)
            }
        }
        function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
            /// @solidity memory-safe-assembly
            assembly {
                // Divide x by y. Note this will return
                // 0 instead of reverting if y is zero.
                r := div(x, y)
            }
        }
        function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
            /// @solidity memory-safe-assembly
            assembly {
                // Add 1 to x * y if x % y > 0. Note this will
                // return 0 instead of reverting if y is zero.
                z := add(gt(mod(x, y), 0), div(x, y))
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    /// @notice Gas optimized merkle proof verification library.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
    library MerkleProofLib {
        function verify(
            bytes32[] calldata proof,
            bytes32 root,
            bytes32 leaf
        ) internal pure returns (bool isValid) {
            /// @solidity memory-safe-assembly
            assembly {
                if proof.length {
                    // Left shifting by 5 is like multiplying by 32.
                    let end := add(proof.offset, shl(5, proof.length))
                    // Initialize offset to the offset of the proof in calldata.
                    let offset := proof.offset
                    // Iterate over proof elements to compute root hash.
                    // prettier-ignore
                    for {} 1 {} {
                        // Slot where the leaf should be put in scratch space. If
                        // leaf > calldataload(offset): slot 32, otherwise: slot 0.
                        let leafSlot := shl(5, gt(leaf, calldataload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // The xor puts calldataload(offset) in whichever slot leaf
                        // is not occupying, so 0 if leafSlot is 32, and 32 otherwise.
                        mstore(leafSlot, leaf)
                        mstore(xor(leafSlot, 32), calldataload(offset))
                        // Reuse leaf to store the hash to reduce stack operations.
                        leaf := keccak256(0, 64) // Hash both slots of scratch space.
                        offset := add(offset, 32) // Shift 1 word per cycle.
                        // prettier-ignore
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root) // The proof is valid if the roots match.
            }
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Gas optimized reentrancy protection for smart contracts.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
    /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
    abstract contract ReentrancyGuard {
        uint256 private locked = 1;
        modifier nonReentrant() virtual {
            require(locked == 1, "REENTRANCY");
            locked = 2;
            _;
            locked = 1;
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    import {ERC20} from "../tokens/ERC20.sol";
    /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
    /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
    /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
    library SafeTransferLib {
        /*//////////////////////////////////////////////////////////////
                                 ETH OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferETH(address to, uint256 amount) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Transfer the ETH and store if it succeeded or not.
                success := call(gas(), to, amount, 0, 0, 0, 0)
            }
            require(success, "ETH_TRANSFER_FAILED");
        }
        /*//////////////////////////////////////////////////////////////
                                ERC20 OPERATIONS
        //////////////////////////////////////////////////////////////*/
        function safeTransferFrom(
            ERC20 token,
            address from,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
                mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
                mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
                )
            }
            require(success, "TRANSFER_FROM_FAILED");
        }
        function safeTransfer(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                )
            }
            require(success, "TRANSFER_FAILED");
        }
        function safeApprove(
            ERC20 token,
            address to,
            uint256 amount
        ) internal {
            bool success;
            /// @solidity memory-safe-assembly
            assembly {
                // Get a pointer to some free memory.
                let freeMemoryPointer := mload(0x40)
                // Write the abi-encoded calldata into memory, beginning with the function selector.
                mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
                mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
                mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
                success := and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
                )
            }
            require(success, "APPROVE_FAILED");
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    import {ERC1155, ERC1155TokenReceiver} from "solmate/tokens/ERC1155.sol";
    import "solmate/tokens/ERC20.sol";
    import "solmate/utils/SafeTransferLib.sol";
    import "solmate/utils/FixedPointMathLib.sol";
    import "solmate/utils/ReentrancyGuard.sol";
    import "@chainlink/interfaces/VRFCoordinatorV2Interface.sol";
    import "@chainlink/VRFConsumerBaseV2.sol";
    import {Gatekeeper} from "./Gatekeeper.sol";
    import {IHoneyComb} from "./IHoneyComb.sol";
    import {IBearCave} from "./IBearCave.sol";
    import {GameRegistryConsumer} from "./GameRegistry.sol";
    import {Constants} from "./GameLib.sol";
    import "forge-std/console2.sol";
    // Example: https://opensea.io/0xd87fa9FeD90948cd7deA9f77c06b9168Ac07F407 :dafoe:
    contract BearCave is IBearCave, VRFConsumerBaseV2, ERC1155TokenReceiver, ReentrancyGuard, GameRegistryConsumer {
        using SafeTransferLib for ERC20;
        using FixedPointMathLib for uint256;
        /**
         *  Game Errors
         */
        // Contract State
        error NotInitialized();
        error AlreadyInitialized();
        // Game state
        error BearAlreadyWoke(uint256 bearId);
        error GameInProgress();
        error AlreadyTooManyHoneyCombs(uint256 bearId);
        error SpecialHoneyCombNotFound(uint256 bearId);
        error NotEnoughHoneyCombMinted(uint256 bearId);
        error GeneralMintNotOpen(uint256 bearId);
        // User Errors
        error NotOwnerOfSpecialHoneyComb(uint256 bearId, uint256 honeycombId);
        error Claim_IncorrectInput();
        error Claim_InvalidProof();
        error MekingTooManyHoneyCombs(uint256 bearId);
        /**
         * Events
         */
        event Initialized(MintConfig mintConfig);
        event BearHibernated(uint256 tokenId);
        event SpecialHoneyCombFound(uint256 tokenId, uint256 honeyCombId);
        event MintConfigChanged(MintConfig mintConfig);
        event HoneycombClaimed(uint256 tokenId, address player, uint256 amount);
        event BearAwoke(uint256 tokenId, address player);
        /**
         * Configuration
         */
        ERC20 public paymentToken; // OHM
        ERC1155 public erc1155; //the openseaAddress (rip) for Bears
        MintConfig public mintConfig;
        bool public distributeWithMint; // Feature Toggle... what if we just make a feature toggle lib...
        uint256 public publicMintingTime;
        /**
         * Chainlink VRF Config
         */
        // The gas lane to use, which specifies the maximum gas price to bump to.
        // For a list of available gas lanes on each network,
        // see https://docs.chain.link/docs/vrf-contracts/#configurations
        bytes32 internal keyHash;
        uint64 internal subId; // https://vrf.chain.link/goerli/new
        uint16 internal minConfirmations = 3; // Default is 3
        // Storing each word costs about 20,000 gas,
        // so 100,000 is a safe default for this example contract.
        uint32 internal callbackGasLimit = 100000; // enough for ~5 words
        /**
         * bearPouch
         */
        address payable private beekeeper; // rev share 22.33%
        address payable private jani;
        uint256 public honeyCombShare; // as a WAD
        // Accounting vars
        uint256 public totalERC20Fees;
        uint256 public totalETHfees;
        /**
         * Depenedncies
         */
        Gatekeeper public gatekeeper;
        IHoneyComb public honeycomb;
        VRFCoordinatorV2Interface internal vrfCoordinator;
        /**
         * Internal Storage
         */
        bool public initialized;
        // TODO: Review usage & combine these mappings into a single struct where appropriate.
        mapping(uint256 => HibernatingBear) public bears; //  bearId --> hibernatingBear status
        mapping(uint256 => uint256[]) public honeyJar; //  bearId --> honeycomb that was made for it (honeyJar[bearId].length is # minted honeycomb)
        mapping(uint256 => uint256) public honeycombToBear; // Reverse mapping: honeyId -> bearId
        mapping(uint256 => uint32) public claimed; // bearid -> numClaimed
        mapping(uint256 => uint256) public rng; // Chainlink VRF request ID => bearId
        constructor(
            address _vrfCoordinator,
            address _gameRegistry,
            address _honeycombAddress,
            address _erc1155Address,
            address _paymentToken,
            address _gatekeeper,
            uint256 _honeyCombShare
        ) VRFConsumerBaseV2(_vrfCoordinator) GameRegistryConsumer(_gameRegistry) ReentrancyGuard() {
            vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
            honeycomb = IHoneyComb(_honeycombAddress);
            erc1155 = ERC1155(_erc1155Address);
            paymentToken = ERC20(_paymentToken);
            gatekeeper = Gatekeeper(_gatekeeper);
            honeyCombShare = _honeyCombShare;
            distributeWithMint = true;
        }
        function initialize(
            bytes32 keyhash_,
            uint64 subId_,
            MintConfig calldata mintConfig_
        ) external onlyRole(Constants.GAME_ADMIN) {
            if (initialized) revert AlreadyInitialized();
            initialized = true;
            keyHash = keyhash_;
            subId = subId_;
            mintConfig = mintConfig_;
            emit Initialized(mintConfig);
        }
        /// @notice you miss your bear so you want it
        function getBear(uint256 _bearId) external view returns (HibernatingBear memory) {
            return bears[_bearId];
        }
        /// @inheritdoc IBearCave
        function hibernateBear(uint256 _bearId) external onlyRole(Constants.GAME_ADMIN) {
            // This is shitty, because theres only one permissions thing.
            require(erc1155.isApprovedForAll(msg.sender, address(this)), "Gibb cave to permissions to hibernate your bear");
            erc1155.safeTransferFrom(msg.sender, address(this), _bearId, 1, "");
            bears[_bearId] = HibernatingBear(_bearId, 0, block.timestamp + 72 hours, false, false);
            gatekeeper.startGatesForToken(_bearId);
            emit BearHibernated(_bearId);
        }
        function _canMintHoneycomb(uint256 bearId_, uint256 amount_) internal view {
            if (!initialized) revert NotInitialized();
            HibernatingBear memory bear = bears[bearId_];
            require(bear.id == bearId_, "Da bear isn't hibernating");
            if (bear.isAwake) revert BearAlreadyWoke(bearId_);
            if (honeyJar[bearId_].length > mintConfig.maxHoneycomb) revert AlreadyTooManyHoneyCombs(bearId_);
            if (honeyJar[bearId_].length + amount_ > mintConfig.maxHoneycomb) revert MekingTooManyHoneyCombs(bearId_);
            require(amount_ > 0, "why you tryna mint nothing");
        }
        function earlyMekHoneyCombWithERC20(
            uint256 bearId,
            uint32 gateId,
            uint32 proofAmount,
            bytes32[] calldata proof,
            uint256 mintAmount
        ) external returns (uint256) {
            require(mintAmount > 0, "why you tryna mint nothing");
            _canMintHoneycomb(bearId, mintAmount);
            // validateProof checks that gates are open
            bool validProof = gatekeeper.validateProof(bearId, gateId, msg.sender, proofAmount, proof);
            if (!validProof) revert Claim_InvalidProof();
            return _distributeERC20AndMintHoneycomb(bearId, mintAmount);
        }
        function earlyMekHoneyCombWithEth(
            uint256 bearId,
            uint32 gateId,
            uint32 proofAmount,
            bytes32[] calldata proof,
            uint256 mintAmount
        ) external payable returns (uint256) {
            _canMintHoneycomb(bearId, mintAmount);
            // validateProof checks that gates are open
            bool validProof = gatekeeper.validateProof(bearId, gateId, msg.sender, proofAmount, proof); // This shit needs to be bulletproof
            if (!validProof) revert Claim_InvalidProof();
            return _distributeETHAndMintHoneycomb(bearId, mintAmount);
        }
        /// @inheritdoc IBearCave
        function mekHoneyCombWithERC20(uint256 bearId_, uint256 amount_) external returns (uint256) {
            _canMintHoneycomb(bearId_, amount_);
            if (bears[bearId_].publicMintTime > block.timestamp) revert GeneralMintNotOpen(bearId_);
            return _distributeERC20AndMintHoneycomb(bearId_, amount_);
        }
        function mekHoneyCombWithEth(uint256 bearId_, uint256 amount_) external payable returns (uint256) {
            _canMintHoneycomb(bearId_, amount_);
            if (bears[bearId_].publicMintTime > block.timestamp) revert GeneralMintNotOpen(bearId_);
            return _distributeETHAndMintHoneycomb(bearId_, amount_);
        }
        /// @dev internal helper function to collect payment and mint honeycomb
        /// @return tokenID of minted honeyComb
        function _distributeERC20AndMintHoneycomb(uint256 bearId_, uint256 amount_) internal returns (uint256) {
            uint256 price = mintConfig.honeycombPrice_ERC20;
            if (distributeWithMint) {
                _distribute(price * amount_);
            } else {
                paymentToken.safeTransferFrom(msg.sender, address(this), price * amount_); // will revert if there isn't enough
                totalERC20Fees += price;
            }
            // Mint da honey
            return _mintHoneyCombForBear(msg.sender, bearId_, amount_);
        }
        /// @dev internal helper function to collect payment and mint honeycomb
        /// @return tokenID of minted honeyComb
        function _distributeETHAndMintHoneycomb(uint256 bearId_, uint256 amount_) internal returns (uint256) {
            uint256 price = mintConfig.honeycombPrice_ETH;
            require(msg.value == price * amount_, "MekHoney::Exact eth pls");
            if (distributeWithMint) {
                _distribute(0);
            } else {
                totalETHfees += price * amount_;
            }
            return _mintHoneyCombForBear(msg.sender, bearId_, amount_);
        }
        /// @notice internal method to mint for a particular user
        /// @param to user to mint to
        /// @param _bearId the bea being minted for
        function _mintHoneyCombForBear(
            address to,
            uint256 _bearId,
            uint256 amount_
        ) internal returns (uint256) {
            uint256 tokenId = honeycomb.nextTokenId();
            honeycomb.batchMint(to, amount_);
            // Have a unique tokenId for a given bearId
            for (uint256 i = 0; i < amount_; ++i) {
                honeyJar[_bearId].push(tokenId);
                honeycombToBear[tokenId] = _bearId;
                ++tokenId;
            }
            // Find the special honeycomb when the last honeyComb is minted.
            if (honeyJar[_bearId].length >= mintConfig.maxHoneycomb) {
                _findHoneyComb(_bearId);
            }
            return tokenId - 1; // returns the lastID created
        }
        /// @notice this function _should_ only be called in case of emergencies
        /// @notice if the honeycombs are minted but the VRF called failed.
        /// @dev kicks off another VRF request
        function forceHoneycombSearch(uint256 bearId_) external onlyRole(Constants.GAME_ADMIN) {
            if (honeyJar[bearId_].length < mintConfig.maxHoneycomb) revert NotEnoughHoneyCombMinted(bearId_);
            _findHoneyComb(bearId_);
        }
        /// @notice Forcing function to find a bear.
        /// @notice Should only be called when the last honeyCombs is minted.
        function _findHoneyComb(uint256 bearId_) internal {
            uint256 requestId = vrfCoordinator.requestRandomWords(keyHash, subId, minConfirmations, callbackGasLimit, 2);
            rng[requestId] = bearId_;
        }
        /// @notice the callback method that is called when VRF completes
        /// @param requestId requestId that is generated when initiaully calling VRF
        /// @param randomness an array of random numbers based on `numWords` config
        function fulfillRandomWords(uint256 requestId, uint256[] memory randomness) internal override {
            /// use requestID to get bearId
            uint256 bearId = rng[requestId];
            _setSpecialHoneyComb(bearId, randomness[0]);
        }
        /// @notice helper function to set a random honeycomb as a winner
        /// @param bearId self-explanatpry
        /// @param randomNumber used to determine the index of the winnign number
        function _setSpecialHoneyComb(uint256 bearId, uint256 randomNumber) internal {
            uint256 numHoneyCombs = honeyJar[bearId].length;
            uint256 specialHoneyIndex = randomNumber % numHoneyCombs;
            uint256 specialHoneyCombId = honeyJar[bearId][specialHoneyIndex];
            HibernatingBear storage bear = bears[bearId];
            bear.specialHoneycombFound = true;
            bear.specialHoneycombId = specialHoneyCombId;
            emit SpecialHoneyCombFound(bearId, specialHoneyCombId);
        }
        /// @inheritdoc IBearCave
        /// @dev erc1155.safeTransferFrom is requires a reciever.
        function wakeBear(uint256 _bearId) external {
            // Check that msg.sender has the special honeycomb to wake up bear
            HibernatingBear memory bear = bears[_bearId];
            if (bear.isAwake) revert BearAlreadyWoke(_bearId);
            if (honeyJar[_bearId].length < mintConfig.maxHoneycomb) revert NotEnoughHoneyCombMinted(_bearId);
            if (!bear.specialHoneycombFound) revert SpecialHoneyCombNotFound(_bearId);
            if (honeycomb.ownerOf(bear.specialHoneycombId) != msg.sender) {
                revert NotOwnerOfSpecialHoneyComb(_bearId, bear.specialHoneycombId);
            }
            // Send over bear
            erc1155.safeTransferFrom(address(this), msg.sender, bear.id, 1, "");
            emit BearAwoke(_bearId, msg.sender);
        }
        /**
         * BearPouch owner methods
         *      Can move into another contract for portability
         * depends on:
         *     Exclusive: beekeeper, jani, honeyCombShare
         *     shared: paymentToken
         */
        /// @dev requires that beekeeper and jani addresses are set.
        /// @param amountERC20 is zero if we're only distributing the ETH
        function _distribute(uint256 amountERC20) internal {
            uint256 beekeeperShareERC20 = amountERC20.mulWadUp(honeyCombShare);
            uint256 beekeeperShareETH = (msg.value).mulWadUp(honeyCombShare);
            if (beekeeperShareERC20 > 0) {
                paymentToken.safeTransferFrom(msg.sender, beekeeper, beekeeperShareERC20);
                paymentToken.safeTransferFrom(msg.sender, jani, amountERC20 - beekeeperShareERC20);
            }
            if (beekeeperShareETH > 0) {
                SafeTransferLib.safeTransferETH(beekeeper, beekeeperShareETH);
                SafeTransferLib.safeTransferETH(jani, msg.value - beekeeperShareETH);
            }
        }
        function _splitFee(uint256 currentBalance) internal view returns (uint256) {
            return currentBalance.mulWadUp(honeyCombShare);
        }
        /// @notice should only get called in the event automatic distribution doesn't work
        function withdrawERC20() external nonReentrant returns (uint256) {
            require(!distributeWithMint, "distriboot w/ mints should be false");
            // permissions check
            require(_hasRole(Constants.JANI) || _hasRole(Constants.BEEKEEPER), "oogabooga you can't do that");
            require(beekeeper != address(0), "withdrawFunds::beekeeper address not set");
            require(jani != address(0), "withdrawFunds::jani address not set");
            uint256 currBalance = paymentToken.balanceOf(address(this));
            require(currBalance > 0, "oogabooga theres nothing here");
            // xfer everything all at once so we don't have to worry about accounting
            uint256 beekeeperShare = _splitFee(currBalance);
            paymentToken.safeTransfer(beekeeper, beekeeperShare);
            paymentToken.safeTransfer(jani, paymentToken.balanceOf(address(this))); // This should be everything
            return paymentToken.balanceOf(address(this));
        }
        /// @notice should only get called in the event automatic distribution doesn't work
        function withdrawETH() public nonReentrant returns (uint256) {
            require(!distributeWithMint, "distriboot w/ mints should be false");
            require(_hasRole(Constants.JANI) || _hasRole(Constants.BEEKEEPER), "oogabooga you can't do that");
            require(beekeeper != address(0), "withdrawETH::beekeeper address not set");
            require(jani != address(0), "withdrawFunds::jani address not set");
            uint256 beekeeperShare = _splitFee(address(this).balance);
            SafeTransferLib.safeTransferETH(beekeeper, beekeeperShare);
            SafeTransferLib.safeTransferETH(jani, address(this).balance);
            return address(this).balance;
        }
        /**
         * Gatekeeper: for claiming free honeycomb
         * BearCave:
         *    - maxMintableHoneyComb per Bear
         *    - claimedHoneyComb per Bear // free
         *    - maxClaimableHoneyComb per Bear
         * Gatekeeper: (per bear)
         * Gates:
         *    - maxHoneycombAvailable per gate
         *    - maxClaimable per gate
         * x
         */
        function claim(
            uint256 bearId_,
            uint32 gateId,
            uint32 amount,
            bytes32[] calldata proof
        ) public {
            // Gatekeeper tracks per-player/per-gate claims
            if (proof.length == 0) revert Claim_InvalidProof();
            uint32 numClaim = gatekeeper.claim(bearId_, gateId, msg.sender, amount, proof);
            if (numClaim == 0) {
                return;
            }
            // Track per bear freeClaims
            uint32 claimedAmount = claimed[bearId_];
            if (numClaim + claimedAmount > mintConfig.maxClaimableHoneycomb) {
                numClaim = mintConfig.maxClaimableHoneycomb - claimedAmount;
            }
            _canMintHoneycomb(bearId_, numClaim); // Validating here because numClaims can change
            // If for some reason this fails, GG no honeyComb for you
            _mintHoneyCombForBear(msg.sender, bearId_, numClaim);
            claimed[bearId_] += numClaim;
            // Can be combined with "claim" call above, but keeping separate to separate view + modification on gatekeeper
            gatekeeper.addClaimed(bearId_, gateId, numClaim, proof);
            emit HoneycombClaimed(bearId_, msg.sender, amount);
        }
        // Helpfer function to claim all the free shit
        function claimAll(
            uint256 bearId_,
            uint32[] calldata gateId,
            uint32[] calldata amount,
            bytes32[][] calldata proof
        ) external {
            uint256 inputLength = proof.length;
            if (inputLength != gateId.length) revert Claim_IncorrectInput();
            if (inputLength != amount.length) revert Claim_IncorrectInput();
            for (uint256 i = 0; i < inputLength; ++i) {
                if (proof[i].length == 0) continue; // Don't nomad yourself
                claim(bearId_, gateId[i], amount[i], proof[i]);
            }
        }
        //=============== SETTERS ================//
        /**
         * Bear Pouch setters (needed for distribution)
         *  Currently separate from the permissioned roles in gameRegistry
         */
        function setJani(address jani_) external onlyRole(Constants.GAME_ADMIN) {
            jani = payable(jani_);
        }
        function setBeeKeeper(address beekeeper_) external onlyRole(Constants.GAME_ADMIN) {
            beekeeper = payable(beekeeper_);
        }
        function setDistributeWithMint(bool distributeWithMint_) external onlyRole(Constants.GAME_ADMIN) {
            distributeWithMint = distributeWithMint_;
        }
        /**
         * Game setters
         *  These should not be called while a game is in progress to prevent hostage holding.
         */
        /// @notice Sets the max number NFTs (honeyComb) that can be generated from the deposit of a bear (asset)
        function setMaxHoneycomb(uint32 _maxHoneycomb) external onlyRole(Constants.GAME_ADMIN) {
            if (_isEnabled(address(this))) revert GameInProgress();
            mintConfig.maxHoneycomb = _maxHoneycomb;
            emit MintConfigChanged(mintConfig);
        }
        /// @notice sets the price of the honeycomb in `paymentToken`
        function setHoneyCombPrice_ERC20(uint256 _honeyCombPrice) external onlyRole(Constants.GAME_ADMIN) {
            if (_isEnabled(address(this))) revert GameInProgress();
            mintConfig.honeycombPrice_ERC20 = _honeyCombPrice;
            emit MintConfigChanged(mintConfig);
        }
        /// @notice sets the price of the honeycomb in `ETH`
        function setHoneyCombPrice_ETH(uint256 _honeyCombPrice) external onlyRole(Constants.GAME_ADMIN) {
            if (_isEnabled(address(this))) revert GameInProgress();
            mintConfig.honeycombPrice_ETH = _honeyCombPrice;
            emit MintConfigChanged(mintConfig);
        }
        /**
         * Chainlink Setters
         * @notice modifiable after initialization isn't a security risk. Just for VRF config.
         */
        function setSubId(uint64 subId_) external onlyRole(Constants.GAME_ADMIN) {
            subId = subId_;
        }
        function setKeyHash(bytes32 keyHash_) external onlyRole(Constants.GAME_ADMIN) {
            keyHash = keyHash_;
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    library Constants {
        // External permissions
        bytes32 public constant GAME_ADMIN = "GAME_ADMIN";
        bytes32 public constant BEEKEEPER = "BEEKEEPER";
        bytes32 public constant JANI = "JANI";
        // Contract instances
        bytes32 public constant GAME_INSTANCE = "GAME_INSTANCE";
        bytes32 public constant BEAR_POUCH = "BEAR_POUCH";
        bytes32 public constant GATEKEEPER = "GATEKEEPER";
        bytes32 public constant GATE = "GATE";
        // Special honeycomb permissions
        bytes32 public constant MINTER = "MINTER";
        bytes32 public constant BURNER = "BURNER";
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import {Constants} from "./GameLib.sol";
    /// @title GameRegistry
    /// @notice Central repository that tracks games and permissions.
    /// @dev All game contracts should use extend `GameRegistryConsumer` to have consistent permissioning
    contract GameRegistry is AccessControl {
        struct Game {
            bool enabled;
        }
        uint256[] public stageTimes;
        constructor() {
            _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
            _setupRole(Constants.GAME_ADMIN, msg.sender);
            // Initial 4 stages
            stageTimes.push(0 hours);
            stageTimes.push(24 hours);
            stageTimes.push(48 hours);
            stageTimes.push(72 hours);
        }
        mapping(address => Game) public games;
        function registerGame(address game_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.GAME_INSTANCE, game_);
            games[game_] = Game(true);
        }
        function startGame(address game_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.MINTER, game_);
        }
        function stopGame(address game_) external onlyRole(Constants.GAME_ADMIN) {
            _revokeRole(Constants.MINTER, game_);
            games[game_].enabled = false;
        }
        /**
         * Gettors
         */
        function getStageTimes() external view returns (uint256[] memory) {
            return stageTimes;
        }
        /**
         * Bear Pouch setters (helper functions)
         * Can check roles directly since this is an access control
         */
        function setJani(address jani_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.JANI, jani_);
        }
        function setBeekeeper(address beeKeeper_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.JANI, beeKeeper_);
        }
        function setStageTimes(uint24[] calldata _stageTimes) external onlyRole(Constants.GAME_ADMIN) {
            stageTimes = _stageTimes;
        }
    }
    abstract contract GameRegistryConsumer {
        GameRegistry public gameRegistry;
        error GameRegistry_NoPermissions(string role, address user);
        error GameRegistry_StageOutOfBounds(uint8 index);
        modifier onlyRole(bytes32 role_) {
            if (!gameRegistry.hasRole(role_, msg.sender)) {
                revert GameRegistry_NoPermissions(string(abi.encodePacked(role_)), msg.sender);
            }
            _;
        }
        constructor(address gameRegistry_) {
            gameRegistry = GameRegistry(gameRegistry_);
        }
        function _isEnabled(address game_) internal view returns (bool enabled) {
            enabled = gameRegistry.games(game_);
        }
        /// @dev the last stageTime is generalMint
        function _getStages() internal view returns (uint256[] memory) {
            return gameRegistry.getStageTimes();
        }
        /// @dev just a helper function. For access to all stages you should use _getStages()
        function _getStage(uint8 stageIndex) internal view returns (uint256) {
            uint256[] memory stageTimes = gameRegistry.getStageTimes();
            if (stageIndex >= stageTimes.length) revert GameRegistry_StageOutOfBounds(stageIndex);
            return stageTimes[stageIndex];
        }
        function _hasRole(bytes32 role_) internal view returns (bool) {
            return gameRegistry.hasRole(role_, msg.sender);
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    import {ERC1155} from "solmate/tokens/ERC1155.sol";
    import {ERC20} from "solmate/tokens/ERC20.sol";
    import {ERC721} from "solmate/tokens/ERC721.sol";
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "solmate/utils/MerkleProofLib.sol";
    import {GameRegistryConsumer} from "./GameRegistry.sol";
    import {Constants} from "./GameLib.sol";
    /**
     * Bear GateKeeper
     *      In order to remain gas-efficient gates will be calculated off-chain
     *      BearGate: owning bears
     *      CrownGate: every single one of the digital collectible articles, then they get a free claim in every game
     *      HoneyGate: Genesis mint & n-1 can mint
     *      FrenGate: owning particular assets
     *      PartnerGate: being on a traditional allowlist
     *      Since gates are merkle trees, the per-player amounts will be set off-chain in the root.
     *  @notice state needs to be reset after each game.
     *  @notice tracks claims per player, and claims per gate.
     */
    contract Gatekeeper is GameRegistryConsumer {
        struct Gate {
            bool enabled;
            uint8 stageIndex; // stage from [0-3]
            uint32 claimedCount; // # of claims already happend
            uint32 maxClaimable; // # of claims per gate
            bytes32 gateRoot;
            uint256 activeAt; // timestamp when active.
        }
        /**
         * Events when business logic is affects
         */
        event GateAdded(uint256 tokenId, uint256 gateId);
        event GateSetEnabled(uint256 tokenId, uint256 gateId, bool enabled);
        event GateActivated(uint256 tokenId, uint256 gateId, uint256 activationTime);
        event GetSetMaxClaimable(uint256 tokenId, uint256 gateId, uint256 maxClaimable);
        event GateReset(uint256 tokenId, uint256 index);
        /**
         * Internal Storage
         */
        mapping(uint256 => Gate[]) public tokenToGates; // bear -> Gates[]
        mapping(uint256 => mapping(bytes32 => bool)) public consumedProofs; // gateId --> proof --> boolean
        mapping(uint256 => bytes32[]) public consumedProofsList; // gateId
        mapping(uint256 => address) public games; // bear --> gameContract;
        /**
         * Dependencies
         */
        /// @notice admin is the address that is set as the owner.
        constructor(address gameRegistry_) GameRegistryConsumer(gameRegistry_) {}
        /// @notice validate how much you can claim for a particular token and gate. (not a real claim)
        /// @param tokenId the ID of the bear in the game.
        /// @param index the gate index we're claiming
        /// @param amount number between 0-maxClaimable you a player wants to claim
        /// @param proof merkle proof
        function claim(uint256 tokenId, uint256 index, address player, uint32 amount, bytes32[] calldata proof)
            external
            view
            returns (uint32 claimAmount)
        {
            // If proof was already used within the gate, there are 0 left to claim
            bytes32 proofHash = keccak256(abi.encode(proof));
            if (consumedProofs[index][proofHash]) return 0;
            Gate storage gate = tokenToGates[tokenId][index];
            uint32 claimedCount = gate.claimedCount;
            require(claimedCount < gate.maxClaimable, "Too much honeycomb went through this gate");
            claimAmount = amount;
            bool validProof = validateProof(tokenId, index, player, amount, proof);
            require(validProof, "Not a valid proof bro");
            if (amount + claimedCount > gate.maxClaimable) {
                claimAmount = gate.maxClaimable - claimedCount;
            }
        }
        /// @notice Validates proof
        /// @dev relies on gates being enabled
        function validateProof(uint256 tokenId, uint256 index, address player, uint32 amount, bytes32[] calldata proof)
            public
            view
            returns (bool validProof)
        {
            Gate[] storage gates = tokenToGates[tokenId];
            require(gates.length > 0, "nogates fren");
            require(index < gates.length, "Index too big bro");
            require(proof.length > 0, "Invalid Proof");
            Gate storage gate = gates[index];
            require(gate.enabled, "gates closed bruh");
            require(gate.activeAt <= block.timestamp, "gate isn't active");
            bytes32 leaf = keccak256(abi.encodePacked(player, amount));
            validProof = MerkleProofLib.verify(proof, gate.gateRoot, leaf);
        }
        /**
         * Setters
         */
        /// @notice  update accounting
        /// @dev should only be called by a game
        function addClaimed(uint256 tokenId, uint256 gateId, uint32 numClaimed, bytes32[] calldata proof)
            external
            onlyRole(Constants.GAME_INSTANCE)
        {
            Gate storage gate = tokenToGates[tokenId][gateId];
            gate.claimedCount += numClaimed;
            bytes32 proofHash = keccak256(abi.encode(proof));
            consumedProofs[gateId][proofHash] = true;
            consumedProofsList[gateId].push(proofHash);
        }
        /**
         * Gate admin methods
         */
        function addGate(uint256 tokenId, bytes32 root_, uint32 maxClaimable_, uint8 stageIndex_)
            external
            onlyRole(Constants.GAME_ADMIN)
        {
            // claimedCount = activeAt = 0
            require(_getStages().length > stageIndex_, "addGate: stageIndex_ is out of bounds");
            tokenToGates[tokenId].push(Gate(false, stageIndex_, 0, maxClaimable_, root_, 0));
            emit GateAdded(tokenId, tokenToGates[tokenId].length - 1);
        }
        function startGatesForToken(uint256 tokenId) external onlyRole(Constants.GAME_INSTANCE) {
            Gate[] storage gates = tokenToGates[tokenId];
            uint256[] memory stageTimes = _getStages(); // External Call
            uint256 numGates = gates.length;
            for (uint256 i = 0; i < numGates; i++) {
                gates[i].enabled = true;
                gates[i].activeAt = block.timestamp + stageTimes[gates[i].stageIndex];
                emit GateActivated(tokenId, i, gates[i].activeAt);
            }
        }
        /// @notice Only to be used for emergency gate shutdown.
        function setGateEnabled(uint256 tokenId, uint256 index, bool enabled) external onlyRole(Constants.GAME_ADMIN) {
            tokenToGates[tokenId][index].enabled = enabled;
            emit GateSetEnabled(tokenId, index, enabled);
        }
        function setGateMaxClaimable(uint256 tokenId, uint256 index, uint32 maxClaimable_)
            external
            onlyRole(Constants.GAME_ADMIN)
        {
            tokenToGates[tokenId][index].maxClaimable = maxClaimable_;
            emit GetSetMaxClaimable(tokenId, index, maxClaimable_);
        }
        function resetGate(uint256 tokenId, uint256 index) external onlyRole(Constants.GAME_ADMIN) {
            tokenToGates[tokenId][index].claimedCount = 0;
            emit GateReset(tokenId, index);
        }
        function resetAllGates(uint256 tokenId) external onlyRole(Constants.GAME_ADMIN) {
            uint256 numGates = tokenToGates[tokenId].length;
            Gate[] storage tokenGates = tokenToGates[tokenId];
            uint256 numProofs;
            // Currently a hacky way but need to clear out if the proofs were used.
            for (uint256 i = 0; i < numGates; i++) {
                tokenGates[i].claimedCount = 0;
                numProofs = consumedProofsList[i].length;
                for (uint256 j = 0; j < numProofs; ++j) {
                    // Step through all proofs from a particular gate.
                    consumedProofs[i][consumedProofsList[i][j]] = false;
                }
            }
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    /// @title BearCave: Only one true honey can make a bear wake up
    interface IBearCave {
        struct HibernatingBear {
            uint256 id;
            uint256 specialHoneycombId; // defaults to 0
            uint256 publicMintTime; // block.timstamp that general public can start making honeycombs
            bool specialHoneycombFound; // So tokenID=0 can't wake bear before special honey is found
            bool isAwake; // don't try to wake if its already awake
        }
        struct MintConfig {
            uint32 maxHoneycomb; // Max # of generated honeys (Max of 4.2m -- we'll have 10420)
            uint32 maxClaimableHoneycomb; // # of honeycombs that can be claimed (total)
            uint256 honeycombPrice_ERC20;
            uint256 honeycombPrice_ETH;
        }
        /// @notice Puts the bear into the cave to mek it sleep
        /// @dev Should be permissioned to be onlyOwner
        /// @param _bearId ID of the bear to mek sleep
        function hibernateBear(uint256 _bearId) external;
        /// @notice Meks honey for `_bearID` that could wake it up. Will revert if user does not have the funds.
        /// @param _bearId ID of the bear the honey will wake up
        function mekHoneyCombWithERC20(uint256 _bearId, uint256 amount) external returns (uint256); // Makes honey for the bear
        /// @notice Same as `mekHoneyCombWithERC20` however this function accepts ETH payments
        function mekHoneyCombWithEth(uint256 _bearId, uint256 amount) external payable returns (uint256);
        /// @notice Takes special honey to wake up the bear
        /// @param _bearId ID of the bear to wake up
        function wakeBear(uint256 _bearId) external;
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    import "ERC721A/extensions/IERC721AQueryable.sol";
    interface IHoneyComb is IERC721AQueryable {
        function mint(address to) external returns (uint256);
        function batchMint(address to, uint256 amount) external;
        function burn(uint256 _id) external;
        function nextTokenId() external view returns (uint256);
    }
    

    File 2 of 2: Gatekeeper
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
    pragma solidity ^0.8.0;
    import "./IAccessControl.sol";
    import "../utils/Context.sol";
    import "../utils/Strings.sol";
    import "../utils/introspection/ERC165.sol";
    /**
     * @dev Contract module that allows children to implement role-based access
     * control mechanisms. This is a lightweight version that doesn't allow enumerating role
     * members except through off-chain means by accessing the contract event logs. Some
     * applications may benefit from on-chain enumerability, for those cases see
     * {AccessControlEnumerable}.
     *
     * Roles are referred to by their `bytes32` identifier. These should be exposed
     * in the external API and be unique. The best way to achieve this is by
     * using `public constant` hash digests:
     *
     * ```
     * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
     * ```
     *
     * Roles can be used to represent a set of permissions. To restrict access to a
     * function call, use {hasRole}:
     *
     * ```
     * function foo() public {
     *     require(hasRole(MY_ROLE, msg.sender));
     *     ...
     * }
     * ```
     *
     * Roles can be granted and revoked dynamically via the {grantRole} and
     * {revokeRole} functions. Each role has an associated admin role, and only
     * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
     *
     * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
     * that only accounts with this role will be able to grant or revoke other
     * roles. More complex role relationships can be created by using
     * {_setRoleAdmin}.
     *
     * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
     * grant and revoke this role. Extra precautions should be taken to secure
     * accounts that have been granted it.
     */
    abstract contract AccessControl is Context, IAccessControl, ERC165 {
        struct RoleData {
            mapping(address => bool) members;
            bytes32 adminRole;
        }
        mapping(bytes32 => RoleData) private _roles;
        bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
        /**
         * @dev Modifier that checks that an account has a specific role. Reverts
         * with a standardized message including the required role.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         *
         * _Available since v4.1._
         */
        modifier onlyRole(bytes32 role) {
            _checkRole(role);
            _;
        }
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
        }
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
            return _roles[role].members[account];
        }
        /**
         * @dev Revert with a standard message if `_msgSender()` is missing `role`.
         * Overriding this function changes the behavior of the {onlyRole} modifier.
         *
         * Format of the revert message is described in {_checkRole}.
         *
         * _Available since v4.6._
         */
        function _checkRole(bytes32 role) internal view virtual {
            _checkRole(role, _msgSender());
        }
        /**
         * @dev Revert with a standard message if `account` is missing `role`.
         *
         * The format of the revert reason is given by the following regular expression:
         *
         *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
         */
        function _checkRole(bytes32 role, address account) internal view virtual {
            if (!hasRole(role, account)) {
                revert(
                    string(
                        abi.encodePacked(
                            "AccessControl: account ",
                            Strings.toHexString(account),
                            " is missing role ",
                            Strings.toHexString(uint256(role), 32)
                        )
                    )
                );
            }
        }
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
            return _roles[role].adminRole;
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleGranted} event.
         */
        function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _grantRole(role, account);
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         *
         * May emit a {RoleRevoked} event.
         */
        function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
            _revokeRole(role, account);
        }
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been revoked `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         *
         * May emit a {RoleRevoked} event.
         */
        function renounceRole(bytes32 role, address account) public virtual override {
            require(account == _msgSender(), "AccessControl: can only renounce roles for self");
            _revokeRole(role, account);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event. Note that unlike {grantRole}, this function doesn't perform any
         * checks on the calling account.
         *
         * May emit a {RoleGranted} event.
         *
         * [WARNING]
         * ====
         * This function should only be called from the constructor when setting
         * up the initial roles for the system.
         *
         * Using this function in any other way is effectively circumventing the admin
         * system imposed by {AccessControl}.
         * ====
         *
         * NOTE: This function is deprecated in favor of {_grantRole}.
         */
        function _setupRole(bytes32 role, address account) internal virtual {
            _grantRole(role, account);
        }
        /**
         * @dev Sets `adminRole` as ``role``'s admin role.
         *
         * Emits a {RoleAdminChanged} event.
         */
        function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
            bytes32 previousAdminRole = getRoleAdmin(role);
            _roles[role].adminRole = adminRole;
            emit RoleAdminChanged(role, previousAdminRole, adminRole);
        }
        /**
         * @dev Grants `role` to `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleGranted} event.
         */
        function _grantRole(bytes32 role, address account) internal virtual {
            if (!hasRole(role, account)) {
                _roles[role].members[account] = true;
                emit RoleGranted(role, account, _msgSender());
            }
        }
        /**
         * @dev Revokes `role` from `account`.
         *
         * Internal function without access restriction.
         *
         * May emit a {RoleRevoked} event.
         */
        function _revokeRole(bytes32 role, address account) internal virtual {
            if (hasRole(role, account)) {
                _roles[role].members[account] = false;
                emit RoleRevoked(role, account, _msgSender());
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev External interface of AccessControl declared to support ERC165 detection.
     */
    interface IAccessControl {
        /**
         * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
         *
         * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
         * {RoleAdminChanged} not being emitted signaling this.
         *
         * _Available since v3.1._
         */
        event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
        /**
         * @dev Emitted when `account` is granted `role`.
         *
         * `sender` is the account that originated the contract call, an admin role
         * bearer except when using {AccessControl-_setupRole}.
         */
        event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Emitted when `account` is revoked `role`.
         *
         * `sender` is the account that originated the contract call:
         *   - if using `revokeRole`, it is the admin role bearer
         *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
         */
        event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
        /**
         * @dev Returns `true` if `account` has been granted `role`.
         */
        function hasRole(bytes32 role, address account) external view returns (bool);
        /**
         * @dev Returns the admin role that controls `role`. See {grantRole} and
         * {revokeRole}.
         *
         * To change a role's admin, use {AccessControl-_setRoleAdmin}.
         */
        function getRoleAdmin(bytes32 role) external view returns (bytes32);
        /**
         * @dev Grants `role` to `account`.
         *
         * If `account` had not been already granted `role`, emits a {RoleGranted}
         * event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function grantRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from `account`.
         *
         * If `account` had been granted `role`, emits a {RoleRevoked} event.
         *
         * Requirements:
         *
         * - the caller must have ``role``'s admin role.
         */
        function revokeRole(bytes32 role, address account) external;
        /**
         * @dev Revokes `role` from the calling account.
         *
         * Roles are often managed via {grantRole} and {revokeRole}: this function's
         * purpose is to provide a mechanism for accounts to lose their privileges
         * if they are compromised (such as when a trusted device is misplaced).
         *
         * If the calling account had been granted `role`, emits a {RoleRevoked}
         * event.
         *
         * Requirements:
         *
         * - the caller must be `account`.
         */
        function renounceRole(bytes32 role, address account) external;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev 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) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions anymore. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby removing any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
    pragma solidity ^0.8.0;
    import "./math/Math.sol";
    /**
     * @dev String operations.
     */
    library Strings {
        bytes16 private constant _SYMBOLS = "0123456789abcdef";
        uint8 private constant _ADDRESS_LENGTH = 20;
        /**
         * @dev Converts a `uint256` to its ASCII `string` decimal representation.
         */
        function toString(uint256 value) internal pure returns (string memory) {
            unchecked {
                uint256 length = Math.log10(value) + 1;
                string memory buffer = new string(length);
                uint256 ptr;
                /// @solidity memory-safe-assembly
                assembly {
                    ptr := add(buffer, add(32, length))
                }
                while (true) {
                    ptr--;
                    /// @solidity memory-safe-assembly
                    assembly {
                        mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                    }
                    value /= 10;
                    if (value == 0) break;
                }
                return buffer;
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
         */
        function toHexString(uint256 value) internal pure returns (string memory) {
            unchecked {
                return toHexString(value, Math.log256(value) + 1);
            }
        }
        /**
         * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
         */
        function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
            bytes memory buffer = new bytes(2 * length + 2);
            buffer[0] = "0";
            buffer[1] = "x";
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = _SYMBOLS[value & 0xf];
                value >>= 4;
            }
            require(value == 0, "Strings: hex length insufficient");
            return string(buffer);
        }
        /**
         * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
         */
        function toHexString(address addr) internal pure returns (string memory) {
            return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
    pragma solidity ^0.8.0;
    import "./IERC165.sol";
    /**
     * @dev Implementation of the {IERC165} interface.
     *
     * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
     * for the additional interface id that will be supported. For example:
     *
     * ```solidity
     * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
     *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
     * }
     * ```
     *
     * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
     */
    abstract contract ERC165 is IERC165 {
        /**
         * @dev See {IERC165-supportsInterface}.
         */
        function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
            return interfaceId == type(IERC165).interfaceId;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
    pragma solidity ^0.8.0;
    /**
     * @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 v4.8.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds up instead
         * of rounding down.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
         * with further edits by Uniswap Labs also under MIT license.
         */
        function mulDiv(
            uint256 x,
            uint256 y,
            uint256 denominator
        ) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod0 := mul(x, y)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1);
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                // See https://cs.stackexchange.com/q/138556/92363.
                // Does not overflow because the denominator cannot be zero at this stage in the function.
                uint256 twos = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(
            uint256 x,
            uint256 y,
            uint256 denominator,
            Rounding rounding
        ) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10**64) {
                    value /= 10**64;
                    result += 64;
                }
                if (value >= 10**32) {
                    value /= 10**32;
                    result += 32;
                }
                if (value >= 10**16) {
                    value /= 10**16;
                    result += 16;
                }
                if (value >= 10**8) {
                    value /= 10**8;
                    result += 8;
                }
                if (value >= 10**4) {
                    value /= 10**4;
                    result += 4;
                }
                if (value >= 10**2) {
                    value /= 10**2;
                    result += 2;
                }
                if (value >= 10**1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256, rounded down, of a positive value.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
            }
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Minimalist and gas efficient standard ERC1155 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
    abstract contract ERC1155 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event TransferSingle(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256 id,
            uint256 amount
        );
        event TransferBatch(
            address indexed operator,
            address indexed from,
            address indexed to,
            uint256[] ids,
            uint256[] amounts
        );
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        event URI(string value, uint256 indexed id);
        /*//////////////////////////////////////////////////////////////
                                 ERC1155 STORAGE
        //////////////////////////////////////////////////////////////*/
        mapping(address => mapping(uint256 => uint256)) public balanceOf;
        mapping(address => mapping(address => bool)) public isApprovedForAll;
        /*//////////////////////////////////////////////////////////////
                                 METADATA LOGIC
        //////////////////////////////////////////////////////////////*/
        function uri(uint256 id) public view virtual returns (string memory);
        /*//////////////////////////////////////////////////////////////
                                  ERC1155 LOGIC
        //////////////////////////////////////////////////////////////*/
        function setApprovalForAll(address operator, bool approved) public virtual {
            isApprovedForAll[msg.sender][operator] = approved;
            emit ApprovalForAll(msg.sender, operator, approved);
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            uint256 amount,
            bytes calldata data
        ) public virtual {
            require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
            balanceOf[from][id] -= amount;
            balanceOf[to][id] += amount;
            emit TransferSingle(msg.sender, from, to, id, amount);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
                        ERC1155TokenReceiver.onERC1155Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function safeBatchTransferFrom(
            address from,
            address to,
            uint256[] calldata ids,
            uint256[] calldata amounts,
            bytes calldata data
        ) public virtual {
            require(ids.length == amounts.length, "LENGTH_MISMATCH");
            require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
            // Storing these outside the loop saves ~15 gas per iteration.
            uint256 id;
            uint256 amount;
            for (uint256 i = 0; i < ids.length; ) {
                id = ids[i];
                amount = amounts[i];
                balanceOf[from][id] -= amount;
                balanceOf[to][id] += amount;
                // An array can't have a total length
                // larger than the max uint256 value.
                unchecked {
                    ++i;
                }
            }
            emit TransferBatch(msg.sender, from, to, ids, amounts);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
                        ERC1155TokenReceiver.onERC1155BatchReceived.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
            public
            view
            virtual
            returns (uint256[] memory balances)
        {
            require(owners.length == ids.length, "LENGTH_MISMATCH");
            balances = new uint256[](owners.length);
            // Unchecked because the only math done is incrementing
            // the array index counter which cannot possibly overflow.
            unchecked {
                for (uint256 i = 0; i < owners.length; ++i) {
                    balances[i] = balanceOf[owners[i]][ids[i]];
                }
            }
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC165 LOGIC
        //////////////////////////////////////////////////////////////*/
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
                interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(
            address to,
            uint256 id,
            uint256 amount,
            bytes memory data
        ) internal virtual {
            balanceOf[to][id] += amount;
            emit TransferSingle(msg.sender, address(0), to, id, amount);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
                        ERC1155TokenReceiver.onERC1155Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _batchMint(
            address to,
            uint256[] memory ids,
            uint256[] memory amounts,
            bytes memory data
        ) internal virtual {
            uint256 idsLength = ids.length; // Saves MLOADs.
            require(idsLength == amounts.length, "LENGTH_MISMATCH");
            for (uint256 i = 0; i < idsLength; ) {
                balanceOf[to][ids[i]] += amounts[i];
                // An array can't have a total length
                // larger than the max uint256 value.
                unchecked {
                    ++i;
                }
            }
            emit TransferBatch(msg.sender, address(0), to, ids, amounts);
            require(
                to.code.length == 0
                    ? to != address(0)
                    : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
                        ERC1155TokenReceiver.onERC1155BatchReceived.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _batchBurn(
            address from,
            uint256[] memory ids,
            uint256[] memory amounts
        ) internal virtual {
            uint256 idsLength = ids.length; // Saves MLOADs.
            require(idsLength == amounts.length, "LENGTH_MISMATCH");
            for (uint256 i = 0; i < idsLength; ) {
                balanceOf[from][ids[i]] -= amounts[i];
                // An array can't have a total length
                // larger than the max uint256 value.
                unchecked {
                    ++i;
                }
            }
            emit TransferBatch(msg.sender, from, address(0), ids, amounts);
        }
        function _burn(
            address from,
            uint256 id,
            uint256 amount
        ) internal virtual {
            balanceOf[from][id] -= amount;
            emit TransferSingle(msg.sender, from, address(0), id, amount);
        }
    }
    /// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
    abstract contract ERC1155TokenReceiver {
        function onERC1155Received(
            address,
            address,
            uint256,
            uint256,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC1155TokenReceiver.onERC1155Received.selector;
        }
        function onERC1155BatchReceived(
            address,
            address,
            uint256[] calldata,
            uint256[] calldata,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
    /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
    /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
    abstract contract ERC20 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 amount);
        event Approval(address indexed owner, address indexed spender, uint256 amount);
        /*//////////////////////////////////////////////////////////////
                                METADATA STORAGE
        //////////////////////////////////////////////////////////////*/
        string public name;
        string public symbol;
        uint8 public immutable decimals;
        /*//////////////////////////////////////////////////////////////
                                  ERC20 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 public totalSupply;
        mapping(address => uint256) public balanceOf;
        mapping(address => mapping(address => uint256)) public allowance;
        /*//////////////////////////////////////////////////////////////
                                EIP-2612 STORAGE
        //////////////////////////////////////////////////////////////*/
        uint256 internal immutable INITIAL_CHAIN_ID;
        bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
        mapping(address => uint256) public nonces;
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(
            string memory _name,
            string memory _symbol,
            uint8 _decimals
        ) {
            name = _name;
            symbol = _symbol;
            decimals = _decimals;
            INITIAL_CHAIN_ID = block.chainid;
            INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        }
        /*//////////////////////////////////////////////////////////////
                                   ERC20 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 amount) public virtual returns (bool) {
            allowance[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);
            return true;
        }
        function transfer(address to, uint256 amount) public virtual returns (bool) {
            balanceOf[msg.sender] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(msg.sender, to, amount);
            return true;
        }
        function transferFrom(
            address from,
            address to,
            uint256 amount
        ) public virtual returns (bool) {
            uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
            if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
            balanceOf[from] -= amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(from, to, amount);
            return true;
        }
        /*//////////////////////////////////////////////////////////////
                                 EIP-2612 LOGIC
        //////////////////////////////////////////////////////////////*/
        function permit(
            address owner,
            address spender,
            uint256 value,
            uint256 deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) public virtual {
            require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
            // Unchecked because the only math done is incrementing
            // the owner's nonce which cannot realistically overflow.
            unchecked {
                address recoveredAddress = ecrecover(
                    keccak256(
                        abi.encodePacked(
                            "\\x19\\x01",
                            DOMAIN_SEPARATOR(),
                            keccak256(
                                abi.encode(
                                    keccak256(
                                        "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                    ),
                                    owner,
                                    spender,
                                    value,
                                    nonces[owner]++,
                                    deadline
                                )
                            )
                        )
                    ),
                    v,
                    r,
                    s
                );
                require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                allowance[recoveredAddress][spender] = value;
            }
            emit Approval(owner, spender, value);
        }
        function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
            return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
        }
        function computeDomainSeparator() internal view virtual returns (bytes32) {
            return
                keccak256(
                    abi.encode(
                        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                        keccak256(bytes(name)),
                        keccak256("1"),
                        block.chainid,
                        address(this)
                    )
                );
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 amount) internal virtual {
            totalSupply += amount;
            // Cannot overflow because the sum of all user
            // balances can't exceed the max uint256 value.
            unchecked {
                balanceOf[to] += amount;
            }
            emit Transfer(address(0), to, amount);
        }
        function _burn(address from, uint256 amount) internal virtual {
            balanceOf[from] -= amount;
            // Cannot underflow because a user's balance
            // will never be larger than the total supply.
            unchecked {
                totalSupply -= amount;
            }
            emit Transfer(from, address(0), amount);
        }
    }
    // SPDX-License-Identifier: AGPL-3.0-only
    pragma solidity >=0.8.0;
    /// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
    abstract contract ERC721 {
        /*//////////////////////////////////////////////////////////////
                                     EVENTS
        //////////////////////////////////////////////////////////////*/
        event Transfer(address indexed from, address indexed to, uint256 indexed id);
        event Approval(address indexed owner, address indexed spender, uint256 indexed id);
        event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
        /*//////////////////////////////////////////////////////////////
                             METADATA STORAGE/LOGIC
        //////////////////////////////////////////////////////////////*/
        string public name;
        string public symbol;
        function tokenURI(uint256 id) public view virtual returns (string memory);
        /*//////////////////////////////////////////////////////////////
                          ERC721 BALANCE/OWNER STORAGE
        //////////////////////////////////////////////////////////////*/
        mapping(uint256 => address) internal _ownerOf;
        mapping(address => uint256) internal _balanceOf;
        function ownerOf(uint256 id) public view virtual returns (address owner) {
            require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
        }
        function balanceOf(address owner) public view virtual returns (uint256) {
            require(owner != address(0), "ZERO_ADDRESS");
            return _balanceOf[owner];
        }
        /*//////////////////////////////////////////////////////////////
                             ERC721 APPROVAL STORAGE
        //////////////////////////////////////////////////////////////*/
        mapping(uint256 => address) public getApproved;
        mapping(address => mapping(address => bool)) public isApprovedForAll;
        /*//////////////////////////////////////////////////////////////
                                   CONSTRUCTOR
        //////////////////////////////////////////////////////////////*/
        constructor(string memory _name, string memory _symbol) {
            name = _name;
            symbol = _symbol;
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC721 LOGIC
        //////////////////////////////////////////////////////////////*/
        function approve(address spender, uint256 id) public virtual {
            address owner = _ownerOf[id];
            require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
            getApproved[id] = spender;
            emit Approval(owner, spender, id);
        }
        function setApprovalForAll(address operator, bool approved) public virtual {
            isApprovedForAll[msg.sender][operator] = approved;
            emit ApprovalForAll(msg.sender, operator, approved);
        }
        function transferFrom(
            address from,
            address to,
            uint256 id
        ) public virtual {
            require(from == _ownerOf[id], "WRONG_FROM");
            require(to != address(0), "INVALID_RECIPIENT");
            require(
                msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                "NOT_AUTHORIZED"
            );
            // Underflow of the sender's balance is impossible because we check for
            // ownership above and the recipient's balance can't realistically overflow.
            unchecked {
                _balanceOf[from]--;
                _balanceOf[to]++;
            }
            _ownerOf[id] = to;
            delete getApproved[id];
            emit Transfer(from, to, id);
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id
        ) public virtual {
            transferFrom(from, to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function safeTransferFrom(
            address from,
            address to,
            uint256 id,
            bytes calldata data
        ) public virtual {
            transferFrom(from, to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        /*//////////////////////////////////////////////////////////////
                                  ERC165 LOGIC
        //////////////////////////////////////////////////////////////*/
        function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
            return
                interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL MINT/BURN LOGIC
        //////////////////////////////////////////////////////////////*/
        function _mint(address to, uint256 id) internal virtual {
            require(to != address(0), "INVALID_RECIPIENT");
            require(_ownerOf[id] == address(0), "ALREADY_MINTED");
            // Counter overflow is incredibly unrealistic.
            unchecked {
                _balanceOf[to]++;
            }
            _ownerOf[id] = to;
            emit Transfer(address(0), to, id);
        }
        function _burn(uint256 id) internal virtual {
            address owner = _ownerOf[id];
            require(owner != address(0), "NOT_MINTED");
            // Ownership check above ensures no underflow.
            unchecked {
                _balanceOf[owner]--;
            }
            delete _ownerOf[id];
            delete getApproved[id];
            emit Transfer(owner, address(0), id);
        }
        /*//////////////////////////////////////////////////////////////
                            INTERNAL SAFE MINT LOGIC
        //////////////////////////////////////////////////////////////*/
        function _safeMint(address to, uint256 id) internal virtual {
            _mint(to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
        function _safeMint(
            address to,
            uint256 id,
            bytes memory data
        ) internal virtual {
            _mint(to, id);
            require(
                to.code.length == 0 ||
                    ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                    ERC721TokenReceiver.onERC721Received.selector,
                "UNSAFE_RECIPIENT"
            );
        }
    }
    /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
    abstract contract ERC721TokenReceiver {
        function onERC721Received(
            address,
            address,
            uint256,
            bytes calldata
        ) external virtual returns (bytes4) {
            return ERC721TokenReceiver.onERC721Received.selector;
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.8.0;
    /// @notice Gas optimized merkle proof verification library.
    /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
    /// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
    library MerkleProofLib {
        function verify(
            bytes32[] calldata proof,
            bytes32 root,
            bytes32 leaf
        ) internal pure returns (bool isValid) {
            /// @solidity memory-safe-assembly
            assembly {
                if proof.length {
                    // Left shifting by 5 is like multiplying by 32.
                    let end := add(proof.offset, shl(5, proof.length))
                    // Initialize offset to the offset of the proof in calldata.
                    let offset := proof.offset
                    // Iterate over proof elements to compute root hash.
                    // prettier-ignore
                    for {} 1 {} {
                        // Slot where the leaf should be put in scratch space. If
                        // leaf > calldataload(offset): slot 32, otherwise: slot 0.
                        let leafSlot := shl(5, gt(leaf, calldataload(offset)))
                        // Store elements to hash contiguously in scratch space.
                        // The xor puts calldataload(offset) in whichever slot leaf
                        // is not occupying, so 0 if leafSlot is 32, and 32 otherwise.
                        mstore(leafSlot, leaf)
                        mstore(xor(leafSlot, 32), calldataload(offset))
                        // Reuse leaf to store the hash to reduce stack operations.
                        leaf := keccak256(0, 64) // Hash both slots of scratch space.
                        offset := add(offset, 32) // Shift 1 word per cycle.
                        // prettier-ignore
                        if iszero(lt(offset, end)) { break }
                    }
                }
                isValid := eq(leaf, root) // The proof is valid if the roots match.
            }
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    library Constants {
        // External permissions
        bytes32 public constant GAME_ADMIN = "GAME_ADMIN";
        bytes32 public constant BEEKEEPER = "BEEKEEPER";
        bytes32 public constant JANI = "JANI";
        // Contract instances
        bytes32 public constant GAME_INSTANCE = "GAME_INSTANCE";
        bytes32 public constant BEAR_POUCH = "BEAR_POUCH";
        bytes32 public constant GATEKEEPER = "GATEKEEPER";
        bytes32 public constant GATE = "GATE";
        // Special honeycomb permissions
        bytes32 public constant MINTER = "MINTER";
        bytes32 public constant BURNER = "BURNER";
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import {Constants} from "./GameLib.sol";
    /// @title GameRegistry
    /// @notice Central repository that tracks games and permissions.
    /// @dev All game contracts should use extend `GameRegistryConsumer` to have consistent permissioning
    contract GameRegistry is AccessControl {
        struct Game {
            bool enabled;
        }
        uint256[] public stageTimes;
        constructor() {
            _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
            _setupRole(Constants.GAME_ADMIN, msg.sender);
            // Initial 4 stages
            stageTimes.push(0 hours);
            stageTimes.push(24 hours);
            stageTimes.push(48 hours);
            stageTimes.push(72 hours);
        }
        mapping(address => Game) public games;
        function registerGame(address game_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.GAME_INSTANCE, game_);
            games[game_] = Game(true);
        }
        function startGame(address game_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.MINTER, game_);
        }
        function stopGame(address game_) external onlyRole(Constants.GAME_ADMIN) {
            _revokeRole(Constants.MINTER, game_);
            games[game_].enabled = false;
        }
        /**
         * Gettors
         */
        function getStageTimes() external view returns (uint256[] memory) {
            return stageTimes;
        }
        /**
         * Bear Pouch setters (helper functions)
         * Can check roles directly since this is an access control
         */
        function setJani(address jani_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.JANI, jani_);
        }
        function setBeekeeper(address beeKeeper_) external onlyRole(Constants.GAME_ADMIN) {
            _grantRole(Constants.JANI, beeKeeper_);
        }
        function setStageTimes(uint24[] calldata _stageTimes) external onlyRole(Constants.GAME_ADMIN) {
            stageTimes = _stageTimes;
        }
    }
    abstract contract GameRegistryConsumer {
        GameRegistry public gameRegistry;
        error GameRegistry_NoPermissions(string role, address user);
        error GameRegistry_StageOutOfBounds(uint8 index);
        modifier onlyRole(bytes32 role_) {
            if (!gameRegistry.hasRole(role_, msg.sender)) {
                revert GameRegistry_NoPermissions(string(abi.encodePacked(role_)), msg.sender);
            }
            _;
        }
        constructor(address gameRegistry_) {
            gameRegistry = GameRegistry(gameRegistry_);
        }
        function _isEnabled(address game_) internal view returns (bool enabled) {
            enabled = gameRegistry.games(game_);
        }
        /// @dev the last stageTime is generalMint
        function _getStages() internal view returns (uint256[] memory) {
            return gameRegistry.getStageTimes();
        }
        /// @dev just a helper function. For access to all stages you should use _getStages()
        function _getStage(uint8 stageIndex) internal view returns (uint256) {
            uint256[] memory stageTimes = gameRegistry.getStageTimes();
            if (stageIndex >= stageTimes.length) revert GameRegistry_StageOutOfBounds(stageIndex);
            return stageTimes[stageIndex];
        }
        function _hasRole(bytes32 role_) internal view returns (bool) {
            return gameRegistry.hasRole(role_, msg.sender);
        }
    }
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.17;
    import {ERC1155} from "solmate/tokens/ERC1155.sol";
    import {ERC20} from "solmate/tokens/ERC20.sol";
    import {ERC721} from "solmate/tokens/ERC721.sol";
    import "@openzeppelin/contracts/access/AccessControl.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "solmate/utils/MerkleProofLib.sol";
    import {GameRegistryConsumer} from "./GameRegistry.sol";
    import {Constants} from "./GameLib.sol";
    /**
     * Bear GateKeeper
     *      In order to remain gas-efficient gates will be calculated off-chain
     *      BearGate: owning bears
     *      CrownGate: every single one of the digital collectible articles, then they get a free claim in every game
     *      HoneyGate: Genesis mint & n-1 can mint
     *      FrenGate: owning particular assets
     *      PartnerGate: being on a traditional allowlist
     *      Since gates are merkle trees, the per-player amounts will be set off-chain in the root.
     *  @notice state needs to be reset after each game.
     *  @notice tracks claims per player, and claims per gate.
     */
    contract Gatekeeper is GameRegistryConsumer {
        struct Gate {
            bool enabled;
            uint8 stageIndex; // stage from [0-3]
            uint32 claimedCount; // # of claims already happend
            uint32 maxClaimable; // # of claims per gate
            bytes32 gateRoot;
            uint256 activeAt; // timestamp when active.
        }
        /**
         * Events when business logic is affects
         */
        event GateAdded(uint256 tokenId, uint256 gateId);
        event GateSetEnabled(uint256 tokenId, uint256 gateId, bool enabled);
        event GateActivated(uint256 tokenId, uint256 gateId, uint256 activationTime);
        event GetSetMaxClaimable(uint256 tokenId, uint256 gateId, uint256 maxClaimable);
        event GateReset(uint256 tokenId, uint256 index);
        /**
         * Internal Storage
         */
        mapping(uint256 => Gate[]) public tokenToGates; // bear -> Gates[]
        mapping(uint256 => mapping(bytes32 => bool)) public consumedProofs; // gateId --> proof --> boolean
        mapping(uint256 => bytes32[]) public consumedProofsList; // gateId
        mapping(uint256 => address) public games; // bear --> gameContract;
        /**
         * Dependencies
         */
        /// @notice admin is the address that is set as the owner.
        constructor(address gameRegistry_) GameRegistryConsumer(gameRegistry_) {}
        /// @notice validate how much you can claim for a particular token and gate. (not a real claim)
        /// @param tokenId the ID of the bear in the game.
        /// @param index the gate index we're claiming
        /// @param amount number between 0-maxClaimable you a player wants to claim
        /// @param proof merkle proof
        function claim(uint256 tokenId, uint256 index, address player, uint32 amount, bytes32[] calldata proof)
            external
            view
            returns (uint32 claimAmount)
        {
            // If proof was already used within the gate, there are 0 left to claim
            bytes32 proofHash = keccak256(abi.encode(proof));
            if (consumedProofs[index][proofHash]) return 0;
            Gate storage gate = tokenToGates[tokenId][index];
            uint32 claimedCount = gate.claimedCount;
            require(claimedCount < gate.maxClaimable, "Too much honeycomb went through this gate");
            claimAmount = amount;
            bool validProof = validateProof(tokenId, index, player, amount, proof);
            require(validProof, "Not a valid proof bro");
            if (amount + claimedCount > gate.maxClaimable) {
                claimAmount = gate.maxClaimable - claimedCount;
            }
        }
        /// @notice Validates proof
        /// @dev relies on gates being enabled
        function validateProof(uint256 tokenId, uint256 index, address player, uint32 amount, bytes32[] calldata proof)
            public
            view
            returns (bool validProof)
        {
            Gate[] storage gates = tokenToGates[tokenId];
            require(gates.length > 0, "nogates fren");
            require(index < gates.length, "Index too big bro");
            require(proof.length > 0, "Invalid Proof");
            Gate storage gate = gates[index];
            require(gate.enabled, "gates closed bruh");
            require(gate.activeAt <= block.timestamp, "gate isn't active");
            bytes32 leaf = keccak256(abi.encodePacked(player, amount));
            validProof = MerkleProofLib.verify(proof, gate.gateRoot, leaf);
        }
        /**
         * Setters
         */
        /// @notice  update accounting
        /// @dev should only be called by a game
        function addClaimed(uint256 tokenId, uint256 gateId, uint32 numClaimed, bytes32[] calldata proof)
            external
            onlyRole(Constants.GAME_INSTANCE)
        {
            Gate storage gate = tokenToGates[tokenId][gateId];
            gate.claimedCount += numClaimed;
            bytes32 proofHash = keccak256(abi.encode(proof));
            consumedProofs[gateId][proofHash] = true;
            consumedProofsList[gateId].push(proofHash);
        }
        /**
         * Gate admin methods
         */
        function addGate(uint256 tokenId, bytes32 root_, uint32 maxClaimable_, uint8 stageIndex_)
            external
            onlyRole(Constants.GAME_ADMIN)
        {
            // claimedCount = activeAt = 0
            require(_getStages().length > stageIndex_, "addGate: stageIndex_ is out of bounds");
            tokenToGates[tokenId].push(Gate(false, stageIndex_, 0, maxClaimable_, root_, 0));
            emit GateAdded(tokenId, tokenToGates[tokenId].length - 1);
        }
        function startGatesForToken(uint256 tokenId) external onlyRole(Constants.GAME_INSTANCE) {
            Gate[] storage gates = tokenToGates[tokenId];
            uint256[] memory stageTimes = _getStages(); // External Call
            uint256 numGates = gates.length;
            for (uint256 i = 0; i < numGates; i++) {
                gates[i].enabled = true;
                gates[i].activeAt = block.timestamp + stageTimes[gates[i].stageIndex];
                emit GateActivated(tokenId, i, gates[i].activeAt);
            }
        }
        /// @notice Only to be used for emergency gate shutdown.
        function setGateEnabled(uint256 tokenId, uint256 index, bool enabled) external onlyRole(Constants.GAME_ADMIN) {
            tokenToGates[tokenId][index].enabled = enabled;
            emit GateSetEnabled(tokenId, index, enabled);
        }
        function setGateMaxClaimable(uint256 tokenId, uint256 index, uint32 maxClaimable_)
            external
            onlyRole(Constants.GAME_ADMIN)
        {
            tokenToGates[tokenId][index].maxClaimable = maxClaimable_;
            emit GetSetMaxClaimable(tokenId, index, maxClaimable_);
        }
        function resetGate(uint256 tokenId, uint256 index) external onlyRole(Constants.GAME_ADMIN) {
            tokenToGates[tokenId][index].claimedCount = 0;
            emit GateReset(tokenId, index);
        }
        function resetAllGates(uint256 tokenId) external onlyRole(Constants.GAME_ADMIN) {
            uint256 numGates = tokenToGates[tokenId].length;
            Gate[] storage tokenGates = tokenToGates[tokenId];
            uint256 numProofs;
            // Currently a hacky way but need to clear out if the proofs were used.
            for (uint256 i = 0; i < numGates; i++) {
                tokenGates[i].claimedCount = 0;
                numProofs = consumedProofsList[i].length;
                for (uint256 j = 0; j < numProofs; ++j) {
                    // Step through all proofs from a particular gate.
                    consumedProofs[i][consumedProofsList[i][j]] = false;
                }
            }
        }
    }